From b6cb9b004506cb3d00db22fd276d288c2eabbf8a Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 17 Dec 2016 00:17:30 +0100 Subject: [PATCH] ffmpeg: support videos with alpha; add ffmpeg-prefer-libvpx prc var ffmpeg-prefer-libvpx forces ffmpeg to use the libvpx decoder for VP8/VP9 files, allowing the playback of WebM files with an alpha channel. --- panda/src/ffmpeg/config_ffmpeg.cxx | 8 ++++ panda/src/ffmpeg/config_ffmpeg.h | 1 + panda/src/ffmpeg/ffmpegVideoCursor.cxx | 61 ++++++++++++++++++-------- panda/src/ffmpeg/ffmpegVideoCursor.h | 1 + 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/panda/src/ffmpeg/config_ffmpeg.cxx b/panda/src/ffmpeg/config_ffmpeg.cxx index 115b3c31c3..f0d6a5518e 100644 --- a/panda/src/ffmpeg/config_ffmpeg.cxx +++ b/panda/src/ffmpeg/config_ffmpeg.cxx @@ -76,6 +76,14 @@ ConfigVariableInt ffmpeg_read_buffer_size "This is important for performance. A typical size is that of a " "cache page, e.g. 4kb.")); +ConfigVariableBool ffmpeg_prefer_libvpx +("ffmpeg-prefer-libvpx", false, + PRC_DESC("If this is true, Panda will overrule ffmpeg's best judgment on " + "which decoder to use for decoding VP8 and VP9 files, and try to " + "choose libvpx. This is useful when you want to play WebM videos " + "with an alpha channel, which aren't supported by ffmpeg's own " + "VP8/VP9 decoders.")); + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be diff --git a/panda/src/ffmpeg/config_ffmpeg.h b/panda/src/ffmpeg/config_ffmpeg.h index 58f81af821..c3854fc9e1 100644 --- a/panda/src/ffmpeg/config_ffmpeg.h +++ b/panda/src/ffmpeg/config_ffmpeg.h @@ -31,6 +31,7 @@ extern ConfigVariableBool ffmpeg_support_seek; extern ConfigVariableBool ffmpeg_global_lock; extern ConfigVariableEnum ffmpeg_thread_priority; extern ConfigVariableInt ffmpeg_read_buffer_size; +extern ConfigVariableBool ffmpeg_prefer_libvpx; extern EXPCL_FFMPEG void init_libffmpeg(); diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index 8affe3c116..1829e003de 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -22,6 +22,7 @@ extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" + #include "libavutil/pixdesc.h" #ifdef HAVE_SWSCALE #include "libswscale/swscale.h" #endif @@ -35,11 +36,16 @@ PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decod PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek"); PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR"); - #if LIBAVFORMAT_VERSION_MAJOR < 53 #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO #endif +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100) +#define AV_PIX_FMT_NONE PIX_FMT_NONE +#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 +#define AV_PIX_FMT_BGRA PIX_FMT_BGRA +#endif + /** * This constructor is only used when reading from a bam file. */ @@ -55,6 +61,7 @@ FfmpegVideoCursor() : _format_ctx(NULL), _video_ctx(NULL), _convert_ctx(NULL), + _pixel_format(AV_PIX_FMT_NONE), _video_index(-1), _frame(NULL), _frame_out(NULL), @@ -80,17 +87,6 @@ init_from(FfmpegVideo *source) { ReMutexHolder av_holder(_av_lock); -#ifdef HAVE_SWSCALE - nassertv(_convert_ctx == NULL); - _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 74, 100) - _size_x, _size_y, AV_PIX_FMT_BGR24, -#else - _size_x, _size_y, PIX_FMT_BGR24, -#endif - SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL); -#endif // HAVE_SWSCALE - #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100) _frame = av_frame_alloc(); _frame_out = av_frame_alloc(); @@ -115,6 +111,25 @@ init_from(FfmpegVideo *source) { _eof_known = false; _eof_frame = 0; + // Check if we got an alpha format. Please note that some video codecs + // (eg. libvpx) change the pix_fmt after decoding the first frame, which is + // why we didn't do this earlier. + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt); + if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) { + _num_components = 4; + _pixel_format = AV_PIX_FMT_BGRA; + } else { + _num_components = 3; + _pixel_format = AV_PIX_FMT_BGR24; + } + +#ifdef HAVE_SWSCALE + nassertv(_convert_ctx == NULL); + _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, + _size_x, _size_y, _pixel_format, + SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL); +#endif // HAVE_SWSCALE + #ifdef HAVE_THREADS set_max_readahead_frames(ffmpeg_max_readahead_frames); #endif // HAVE_THREADS @@ -495,7 +510,17 @@ open_stream() { return false; } - AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); + AVCodec *pVideoCodec = NULL; + if (ffmpeg_prefer_libvpx) { + if (_video_ctx->codec_id == AV_CODEC_ID_VP9) { + pVideoCodec = avcodec_find_decoder_by_name("libvpx-vp9"); + } else if (_video_ctx->codec_id == AV_CODEC_ID_VP8) { + pVideoCodec = avcodec_find_decoder_by_name("libvpx"); + } + } + if (pVideoCodec == NULL) { + pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); + } if (pVideoCodec == NULL) { ffmpeg_cat.info() << "Couldn't find codec\n"; @@ -515,7 +540,7 @@ open_stream() { _size_x = _video_ctx->width; _size_y = _video_ctx->height; - _num_components = 3; // Don't know how to implement RGBA movies yet. + _num_components = 3; _length = (double)_format_ctx->duration / (double)AV_TIME_BASE; _can_seek = true; _can_seek_fast = true; @@ -1075,8 +1100,8 @@ export_frame(FfmpegBuffer *buffer) { return; } - _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3); - _frame_out->linesize[0] = _size_x * -3; + _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * _num_components); + _frame_out->linesize[0] = _size_x * -_num_components; buffer->_begin_frame = _begin_frame; buffer->_end_frame = _end_frame; @@ -1086,7 +1111,7 @@ export_frame(FfmpegBuffer *buffer) { nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL); sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); #else - img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, + img_convert((AVPicture *)_frame_out, _pixel_format, (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); #endif } else { @@ -1094,7 +1119,7 @@ export_frame(FfmpegBuffer *buffer) { nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL); sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); #else - img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, + img_convert((AVPicture *)_frame_out, _pixel_format, (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); #endif } diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.h b/panda/src/ffmpeg/ffmpegVideoCursor.h index a44ec57490..199b533f47 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.h +++ b/panda/src/ffmpeg/ffmpegVideoCursor.h @@ -104,6 +104,7 @@ private: int _max_readahead_frames; ThreadPriority _thread_priority; PT(GenericThread) _thread; + AVPixelFormat _pixel_format; // This global Mutex protects calls to avcodec_opencloseetc. static ReMutex _av_lock;