~ubuntu-branches/ubuntu/natty/kdemultimedia/natty-proposed

« back to all changes in this revision

Viewing changes to ffmpegthumbs/ffmpegthumbnailer/moviedecoder.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Debian Qt/KDE Maintainers
  • Date: 2011-05-26 02:41:36 UTC
  • mfrom: (0.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 108.
  • Revision ID: james.westby@ubuntu.com-20110526024136-jjwsigfy402jhupm
Tags: upstream-4.6.3
ImportĀ upstreamĀ versionĀ 4.6.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//    Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com>
 
2
//
 
3
//    This program is free software; you can redistribute it and/or modify
 
4
//    it under the terms of the GNU General Public License as published by
 
5
//    the Free Software Foundation; either version 2 of the License, or
 
6
//    (at your option) any later version.
 
7
//
 
8
//    This program is distributed in the hope that it will be useful,
 
9
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
//    GNU General Public License for more details.
 
12
//
 
13
//    You should have received a copy of the GNU General Public License
 
14
//    along with this program; if not, write to the Free Software
 
15
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
#include "moviedecoder.h"
 
18
 
 
19
#include <kdebug.h>
 
20
#include <QFileInfo>
 
21
 
 
22
extern "C" {
 
23
#include <libswscale/swscale.h>
 
24
}
 
25
 
 
26
using namespace std;
 
27
 
 
28
namespace ffmpegthumbnailer
 
29
{
 
30
 
 
31
MovieDecoder::MovieDecoder(const QString& filename, AVFormatContext* pavContext)
 
32
        : m_VideoStream(-1)
 
33
        , m_pFormatContext(pavContext)
 
34
        , m_pVideoCodecContext(NULL)
 
35
        , m_pVideoCodec(NULL)
 
36
        , m_pVideoStream(NULL)
 
37
        , m_pFrame(NULL)
 
38
        , m_pFrameBuffer(NULL)
 
39
        , m_pPacket(NULL)
 
40
        , m_FormatContextWasGiven(pavContext != NULL)
 
41
        , m_AllowSeek(true)
 
42
        , m_initialized(false)
 
43
{
 
44
    initialize(filename);
 
45
}
 
46
 
 
47
MovieDecoder::~MovieDecoder()
 
48
{
 
49
    destroy();
 
50
}
 
51
 
 
52
void MovieDecoder::initialize(const QString& filename)
 
53
{
 
54
    av_register_all();
 
55
    avcodec_init();
 
56
    avcodec_register_all();
 
57
 
 
58
    QFileInfo fileInfo(filename);
 
59
 
 
60
    if ((!m_FormatContextWasGiven) && av_open_input_file(&m_pFormatContext, fileInfo.absoluteFilePath().toUtf8().data(), NULL, 0, NULL) != 0) {
 
61
        kDebug() <<  "Could not open input file: " << fileInfo.absoluteFilePath();
 
62
        return;
 
63
    }
 
64
 
 
65
    if (av_find_stream_info(m_pFormatContext) < 0) {
 
66
        kDebug() << "Could not find stream information";
 
67
        return;
 
68
    }
 
69
 
 
70
    initializeVideo();
 
71
    m_pFrame = avcodec_alloc_frame();
 
72
 
 
73
    if (m_pFrame) {
 
74
        m_initialized=true;
 
75
    }
 
76
}
 
77
 
 
78
bool MovieDecoder::getInitialized()
 
79
{
 
80
    return m_initialized;
 
81
}
 
82
 
 
83
 
 
84
void MovieDecoder::destroy()
 
85
{
 
86
    if (m_pVideoCodecContext) {
 
87
        avcodec_close(m_pVideoCodecContext);
 
88
        m_pVideoCodecContext = NULL;
 
89
    }
 
90
 
 
91
    if ((!m_FormatContextWasGiven) && m_pFormatContext) {
 
92
        av_close_input_file(m_pFormatContext);
 
93
        m_pFormatContext = NULL;
 
94
    }
 
95
 
 
96
    if (m_pPacket) {
 
97
        av_free_packet(m_pPacket);
 
98
        delete m_pPacket;
 
99
        m_pPacket = NULL;
 
100
    }
 
101
 
 
102
    if (m_pFrame) {
 
103
        av_free(m_pFrame);
 
104
        m_pFrame = NULL;
 
105
    }
 
106
 
 
107
    if (m_pFrameBuffer) {
 
108
        av_free(m_pFrameBuffer);
 
109
        m_pFrameBuffer = NULL;
 
110
    }
 
111
}
 
112
 
 
113
QString MovieDecoder::getCodec()
 
114
{
 
115
    QString codecName;
 
116
    if (m_pVideoCodec) {
 
117
        codecName=QString::fromLatin1(m_pVideoCodec->name);
 
118
    }
 
119
    return codecName;
 
120
}
 
121
 
 
122
void MovieDecoder::initializeVideo()
 
123
{
 
124
    for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) {
 
125
        if (m_pFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
 
126
            m_pVideoStream = m_pFormatContext->streams[i];
 
127
            m_VideoStream = i;
 
128
            break;
 
129
        }
 
130
    }
 
131
 
 
132
    if (m_VideoStream == -1) {
 
133
        kDebug() << "Could not find video stream";
 
134
        return;
 
135
    }
 
136
 
 
137
    m_pVideoCodecContext = m_pFormatContext->streams[m_VideoStream]->codec;
 
138
    m_pVideoCodec = avcodec_find_decoder(m_pVideoCodecContext->codec_id);
 
139
 
 
140
    if (m_pVideoCodec == NULL) {
 
141
        // set to NULL, otherwise avcodec_close(m_pVideoCodecContext) crashes
 
142
        m_pVideoCodecContext = NULL;
 
143
        kDebug() << "Video Codec not found";
 
144
        return;
 
145
    }
 
146
 
 
147
    m_pVideoCodecContext->workaround_bugs = 1;
 
148
 
 
149
    if (avcodec_open(m_pVideoCodecContext, m_pVideoCodec) < 0) {
 
150
        kDebug() << "Could not open video codec";
 
151
    }
 
152
}
 
153
 
 
154
int MovieDecoder::getWidth()
 
155
{
 
156
    if (m_pVideoCodecContext) {
 
157
        return m_pVideoCodecContext->width;
 
158
    }
 
159
 
 
160
    return -1;
 
161
}
 
162
 
 
163
int MovieDecoder::getHeight()
 
164
{
 
165
    if (m_pVideoCodecContext) {
 
166
        return m_pVideoCodecContext->height;
 
167
    }
 
168
 
 
169
    return -1;
 
170
}
 
171
 
 
172
int MovieDecoder::getDuration()
 
173
{
 
174
    if (m_pFormatContext) {
 
175
        return static_cast<int>(m_pFormatContext->duration / AV_TIME_BASE);
 
176
    }
 
177
 
 
178
    return 0;
 
179
}
 
180
 
 
181
void MovieDecoder::seek(int timeInSeconds)
 
182
{
 
183
    if (!m_AllowSeek) {
 
184
        return;
 
185
    }
 
186
 
 
187
    qint64 timestamp = AV_TIME_BASE * static_cast<qint64>(timeInSeconds);
 
188
 
 
189
    if (timestamp < 0) {
 
190
        timestamp = 0;
 
191
    }
 
192
 
 
193
    int ret = av_seek_frame(m_pFormatContext, -1, timestamp, 0);
 
194
    if (ret >= 0) {
 
195
        avcodec_flush_buffers(m_pFormatContext->streams[m_VideoStream]->codec);
 
196
    } else {
 
197
        kDebug() << "Seeking in video failed";
 
198
        return;
 
199
    }
 
200
 
 
201
    int keyFrameAttempts = 0;
 
202
    bool gotFrame = 0;
 
203
 
 
204
    do {
 
205
        int count = 0;
 
206
        gotFrame = 0;
 
207
 
 
208
        while (!gotFrame && count < 20) {
 
209
            getVideoPacket();
 
210
            gotFrame = decodeVideoPacket();
 
211
            ++count;
 
212
        }
 
213
 
 
214
        ++keyFrameAttempts;
 
215
    } while ((!gotFrame || !m_pFrame->key_frame) && keyFrameAttempts < 200);
 
216
 
 
217
    if (gotFrame == 0) {
 
218
        kDebug() << "Seeking in video failed";
 
219
    }
 
220
}
 
221
 
 
222
 
 
223
void MovieDecoder::decodeVideoFrame()
 
224
{
 
225
    bool frameFinished = false;
 
226
 
 
227
    while (!frameFinished && getVideoPacket()) {
 
228
        frameFinished = decodeVideoPacket();
 
229
    }
 
230
 
 
231
    if (!frameFinished) {
 
232
        kDebug() << "decodeVideoFrame() failed: frame not finished";
 
233
        return;
 
234
    }
 
235
}
 
236
 
 
237
bool MovieDecoder::decodeVideoPacket()
 
238
{
 
239
    if (m_pPacket->stream_index != m_VideoStream) {
 
240
        return false;
 
241
    }
 
242
 
 
243
    avcodec_get_frame_defaults(m_pFrame);
 
244
 
 
245
    int frameFinished = 0;
 
246
 
 
247
#if LIBAVCODEC_VERSION_MAJOR < 53
 
248
    int bytesDecoded = avcodec_decode_video(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket->data, m_pPacket->size);
 
249
#else
 
250
    int bytesDecoded = avcodec_decode_video2(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket);
 
251
#endif
 
252
 
 
253
    if (bytesDecoded < 0) {
 
254
        kDebug() << "Failed to decode video frame: bytesDecoded < 0";
 
255
    }
 
256
 
 
257
    return (frameFinished > 0);
 
258
}
 
259
 
 
260
bool MovieDecoder::getVideoPacket()
 
261
{
 
262
    bool framesAvailable = true;
 
263
    bool frameDecoded = false;
 
264
 
 
265
    int attempts = 0;
 
266
 
 
267
    if (m_pPacket) {
 
268
        av_free_packet(m_pPacket);
 
269
        delete m_pPacket;
 
270
    }
 
271
 
 
272
    m_pPacket = new AVPacket();
 
273
 
 
274
    while (framesAvailable && !frameDecoded && (attempts++ < 1000)) {
 
275
        framesAvailable = av_read_frame(m_pFormatContext, m_pPacket) >= 0;
 
276
        if (framesAvailable) {
 
277
            frameDecoded = m_pPacket->stream_index == m_VideoStream;
 
278
            if (!frameDecoded) {
 
279
                av_free_packet(m_pPacket);
 
280
            }
 
281
        }
 
282
    }
 
283
 
 
284
    return frameDecoded;
 
285
}
 
286
 
 
287
void MovieDecoder::getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame)
 
