diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index 6421f6809e..4f2ca7ae7a 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -50,6 +50,14 @@ ConfigVariableInt ffmpeg_max_readahead_frames "should read in advance of actual playback. Set this to 0 to " "decode ffmpeg videos in the main thread.")); +ConfigVariableBool ffmpeg_support_seek +("ffmpeg-support-seek", true, + PRC_DESC("True to use the av_seek_frame() function to seek within ffmpeg " + "video files. If this is false, Panda will only seek within a " + "file by reading it from the beginning until the desired point, " + "which can be much slower. Set this false only if you suspect " + "a problem with av_seek_frame().")); + 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 4d2a238580..9e85870fe8 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -19,6 +19,7 @@ #include "notifyCategoryProxy.h" #include "configVariableEnum.h" #include "configVariableInt.h" +#include "configVariableBool.h" #include "threadPriority.h" #include "dconfig.h" @@ -27,6 +28,7 @@ NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); NotifyCategoryDecl(ffmpeg, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); extern ConfigVariableInt ffmpeg_max_readahead_frames; +extern ConfigVariableBool ffmpeg_support_seek; 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 3913503ef1..0888383f37 100644 --- a/panda/src/movies/ffmpegVideoCursor.cxx +++ b/panda/src/movies/ffmpegVideoCursor.cxx @@ -104,8 +104,8 @@ init_from(FfmpegVideo *source) { memset(_packet1, 0, sizeof(AVPacket)); fetch_packet(0); - _initial_dts = _packet0->dts; fetch_frame(-1); + _initial_dts = _begin_frame; _current_frame = -1; _eof_known = false; @@ -341,7 +341,10 @@ set_time(double time, int loop_count) { } } - if (ffmpeg_cat.is_spam() /* && frame != _current_frame*/) { + // No point in trying to position before the first frame. + frame = max(frame, _initial_dts); + + if (ffmpeg_cat.is_spam() && frame != _current_frame) { ffmpeg_cat.spam() << "set_time(" << time << "): " << frame << ", loop_count = " << loop_count << "\n"; } @@ -655,8 +658,16 @@ thread_main() { ffmpeg_cat.spam() << "ffmpeg thread for " << _filename.get_basename() << " starting.\n"; } + + // First, push the first frame onto the readahead queue. + if (_frame_ready) { + PT(FfmpegBuffer) frame = do_alloc_frame(); + export_frame(frame); + MutexHolder holder(_lock); + _readahead_frames.push_back(frame); + } - // Repeatedly wait for something interesting to do, until we're told + // Now repeatedly wait for something interesting to do, until we're told // to shut down. while (_thread_status != TS_shutdown) { nassertv(_thread_status != TS_stopped); @@ -952,66 +963,54 @@ seek(int frame, bool backward) { static PStatCollector seek_pcollector("*:FFMPEG Video Decoding:Seek"); PStatTimer timer(seek_pcollector); - // Protect the call to av_seek_frame() in a global lock, just to be - // paranoid. - ReMutexHolder av_holder(_av_lock); - - PN_int64 target_ts = (PN_int64)frame; - if (target_ts < (PN_int64)(_initial_dts)) { - // Attempts to seek before the first packet will fail. - target_ts = _initial_dts; - } - int flags = 0; - if (backward) { - flags = AVSEEK_FLAG_BACKWARD; - //reset_stream(); - } - - if (av_seek_frame(_format_ctx, _video_index, target_ts, flags) < 0) { - if (ffmpeg_cat.is_spam()) { - ffmpeg_cat.spam() - << "Seek failure.\n"; + if (ffmpeg_support_seek) { + // Protect the call to av_seek_frame() in a global lock, just to be + // paranoid. + ReMutexHolder av_holder(_av_lock); + + PN_int64 target_ts = (PN_int64)frame; + if (target_ts < (PN_int64)(_initial_dts)) { + // Attempts to seek before the first packet will fail. + target_ts = _initial_dts; } - + int flags = 0; if (backward) { - // Now try to seek forward. - reset_stream(); - return seek(frame, false); + flags = AVSEEK_FLAG_BACKWARD; } - - // Try a binary search to get a little closer. - if (binary_seek(_initial_dts, frame, frame, 1) < 0) { + + if (av_seek_frame(_format_ctx, _video_index, target_ts, flags) < 0) { if (ffmpeg_cat.is_spam()) { ffmpeg_cat.spam() - << "Seek double failure.\n"; + << "Seek failure.\n"; } + + if (backward) { + // Now try to seek forward. + reset_stream(); + seek(frame, false); + return; + } + + // Try a binary search to get a little closer. + if (binary_seek(_initial_dts, frame, frame, 1) < 0) { + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Seek double failure.\n"; + } + reset_stream(); + return; + } + } + + fetch_packet(0); + fetch_frame(-1); + + } else { + // If seeking isn't supported, close-and-reopen. + if (backward) { reset_stream(); } } - - { - // Close and re-open the codec (presumably to flush the queue). - // Actually, this causes the stream to fail in certain video - // files, and doesn't seem to have any useful benefit. So screw - // it, and don't do this. - - /* - avcodec_close(_video_ctx); - AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); - if (pVideoCodec == 0) { - cleanup(); - return; - } - - if (avcodec_open(_video_ctx, pVideoCodec)<0) { - cleanup(); - return; - } - */ - } - - fetch_packet(0); - fetch_frame(-1); } //////////////////////////////////////////////////////////////////// @@ -1065,7 +1064,6 @@ reset_stream() { } fetch_packet(0); - _initial_dts = _packet0->dts; fetch_frame(-1); }