Skip to content

Commit b6cb9b0

Browse files
committed
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.
1 parent 34068dc commit b6cb9b0

File tree

4 files changed

+53
-18
lines changed

4 files changed

+53
-18
lines changed

panda/src/ffmpeg/config_ffmpeg.cxx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ ConfigVariableInt ffmpeg_read_buffer_size
7676
"This is important for performance. A typical size is that of a "
7777
"cache page, e.g. 4kb."));
7878

79+
ConfigVariableBool ffmpeg_prefer_libvpx
80+
("ffmpeg-prefer-libvpx", false,
81+
PRC_DESC("If this is true, Panda will overrule ffmpeg's best judgment on "
82+
"which decoder to use for decoding VP8 and VP9 files, and try to "
83+
"choose libvpx. This is useful when you want to play WebM videos "
84+
"with an alpha channel, which aren't supported by ffmpeg's own "
85+
"VP8/VP9 decoders."));
86+
7987
/**
8088
* Initializes the library. This must be called at least once before any of
8189
* the functions or classes in this library can be used. Normally it will be

panda/src/ffmpeg/config_ffmpeg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern ConfigVariableBool ffmpeg_support_seek;
3131
extern ConfigVariableBool ffmpeg_global_lock;
3232
extern ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority;
3333
extern ConfigVariableInt ffmpeg_read_buffer_size;
34+
extern ConfigVariableBool ffmpeg_prefer_libvpx;
3435

3536
extern EXPCL_FFMPEG void init_libffmpeg();
3637

panda/src/ffmpeg/ffmpegVideoCursor.cxx

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
extern "C" {
2323
#include "libavcodec/avcodec.h"
2424
#include "libavformat/avformat.h"
25+
#include "libavutil/pixdesc.h"
2526
#ifdef HAVE_SWSCALE
2627
#include "libswscale/swscale.h"
2728
#endif
@@ -35,11 +36,16 @@ PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decod
3536
PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
3637
PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR");
3738

38-
3939
#if LIBAVFORMAT_VERSION_MAJOR < 53
4040
#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
4141
#endif
4242

43+
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100)
44+
#define AV_PIX_FMT_NONE PIX_FMT_NONE
45+
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
46+
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
47+
#endif
48+
4349
/**
4450
* This constructor is only used when reading from a bam file.
4551
*/
@@ -55,6 +61,7 @@ FfmpegVideoCursor() :
5561
_format_ctx(NULL),
5662
_video_ctx(NULL),
5763
_convert_ctx(NULL),
64+
_pixel_format(AV_PIX_FMT_NONE),
5865
_video_index(-1),
5966
_frame(NULL),
6067
_frame_out(NULL),
@@ -80,17 +87,6 @@ init_from(FfmpegVideo *source) {
8087

8188
ReMutexHolder av_holder(_av_lock);
8289

83-
#ifdef HAVE_SWSCALE
84-
nassertv(_convert_ctx == NULL);
85-
_convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt,
86-
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 74, 100)
87-
_size_x, _size_y, AV_PIX_FMT_BGR24,
88-
#else
89-
_size_x, _size_y, PIX_FMT_BGR24,
90-
#endif
91-
SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
92-
#endif // HAVE_SWSCALE
93-
9490
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
9591
_frame = av_frame_alloc();
9692
_frame_out = av_frame_alloc();
@@ -115,6 +111,25 @@ init_from(FfmpegVideo *source) {
115111
_eof_known = false;
116112
_eof_frame = 0;
117113

114+
// Check if we got an alpha format. Please note that some video codecs
115+
// (eg. libvpx) change the pix_fmt after decoding the first frame, which is
116+
// why we didn't do this earlier.
117+
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt);
118+
if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) {
119+
_num_components = 4;
120+
_pixel_format = AV_PIX_FMT_BGRA;
121+
} else {
122+
_num_components = 3;
123+
_pixel_format = AV_PIX_FMT_BGR24;
124+
}
125+
126+
#ifdef HAVE_SWSCALE
127+
nassertv(_convert_ctx == NULL);
128+
_convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt,
129+
_size_x, _size_y, _pixel_format,
130+
SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
131+
#endif // HAVE_SWSCALE
132+
118133
#ifdef HAVE_THREADS
119134
set_max_readahead_frames(ffmpeg_max_readahead_frames);
120135
#endif // HAVE_THREADS
@@ -495,7 +510,17 @@ open_stream() {
495510
return false;
496511
}
497512

498-
AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
513+
AVCodec *pVideoCodec = NULL;
514+
if (ffmpeg_prefer_libvpx) {
515+
if (_video_ctx->codec_id == AV_CODEC_ID_VP9) {
516+
pVideoCodec = avcodec_find_decoder_by_name("libvpx-vp9");
517+
} else if (_video_ctx->codec_id == AV_CODEC_ID_VP8) {
518+
pVideoCodec = avcodec_find_decoder_by_name("libvpx");
519+
}
520+
}
521+
if (pVideoCodec == NULL) {
522+
pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
523+
}
499524
if (pVideoCodec == NULL) {
500525
ffmpeg_cat.info()
501526
<< "Couldn't find codec\n";
@@ -515,7 +540,7 @@ open_stream() {
515540

516541
_size_x = _video_ctx->width;
517542
_size_y = _video_ctx->height;
518-
_num_components = 3; // Don't know how to implement RGBA movies yet.
543+
_num_components = 3;
519544
_length = (double)_format_ctx->duration / (double)AV_TIME_BASE;
520545
_can_seek = true;
521546
_can_seek_fast = true;
@@ -1075,8 +1100,8 @@ export_frame(FfmpegBuffer *buffer) {
10751100
return;
10761101
}
10771102

1078-
_frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3);
1079-
_frame_out->linesize[0] = _size_x * -3;
1103+
_frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * _num_components);
1104+
_frame_out->linesize[0] = _size_x * -_num_components;
10801105
buffer->_begin_frame = _begin_frame;
10811106
buffer->_end_frame = _end_frame;
10821107

@@ -1086,15 +1111,15 @@ export_frame(FfmpegBuffer *buffer) {
10861111
nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
10871112
sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
10881113
#else
1089-
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
1114+
img_convert((AVPicture *)_frame_out, _pixel_format,
10901115
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
10911116
#endif
10921117
} else {
10931118
#ifdef HAVE_SWSCALE
10941119
nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
10951120
sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
10961121
#else
1097-
img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
1122+
img_convert((AVPicture *)_frame_out, _pixel_format,
10981123
(AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
10991124
#endif
11001125
}

panda/src/ffmpeg/ffmpegVideoCursor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class EXPCL_FFMPEG FfmpegVideoCursor : public MovieVideoCursor {
104104
int _max_readahead_frames;
105105
ThreadPriority _thread_priority;
106106
PT(GenericThread) _thread;
107+
AVPixelFormat _pixel_format;
107108

108109
// This global Mutex protects calls to avcodec_opencloseetc.
109110
static ReMutex _av_lock;

0 commit comments

Comments
 (0)