288
{
 
289
    if (m_pFrame->interlaced_frame) {
 
290
        avpicture_deinterlace((AVPicture*) m_pFrame, (AVPicture*) m_pFrame, m_pVideoCodecContext->pix_fmt,
 
291
                              m_pVideoCodecContext->width, m_pVideoCodecContext->height);
 
292
    }
 
293
 
 
294
    int scaledWidth, scaledHeight;
 
295
    convertAndScaleFrame(PIX_FMT_RGB24, scaledSize, maintainAspectRatio, scaledWidth, scaledHeight);
 
296
 
 
297
    videoFrame.width = scaledWidth;
 
298
    videoFrame.height = scaledHeight;
 
299
    videoFrame.lineSize = m_pFrame->linesize[0];
 
300
 
 
301
    videoFrame.frameData.clear();
 
302
    videoFrame.frameData.resize(videoFrame.lineSize * videoFrame.height);
 
303
    memcpy((&(videoFrame.frameData.front())), m_pFrame->data[0], videoFrame.lineSize * videoFrame.height);
 
304
}
 
305
 
 
306
void MovieDecoder::convertAndScaleFrame(PixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight)
 
307
{
 
308
    calculateDimensions(scaledSize, maintainAspectRatio, scaledWidth, scaledHeight);
 
309
    SwsContext* scaleContext = sws_getContext(m_pVideoCodecContext->width, m_pVideoCodecContext->height,
 
310
                               m_pVideoCodecContext->pix_fmt, scaledWidth, scaledHeight,
 
311
                               format, SWS_BICUBIC, NULL, NULL, NULL);
 
312
 
 
313
    if (NULL == scaleContext) {
 
314
        kDebug() << "Failed to create resize context";
 
315
        return;
 
316
    }
 
317
 
 
318
    AVFrame* convertedFrame = NULL;
 
319
    uint8_t* convertedFrameBuffer = NULL;
 
320
 
 
321
    createAVFrame(&convertedFrame, &convertedFrameBuffer, scaledWidth, scaledHeight, format);
 
322
 
 
323
    sws_scale(scaleContext, m_pFrame->data, m_pFrame->linesize, 0, m_pVideoCodecContext->height,
 
324
              convertedFrame->data, convertedFrame->linesize);
 
325
    sws_freeContext(scaleContext);
 
326
 
 
327
    av_free(m_pFrame);
 
328
    av_free(m_pFrameBuffer);
 
329
 
 
330
    m_pFrame        = convertedFrame;
 
331
    m_pFrameBuffer  = convertedFrameBuffer;
 
332
}
 
