diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index 4f2ca7ae7a..1c9f4aa9e0 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -58,6 +58,16 @@ ConfigVariableBool ffmpeg_support_seek "which can be much slower. Set this false only if you suspect " "a problem with av_seek_frame().")); +ConfigVariableBool ffmpeg_global_lock +("ffmpeg-global-lock", false, + PRC_DESC("Set this true to enable a single global mutex across *all* ffmpeg " + "operations. Leave this false to use the mutex only for " + "the ffmpeg operations that are generally known to be " + "not thread-safe. This will negatively affect ffmpeg performance, " + "especially when decoding multiple videos at once (including the " + "left and right channels of a stereo video). Set this true only " + "if you suspect a problem with ffmpeg's own thread-safe nature.")); + ConfigVariableEnum ffmpeg_thread_priority ("ffmpeg-thread-priority", TP_normal, PRC_DESC("The default thread priority at which to start ffmpeg decoder " diff --git a/panda/src/movies/config_movies.h b/panda/src/movies/config_movies.h index 9e85870fe8..676b72abf4 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -29,6 +29,7 @@ NotifyCategoryDecl(ffmpeg, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); extern ConfigVariableInt ffmpeg_max_readahead_frames; extern ConfigVariableBool ffmpeg_support_seek; +extern ConfigVariableBool ffmpeg_global_lock; extern ConfigVariableEnum ffmpeg_thread_priority; extern EXPCL_PANDA_MOVIES void init_libmovies(); diff --git a/panda/src/movies/ffmpegVideoCursor.cxx b/panda/src/movies/ffmpegVideoCursor.cxx index 0888383f37..cbf7bcf0ee 100644 --- a/panda/src/movies/ffmpegVideoCursor.cxx +++ b/panda/src/movies/ffmpegVideoCursor.cxx @@ -827,6 +827,22 @@ do_recycle_all_frames() { //////////////////////////////////////////////////////////////////// bool FfmpegVideoCursor:: fetch_packet(int default_frame) { + if (ffmpeg_global_lock) { + ReMutexHolder av_holder(_av_lock); + return do_fetch_packet(default_frame); + } else { + return do_fetch_packet(default_frame); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::do_fetch_packet +// Access: Private +// Description: As above, with the ffmpeg global lock held (if +// configured on). +//////////////////////////////////////////////////////////////////// +bool FfmpegVideoCursor:: +do_fetch_packet(int default_frame) { if (_packet0->data) { av_free_packet(_packet0); } @@ -906,12 +922,7 @@ fetch_frame(int frame) { PStatTimer timer(seek_pcollector); // Decode and discard the previous packet. -#if LIBAVCODEC_VERSION_INT < 3414272 - avcodec_decode_video(_video_ctx, _frame, - &finished, _packet1->data, _packet1->size); -#else - avcodec_decode_video2(_video_ctx, _frame, &finished, _packet1); -#endif + decode_frame(finished, _packet1); flip_packets(); _begin_frame = _packet_frame; if (fetch_packet(frame)) { @@ -925,23 +936,13 @@ fetch_frame(int frame) { // At this point, _packet0 contains the *next* packet to be // decoded next frame, and _packet1 contains the packet to decode // for this frame. -#if LIBAVCODEC_VERSION_INT < 3414272 - avcodec_decode_video(_video_ctx, _frame, - &finished, _packet1->data, _packet1->size); -#else - avcodec_decode_video2(_video_ctx, _frame, &finished, _packet1); -#endif + decode_frame(finished, _packet1); } else { // Just get the next frame. finished = 0; while (!finished && _packet0->data) { -#if LIBAVCODEC_VERSION_INT < 3414272 - avcodec_decode_video(_video_ctx, _frame, - &finished, _packet0->data, _packet0->size); -#else - avcodec_decode_video2(_video_ctx, _frame, &finished, _packet0); -#endif + decode_frame(finished, _packet0); _begin_frame = _packet_frame; fetch_packet(_begin_frame + 1); } @@ -951,6 +952,38 @@ fetch_frame(int frame) { _frame_ready = true; } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::decode_frame +// Access: Private +// Description: Called within the sub-thread. Decodes the data in +// the specified packet into _frame. +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +decode_frame(int &finished, AVPacket *packet) { + if (ffmpeg_global_lock) { + ReMutexHolder av_holder(_av_lock); + do_decode_frame(finished, packet); + } else { + do_decode_frame(finished, packet); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::do_decode_frame +// Access: Private +// Description: As above, with the ffmpeg global lock held (if +// configured on). +//////////////////////////////////////////////////////////////////// +void FfmpegVideoCursor:: +do_decode_frame(int &finished, AVPacket *packet) { +#if LIBAVCODEC_VERSION_INT < 3414272 + avcodec_decode_video(_video_ctx, _frame, + &finished, packet->data, packet->size); +#else + avcodec_decode_video2(_video_ctx, _frame, &finished, packet); +#endif +} + //////////////////////////////////////////////////////////////////// // Function: FfmpegVideoCursor::seek // Access: Private @@ -1182,13 +1215,25 @@ export_frame(FfmpegBuffer *buffer) { _frame_out->linesize[0] = _size_x * -3; buffer->_begin_frame = _begin_frame; buffer->_end_frame = _end_frame; + + if (ffmpeg_global_lock) { + ReMutexHolder av_holder(_av_lock); #ifdef HAVE_SWSCALE - 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); + 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, - (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); + img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, + (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); #endif + } else { +#ifdef HAVE_SWSCALE + 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, + (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); +#endif + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/movies/ffmpegVideoCursor.h b/panda/src/movies/ffmpegVideoCursor.h index be051cdfdb..1503f17b04 100644 --- a/panda/src/movies/ffmpegVideoCursor.h +++ b/panda/src/movies/ffmpegVideoCursor.h @@ -124,8 +124,11 @@ private: void do_recycle_all_frames(); bool fetch_packet(int default_frame); + bool do_fetch_packet(int default_frame); void flip_packets(); void fetch_frame(int frame); + void decode_frame(int &finished, AVPacket *packet); + void do_decode_frame(int &finished, AVPacket *packet); void seek(int frame, bool backward); int binary_seek(int min_frame, int max_frame, int target_frame, int num_iterations); void advance_to_frame(int frame);