Browse Source

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.
rdb 9 years ago
parent
commit
b6cb9b0045

+ 8 - 0
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

+ 1 - 0
panda/src/ffmpeg/config_ffmpeg.h

@@ -31,6 +31,7 @@ extern ConfigVariableBool ffmpeg_support_seek;
 extern ConfigVariableBool ffmpeg_global_lock;
 extern ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority;
 extern ConfigVariableInt ffmpeg_read_buffer_size;
+extern ConfigVariableBool ffmpeg_prefer_libvpx;
 
 extern EXPCL_FFMPEG void init_libffmpeg();
 

+ 43 - 18
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
   }

+ 1 - 0
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;