333
 
 
334
void MovieDecoder::calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight)
 
335
{
 
336
    if (!maintainAspectRatio) {
 
337
        destWidth = squareSize;
 
338
        destHeight = squareSize;
 
339
    } else {
 
340
        int srcWidth            = m_pVideoCodecContext->width;
 
341
        int srcHeight           = m_pVideoCodecContext->height;
 
342
        int ascpectNominator    = m_pVideoCodecContext->sample_aspect_ratio.num;
 
343
        int ascpectDenominator  = m_pVideoCodecContext->sample_aspect_ratio.den;
 
344
 
 
345
        if (ascpectNominator != 0 && ascpectDenominator != 0) {
 
346
            srcWidth = srcWidth * ascpectNominator / ascpectDenominator;
 
347
        }
 
348
 
 
349
        if (srcWidth > srcHeight) {
 
350
            destWidth  = squareSize;
 
351
            destHeight = static_cast<int>(static_cast<float>(squareSize) / srcWidth * srcHeight);
 
352
        } else {
 
353
            destWidth  = static_cast<int>(static_cast<float>(squareSize) / srcHeight * srcWidth);
 
354
            destHeight = squareSize;
 
355
        }
 
356
    }
 
357
}
 
358
 
 
359
void MovieDecoder::createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, PixelFormat format)
 
360
{
 
361
    *avFrame = avcodec_alloc_frame();
 
362
 
 
363
    int numBytes = avpicture_get_size(format, width, height);
 
364
    *frameBuffer = reinterpret_cast<quint8*>(av_malloc(numBytes));
 
365
    avpicture_fill((AVPicture*) *avFrame, *frameBuffer, format, width, height);
 
366
}
 
367
 
 
368
}