1 // VideoDecoderFfmpeg.cpp: Video decoding using the FFMPEG library.
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc.
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
22 #include "gnashconfig.h"
25 #include "VideoDecoderFfmpeg.h"
27 #include <boost/scoped_array.hpp>
28 #include <boost/format.hpp>
31 #include "ffmpegHeaders.h"
32 #include "MediaParserFfmpeg.h" // for ExtraVideoInfoFfmpeg
33 #include "GnashException.h" // for MediaException
35 #include "FLVParser.h"
38 # include "vaapi_utils.h"
39 # include "VideoDecoderFfmpegVaapi.h"
40 # include "GnashVaapiImage.h"
47 class VaapiContextFfmpeg
;
49 // Forward declarations of VAAPI functions.
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
);
63 /// A wrapper round an SwsContext that ensures it's
64 /// freed on destruction.
65 class SwsContextWrapper
69 SwsContextWrapper(SwsContext
* context
)
76 sws_freeContext(_context
);
79 SwsContext
* getContext() const { return _context
; }
88 // A Wrapper ensuring an AVCodecContext is closed and freed
90 class CodecContextWrapper
93 CodecContextWrapper(AVCodecContext
* context
)
98 ~CodecContextWrapper()
102 avcodec_close(_codecCtx
);
103 clear_vaapi_context(_codecCtx
);
108 AVCodecContext
* getContext() const { return _codecCtx
; }
111 AVCodecContext
* _codecCtx
;
115 VideoDecoderFfmpeg::VideoDecoderFfmpeg(videoCodecType format
, int width
, int height
)
120 CodecID codec_id
= flashToFfmpegCodec(format
);
121 init(codec_id
, width
, height
);
125 VideoDecoderFfmpeg::VideoDecoderFfmpeg(const VideoInfo
& info
)
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;
148 if (info
.extra
.get())
150 if (dynamic_cast<ExtraVideoInfoFfmpeg
*>(info
.extra
.get())) {
151 const ExtraVideoInfoFfmpeg
& ei
=
152 static_cast<ExtraVideoInfoFfmpeg
&>(*info
.extra
);
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
;
166 init(codec_id
, info
.width
, info
.height
, extradata
, extradataSize
);
170 VideoDecoderFfmpeg::init(enum CodecID codecId
, int /*width*/, int /*height*/,
171 boost::uint8_t* extradata
, int extradataSize
)
173 // Init the avdecoder-decoder
175 avcodec_register_all();// change this to only register need codec?
177 _videoCodec
= avcodec_find_decoder(codecId
);
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
;
199 if (vaapi_is_enabled()) {
200 VaapiContextFfmpeg
*vactx
= VaapiContextFfmpeg::create(codecId
);
202 reset_context(ctx
, vactx
);
206 int ret
= avcodec_open(ctx
, _videoCodec
);
208 boost::format msg
= boost::format(_("libavcodec "
209 "failed to initialize FFMPEG "
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
;
250 PixelFormat pixFmt
= (srcCtx
->codec
->id
== CODEC_ID_VP6A
) ?
251 PIX_FMT_RGBA
: PIX_FMT_RGB24
;
253 PixelFormat pixFmt
= PIX_FMT_RGB24
;
256 std::auto_ptr
<image::GnashImage
> im
;
259 VaapiContextFfmpeg
* const vactx
= get_vaapi_context(srcCtx
);
261 VaapiSurfaceFfmpeg
* const vaSurface
= vaapi_get_surface(&srcFrameRef
);
266 im
.reset(new GnashVaapiImage(vaSurface
->get(), image::TYPE_RGBA
));
271 #ifdef HAVE_SWSCALE_H
272 // Check whether the context wrapper exists
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.
288 // Can't do anything now, though.
294 int bufsize
= avpicture_get_size(pixFmt
, width
, height
);
295 if (bufsize
== -1) return im
;
300 im
.reset(new image::ImageRGBA(width
, height
));
303 im
.reset(new image::ImageRGB(width
, height
));
306 log_error(_("Pixel format not handled"));
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
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
);
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
,
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();
354 log_error(_("Out of memory while allocating avcodec frame"));
359 // no idea why avcodec_decode_video wants a non-const input...
360 #if LIBAVCODEC_VERSION_MAJOR >= 53
362 av_init_packet(&pkt
);
363 pkt
.data
= (uint8_t*) input
;
364 pkt
.size
= input_size
;
365 avcodec_decode_video2(_videoCodecCtx
->getContext(), frame
, &bytes
,
368 avcodec_decode_video(_videoCodecCtx
->getContext(), frame
, &bytes
,
373 log_error(_("Decoding of a video frame failed"));
378 ret
= frameToImage(_videoCodecCtx
->getContext(), *frame
);
380 // FIXME: av_free doesn't free frame->data!
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();
408 VideoDecoderFfmpeg::peek()
410 return (!_video_frames
.empty());
415 VideoDecoderFfmpeg::flashToFfmpegCodec(videoCodecType format
)
417 // Find the decoder and init the parser
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
;
428 case VIDEO_CODEC_VP6A
:
429 return CODEC_ID_VP6A
;
431 case VIDEO_CODEC_SCREENVIDEO
:
432 return CODEC_ID_FLASHSV
;
434 log_error(_("Unsupported video codec %d"),
435 static_cast<int>(format
));
436 return CODEC_ID_NONE
;
442 inline VaapiContextFfmpeg
*
443 get_vaapi_context(AVCodecContext
* avctx
)
446 return static_cast<VaapiContextFfmpeg
*>(avctx
->hwaccel_context
);
454 set_vaapi_context(AVCodecContext
* avctx
, VaapiContextFfmpeg
* vactx
)
457 avctx
->hwaccel_context
= vactx
;
459 UNUSED(avctx
), UNUSED(vactx
);
465 clear_vaapi_context(AVCodecContext
* avctx
)
468 VaapiContextFfmpeg
* const vactx
= get_vaapi_context(avctx
);
472 set_vaapi_context(avctx
, NULL
);
478 /// (Re)set AVCodecContext to sane values
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;
488 avctx
->slice_flags
= SLICE_FLAG_CODED_ORDER
|SLICE_FLAG_ALLOW_FIELD
;
490 else avctx
->slice_flags
= 0;
493 /// AVCodecContext.get_format() implementation
495 get_format(AVCodecContext
* avctx
, const PixelFormat
* fmt
)
498 VaapiContextFfmpeg
* const vactx
= get_vaapi_context(avctx
);
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
)) {
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
);
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
);
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
552 release_buffer(AVCodecContext
*avctx
, AVFrame
*pic
)
554 VaapiContextFfmpeg
* const vactx
= get_vaapi_context(avctx
);
556 avcodec_default_release_buffer(avctx
, pic
);
561 VaapiSurfaceFfmpeg
* const surface
= vaapi_get_surface(pic
);
573 } // gnash.media.ffmpeg namespace
574 } // gnash.media namespace