Revert "Fix build with latest FFmpeg."
[gnash.git] / libmedia / ffmpeg / VideoDecoderFfmpeg.cpp
blob40a5c808b75795338727255289021cea710b966b
1 // VideoDecoderFfmpeg.cpp: Video decoding using the FFMPEG library.
2 //
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc.
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
25 #include "VideoDecoderFfmpeg.h"
27 #include <boost/scoped_array.hpp>
28 #include <boost/format.hpp>
29 #include <algorithm>
31 #include "ffmpegHeaders.h"
32 #include "MediaParserFfmpeg.h" // for ExtraVideoInfoFfmpeg
33 #include "GnashException.h" // for MediaException
34 #include "utility.h"
35 #include "FLVParser.h"
37 #ifdef HAVE_VA_VA_H
38 # include "vaapi_utils.h"
39 # include "VideoDecoderFfmpegVaapi.h"
40 # include "GnashVaapiImage.h"
41 #endif
43 namespace gnash {
44 namespace media {
45 namespace ffmpeg {
47 class VaapiContextFfmpeg;
49 // Forward declarations of VAAPI functions.
50 namespace {
51 VaapiContextFfmpeg* get_vaapi_context(AVCodecContext* avctx);
52 void set_vaapi_context(AVCodecContext* avctx, VaapiContextFfmpeg* vactx);
53 void clear_vaapi_context(AVCodecContext* avctx);
54 void reset_context(AVCodecContext* avctx, VaapiContextFfmpeg* vactx = 0);
55 PixelFormat get_format(AVCodecContext* avctx, const PixelFormat* fmt);
56 int get_buffer(AVCodecContext* avctx, AVFrame* pic);
57 int reget_buffer(AVCodecContext* avctx, AVFrame* pic);
58 void release_buffer(AVCodecContext *avctx, AVFrame *pic);
61 #ifdef HAVE_SWSCALE_H
63 /// A wrapper round an SwsContext that ensures it's
64 /// freed on destruction.
65 class SwsContextWrapper
67 public:
69 SwsContextWrapper(SwsContext* context)
71 _context(context)
74 ~SwsContextWrapper()
76 sws_freeContext(_context);
79 SwsContext* getContext() const { return _context; }
81 private:
82 SwsContext* _context;
85 #endif
88 // A Wrapper ensuring an AVCodecContext is closed and freed
89 // on destruction.
90 class CodecContextWrapper
92 public:
93 CodecContextWrapper(AVCodecContext* context)
95 _codecCtx(context)
98 ~CodecContextWrapper()
100 if (_codecCtx)
102 avcodec_close(_codecCtx);
103 clear_vaapi_context(_codecCtx);
104 av_free(_codecCtx);
108 AVCodecContext* getContext() const { return _codecCtx; }
110 private:
111 AVCodecContext* _codecCtx;
115 VideoDecoderFfmpeg::VideoDecoderFfmpeg(videoCodecType format, int width, int height)
117 _videoCodec(NULL)
120 CodecID codec_id = flashToFfmpegCodec(format);
121 init(codec_id, width, height);
125 VideoDecoderFfmpeg::VideoDecoderFfmpeg(const VideoInfo& info)
127 _videoCodec(NULL)
130 CodecID codec_id = CODEC_ID_NONE;
132 if ( info.type == CODEC_TYPE_FLASH )
134 codec_id = flashToFfmpegCodec(static_cast<videoCodecType>(info.codec));
136 else codec_id = static_cast<CodecID>(info.codec);
138 // This would cause nasty segfaults.
139 if (codec_id == CODEC_ID_NONE)
141 boost::format msg = boost::format(_("Cannot find suitable "
142 "decoder for flash codec %d")) % info.codec;
143 throw MediaException(msg.str());
146 boost::uint8_t* extradata=0;
147 int extradataSize=0;
148 if (info.extra.get())
150 if (dynamic_cast<ExtraVideoInfoFfmpeg*>(info.extra.get())) {
151 const ExtraVideoInfoFfmpeg& ei =
152 static_cast<ExtraVideoInfoFfmpeg&>(*info.extra);
153 extradata = ei.data;
154 extradataSize = ei.dataSize;
156 else if (dynamic_cast<ExtraVideoInfoFlv*>(info.extra.get())) {
157 const ExtraVideoInfoFlv& ei =
158 static_cast<ExtraVideoInfoFlv&>(*info.extra);
159 extradata = ei.data.get();
160 extradataSize = ei.size;
162 else {
163 std::abort();
166 init(codec_id, info.width, info.height, extradata, extradataSize);
169 void
170 VideoDecoderFfmpeg::init(enum CodecID codecId, int /*width*/, int /*height*/,
171 boost::uint8_t* extradata, int extradataSize)
173 // Init the avdecoder-decoder
174 avcodec_init();
175 avcodec_register_all();// change this to only register need codec?
177 _videoCodec = avcodec_find_decoder(codecId);
179 if (!_videoCodec) {
180 throw MediaException(_("libavcodec can't decode this video format"));
183 _videoCodecCtx.reset(new CodecContextWrapper(avcodec_alloc_context()));
184 if (!_videoCodecCtx->getContext()) {
185 throw MediaException(_("libavcodec couldn't allocate context"));
188 AVCodecContext* const ctx = _videoCodecCtx->getContext();
190 ctx->extradata = extradata;
191 ctx->extradata_size = extradataSize;
193 ctx->get_format = get_format;
194 ctx->get_buffer = get_buffer;
195 ctx->reget_buffer = reget_buffer;
196 ctx->release_buffer = release_buffer;
198 #ifdef HAVE_VA_VA_H
199 if (vaapi_is_enabled()) {
200 VaapiContextFfmpeg *vactx = VaapiContextFfmpeg::create(codecId);
201 if (vactx)
202 reset_context(ctx, vactx);
204 #endif
206 int ret = avcodec_open(ctx, _videoCodec);
207 if (ret < 0) {
208 boost::format msg = boost::format(_("libavcodec "
209 "failed to initialize FFMPEG "
210 "codec %s (%d)")) %
211 _videoCodec->name % (int)codecId;
213 throw MediaException(msg.str());
216 log_debug(_("VideoDecoder: initialized FFMPEG codec %s (%d)"),
217 _videoCodec->name, (int)codecId);
221 VideoDecoderFfmpeg::~VideoDecoderFfmpeg()
226 VideoDecoderFfmpeg::width() const
228 if (!_videoCodecCtx.get()) return 0;
229 return _videoCodecCtx->getContext()->width;
233 VideoDecoderFfmpeg::height() const
235 if (!_videoCodecCtx.get()) return 0;
236 return _videoCodecCtx->getContext()->height;
239 std::auto_ptr<image::GnashImage>
240 VideoDecoderFfmpeg::frameToImage(AVCodecContext* srcCtx,
241 const AVFrame& srcFrameRef)
243 const AVFrame *srcFrame = &srcFrameRef;
244 PixelFormat srcPixFmt = srcCtx->pix_fmt;
246 const int width = srcCtx->width;
247 const int height = srcCtx->height;
249 #ifdef FFMPEG_VP6A
250 PixelFormat pixFmt = (srcCtx->codec->id == CODEC_ID_VP6A) ?
251 PIX_FMT_RGBA : PIX_FMT_RGB24;
252 #else
253 PixelFormat pixFmt = PIX_FMT_RGB24;
254 #endif
256 std::auto_ptr<image::GnashImage> im;
258 #ifdef HAVE_VA_VA_H
259 VaapiContextFfmpeg * const vactx = get_vaapi_context(srcCtx);
260 if (vactx) {
261 VaapiSurfaceFfmpeg * const vaSurface = vaapi_get_surface(&srcFrameRef);
262 if (!vaSurface) {
263 im.reset();
264 return im;
266 im.reset(new GnashVaapiImage(vaSurface->get(), image::TYPE_RGBA));
267 return im;
269 #endif
271 #ifdef HAVE_SWSCALE_H
272 // Check whether the context wrapper exists
273 // already.
274 if (!_swsContext.get()) {
276 _swsContext.reset(new SwsContextWrapper(
277 sws_getContext(width, height, srcPixFmt, width, height,
278 pixFmt, SWS_BILINEAR, NULL, NULL, NULL)
281 // Check that the context was assigned.
282 if (!_swsContext->getContext()) {
284 // This means we will try to assign the
285 // context again next time.
286 _swsContext.reset();
288 // Can't do anything now, though.
289 return im;
292 #endif
294 int bufsize = avpicture_get_size(pixFmt, width, height);
295 if (bufsize == -1) return im;
297 switch (pixFmt)
299 case PIX_FMT_RGBA:
300 im.reset(new image::ImageRGBA(width, height));
301 break;
302 case PIX_FMT_RGB24:
303 im.reset(new image::ImageRGB(width, height));
304 break;
305 default:
306 log_error(_("Pixel format not handled"));
307 return im;
310 AVPicture picture;
312 // Let ffmpeg write directly to the GnashImage data. It is an uninitialized
313 // buffer here, so do not return the image if there is any error in
314 // conversion.
315 avpicture_fill(&picture, im->begin(), pixFmt, width, height);
317 #ifndef HAVE_SWSCALE_H
318 img_convert(&picture, PIX_FMT_RGB24, (AVPicture*)srcFrame,
319 srcPixFmt, width, height);
320 #else
322 // Is it possible for the context to be reset
323 // to NULL once it's been created?
324 assert(_swsContext->getContext());
326 int rv = sws_scale(_swsContext->getContext(),
327 const_cast<uint8_t**>(srcFrame->data),
328 const_cast<int*>(srcFrame->linesize), 0, height, picture.data,
329 picture.linesize);
331 if (rv == -1) {
332 im.reset();
333 return im;
336 #endif
338 return im;
342 std::auto_ptr<image::GnashImage>
343 VideoDecoderFfmpeg::decode(const boost::uint8_t* input,
344 boost::uint32_t input_size)
346 // This object shouldn't exist if there's no codec, as it can'
347 // do anything anyway.
348 assert(_videoCodecCtx.get());
350 std::auto_ptr<image::GnashImage> ret;
352 AVFrame* frame = avcodec_alloc_frame();
353 if ( ! frame ) {
354 log_error(_("Out of memory while allocating avcodec frame"));
355 return ret;
358 int bytes = 0;
359 // no idea why avcodec_decode_video wants a non-const input...
360 #if LIBAVCODEC_VERSION_MAJOR >= 53
361 AVPacket pkt;
362 av_init_packet(&pkt);
363 pkt.data = (uint8_t*) input;
364 pkt.size = input_size;
365 avcodec_decode_video2(_videoCodecCtx->getContext(), frame, &bytes,
366 &pkt);
367 #else
368 avcodec_decode_video(_videoCodecCtx->getContext(), frame, &bytes,
369 input, input_size);
370 #endif
372 if (!bytes) {
373 log_error(_("Decoding of a video frame failed"));
374 av_free(frame);
375 return ret;
378 ret = frameToImage(_videoCodecCtx->getContext(), *frame);
380 // FIXME: av_free doesn't free frame->data!
381 av_free(frame);
382 return ret;
386 void
387 VideoDecoderFfmpeg::push(const EncodedVideoFrame& buffer)
389 _video_frames.push_back(&buffer);
392 std::auto_ptr<image::GnashImage>
393 VideoDecoderFfmpeg::pop()
395 std::auto_ptr<image::GnashImage> ret;
397 for (std::vector<const EncodedVideoFrame*>::iterator it =
398 _video_frames.begin(), end = _video_frames.end(); it != end; ++it) {
399 ret = decode((*it)->data(), (*it)->dataSize());
402 _video_frames.clear();
404 return ret;
407 bool
408 VideoDecoderFfmpeg::peek()
410 return (!_video_frames.empty());
413 /* public static */
414 enum CodecID
415 VideoDecoderFfmpeg::flashToFfmpegCodec(videoCodecType format)
417 // Find the decoder and init the parser
418 switch(format) {
419 case VIDEO_CODEC_H264:
420 return CODEC_ID_H264;
421 case VIDEO_CODEC_H263:
422 // CODEC_ID_H263I didn't work with Lavc51.50.0
423 // and NetStream-SquareTest.swf
424 return CODEC_ID_FLV1;
425 case VIDEO_CODEC_VP6:
426 return CODEC_ID_VP6F;
427 #ifdef FFMPEG_VP6A
428 case VIDEO_CODEC_VP6A:
429 return CODEC_ID_VP6A;
430 #endif
431 case VIDEO_CODEC_SCREENVIDEO:
432 return CODEC_ID_FLASHSV;
433 default:
434 log_error(_("Unsupported video codec %d"),
435 static_cast<int>(format));
436 return CODEC_ID_NONE;
440 namespace {
442 inline VaapiContextFfmpeg*
443 get_vaapi_context(AVCodecContext* avctx)
445 #ifdef HAVE_VA_VA_H
446 return static_cast<VaapiContextFfmpeg *>(avctx->hwaccel_context);
447 #else
448 UNUSED(avctx);
449 return 0;
450 #endif
453 inline void
454 set_vaapi_context(AVCodecContext* avctx, VaapiContextFfmpeg* vactx)
456 #ifdef HAVE_VA_VA_H
457 avctx->hwaccel_context = vactx;
458 #else
459 UNUSED(avctx), UNUSED(vactx);
460 #endif
464 inline void
465 clear_vaapi_context(AVCodecContext* avctx)
467 #ifdef HAVE_VA_VA_H
468 VaapiContextFfmpeg* const vactx = get_vaapi_context(avctx);
469 if (!vactx) return;
471 delete vactx;
472 set_vaapi_context(avctx, NULL);
473 #else
474 UNUSED(avctx);
475 #endif
478 /// (Re)set AVCodecContext to sane values
479 void
480 reset_context(AVCodecContext* avctx, VaapiContextFfmpeg* vactx)
482 clear_vaapi_context(avctx);
483 set_vaapi_context(avctx, vactx);
485 avctx->thread_count = 1;
486 avctx->draw_horiz_band = 0;
487 if (vactx) {
488 avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
490 else avctx->slice_flags = 0;
493 /// AVCodecContext.get_format() implementation
494 PixelFormat
495 get_format(AVCodecContext* avctx, const PixelFormat* fmt)
497 #ifdef HAVE_VA_VA_H
498 VaapiContextFfmpeg* const vactx = get_vaapi_context(avctx);
500 if (vactx) {
501 for (int i = 0; fmt[i] != PIX_FMT_NONE; i++) {
502 if (fmt[i] != PIX_FMT_VAAPI_VLD) continue;
504 if (vactx->initDecoder(avctx->width, avctx->height)) {
505 return fmt[i];
509 #endif
511 reset_context(avctx);
512 return avcodec_default_get_format(avctx, fmt);
515 /// AVCodecContext.get_buffer() implementation
517 get_buffer(AVCodecContext* avctx, AVFrame* pic)
519 VaapiContextFfmpeg* const vactx = get_vaapi_context(avctx);
520 if (!vactx) return avcodec_default_get_buffer(avctx, pic);
522 #ifdef HAVE_VA_VA_H
523 if (!vactx->initDecoder(avctx->width, avctx->height)) return -1;
525 VaapiSurfaceFfmpeg * const surface = vactx->getSurface();
526 if (!surface) return -1;
528 vaapi_set_surface(pic, surface);
530 static unsigned int pic_num = 0;
531 pic->type = FF_BUFFER_TYPE_USER;
532 pic->age = ++pic_num - surface->getPicNum();
533 surface->setPicNum(pic_num);
534 return 0;
535 #endif
536 return -1;
539 /// AVCodecContext.reget_buffer() implementation
541 reget_buffer(AVCodecContext* avctx, AVFrame* pic)
543 VaapiContextFfmpeg* const vactx = get_vaapi_context(avctx);
545 if (!vactx) return avcodec_default_reget_buffer(avctx, pic);
547 return get_buffer(avctx, pic);
550 /// AVCodecContext.release_buffer() implementation
551 void
552 release_buffer(AVCodecContext *avctx, AVFrame *pic)
554 VaapiContextFfmpeg* const vactx = get_vaapi_context(avctx);
555 if (!vactx) {
556 avcodec_default_release_buffer(avctx, pic);
557 return;
560 #ifdef HAVE_VA_VA_H
561 VaapiSurfaceFfmpeg* const surface = vaapi_get_surface(pic);
562 delete surface;
564 pic->data[0] = NULL;
565 pic->data[1] = NULL;
566 pic->data[2] = NULL;
567 pic->data[3] = NULL;
568 #endif
573 } // gnash.media.ffmpeg namespace
574 } // gnash.media namespace
575 } // gnash namespace