1
// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com>
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.
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.
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
17
#include "moviedecoder.h"
23
#include <libswscale/swscale.h>
28
namespace ffmpegthumbnailer
31
MovieDecoder::MovieDecoder(const QString& filename, AVFormatContext* pavContext)
33
, m_pFormatContext(pavContext)
34
, m_pVideoCodecContext(NULL)
36
, m_pVideoStream(NULL)
38
, m_pFrameBuffer(NULL)
40
, m_FormatContextWasGiven(pavContext != NULL)
42
, m_initialized(false)
47
MovieDecoder::~MovieDecoder()
52
void MovieDecoder::initialize(const QString& filename)
56
avcodec_register_all();
58
QFileInfo fileInfo(filename);
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();
65
if (av_find_stream_info(m_pFormatContext) < 0) {
66
kDebug() << "Could not find stream information";
71
m_pFrame = avcodec_alloc_frame();
78
bool MovieDecoder::getInitialized()
84
void MovieDecoder::destroy()
86
if (m_pVideoCodecContext) {
87
avcodec_close(m_pVideoCodecContext);
88
m_pVideoCodecContext = NULL;
91
if ((!m_FormatContextWasGiven) && m_pFormatContext) {
92
av_close_input_file(m_pFormatContext);
93
m_pFormatContext = NULL;
97
av_free_packet(m_pPacket);
107
if (m_pFrameBuffer) {
108
av_free(m_pFrameBuffer);
109
m_pFrameBuffer = NULL;
113
QString MovieDecoder::getCodec()
117
codecName=QString::fromLatin1(m_pVideoCodec->name);
122
void MovieDecoder::initializeVideo()
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];
132
if (m_VideoStream == -1) {
133
kDebug() << "Could not find video stream";
137
m_pVideoCodecContext = m_pFormatContext->streams[m_VideoStream]->codec;
138
m_pVideoCodec = avcodec_find_decoder(m_pVideoCodecContext->codec_id);
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";
147
m_pVideoCodecContext->workaround_bugs = 1;
149
if (avcodec_open(m_pVideoCodecContext, m_pVideoCodec) < 0) {
150
kDebug() << "Could not open video codec";
154
int MovieDecoder::getWidth()
156
if (m_pVideoCodecContext) {
157
return m_pVideoCodecContext->width;
163
int MovieDecoder::getHeight()
165
if (m_pVideoCodecContext) {
166
return m_pVideoCodecContext->height;
172
int MovieDecoder::getDuration()
174
if (m_pFormatContext) {
175
return static_cast<int>(m_pFormatContext->duration / AV_TIME_BASE);
181
void MovieDecoder::seek(int timeInSeconds)
187
qint64 timestamp = AV_TIME_BASE * static_cast<qint64>(timeInSeconds);
193
int ret = av_seek_frame(m_pFormatContext, -1, timestamp, 0);
195
avcodec_flush_buffers(m_pFormatContext->streams[m_VideoStream]->codec);
197
kDebug() << "Seeking in video failed";
201
int keyFrameAttempts = 0;
208
while (!gotFrame && count < 20) {
210
gotFrame = decodeVideoPacket();
215
} while ((!gotFrame || !m_pFrame->key_frame) && keyFrameAttempts < 200);
218
kDebug() << "Seeking in video failed";
223
void MovieDecoder::decodeVideoFrame()
225
bool frameFinished = false;
227
while (!frameFinished && getVideoPacket()) {
228
frameFinished = decodeVideoPacket();
231
if (!frameFinished) {
232
kDebug() << "decodeVideoFrame() failed: frame not finished";
237
bool MovieDecoder::decodeVideoPacket()
239
if (m_pPacket->stream_index != m_VideoStream) {
243
avcodec_get_frame_defaults(m_pFrame);
245
int frameFinished = 0;
247
#if LIBAVCODEC_VERSION_MAJOR < 53
248
int bytesDecoded = avcodec_decode_video(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket->data, m_pPacket->size);
250
int bytesDecoded = avcodec_decode_video2(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket);
253
if (bytesDecoded < 0) {
254
kDebug() << "Failed to decode video frame: bytesDecoded < 0";
257
return (frameFinished > 0);
260
bool MovieDecoder::getVideoPacket()
262
bool framesAvailable = true;
263
bool frameDecoded = false;
268
av_free_packet(m_pPacket);
272
m_pPacket = new AVPacket();
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;
279
av_free_packet(m_pPacket);
287
void MovieDecoder::getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame)
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);
294
int scaledWidth, scaledHeight;
295
convertAndScaleFrame(PIX_FMT_RGB24, scaledSize, maintainAspectRatio, scaledWidth, scaledHeight);
297
videoFrame.width = scaledWidth;
298
videoFrame.height = scaledHeight;
299
videoFrame.lineSize = m_pFrame->linesize[0];
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);
306
void MovieDecoder::convertAndScaleFrame(PixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight)
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);
313
if (NULL == scaleContext) {
314
kDebug() << "Failed to create resize context";
318
AVFrame* convertedFrame = NULL;
319
uint8_t* convertedFrameBuffer = NULL;
321
createAVFrame(&convertedFrame, &convertedFrameBuffer, scaledWidth, scaledHeight, format);
323
sws_scale(scaleContext, m_pFrame->data, m_pFrame->linesize, 0, m_pVideoCodecContext->height,
324
convertedFrame->data, convertedFrame->linesize);
325
sws_freeContext(scaleContext);
328
av_free(m_pFrameBuffer);
330
m_pFrame = convertedFrame;
331
m_pFrameBuffer = convertedFrameBuffer;
334
void MovieDecoder::calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight)
336
if (!maintainAspectRatio) {
337
destWidth = squareSize;
338
destHeight = squareSize;
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;
345
if (ascpectNominator != 0 && ascpectDenominator != 0) {
346
srcWidth = srcWidth * ascpectNominator / ascpectDenominator;
349
if (srcWidth > srcHeight) {
350
destWidth = squareSize;
351
destHeight = static_cast<int>(static_cast<float>(squareSize) / srcWidth * srcHeight);
353
destWidth = static_cast<int>(static_cast<float>(squareSize) / srcHeight * srcWidth);
354
destHeight = squareSize;
359
void MovieDecoder::createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, PixelFormat format)
361
*avFrame = avcodec_alloc_frame();
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);