David Rose před 14 roky
rodič
revize
9f5d8a9548

+ 3 - 3
panda/src/movies/config_movies.cxx

@@ -45,15 +45,15 @@ ConfigureFn(config_movies) {
 }
 
 ConfigVariableInt ffmpeg_max_readahead_frames
-("ffmpeg-max-readahead-frames", 10,
+("ffmpeg-max-readahead-frames", 2,
  PRC_DESC("The maximum number of frames ahead which an ffmpeg decoder thread "
           "should read in advance of actual playback.  Set this to 0 to "
           "decode ffmpeg videos in the main thread."));
 
 ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority
 ("ffmpeg-thread-priority", TP_normal,
- PRC_DESC("The default thread priority at which to run the ffmpeg decoder thread "
-          "for a given video texture."));
+ PRC_DESC("The default thread priority at which to start ffmpeg decoder "
+          "threads."));
 
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libmovies

+ 101 - 69
panda/src/movies/ffmpegVideoCursor.cxx

@@ -25,8 +25,10 @@ extern "C" {
 }
 #include "pStatCollector.h"
 #include "pStatTimer.h"
+#include "mutexHolder.h"
+#include "reMutexHolder.h"
 
-Mutex FfmpegVideoCursor::_av_lock;
+ReMutex FfmpegVideoCursor::_av_lock;
 TypeHandle FfmpegVideoCursor::_type_handle;
 
 
@@ -74,6 +76,10 @@ init_from(FfmpegVideo *source) {
   _source = source;
   _filename = _source->get_filename();
 
+  // Hold the global lock while we open the file and create avcodec
+  // objects.
+  ReMutexHolder av_holder(_av_lock);
+
   if (!_source->get_subfile_info().is_empty()) {
     // Read a subfile.
     if (!_ffvfile.open_subfile(_source->get_subfile_info())) {
@@ -119,42 +125,41 @@ init_from(FfmpegVideo *source) {
     return;
   }
 
-  {
-    MutexHolder av_holder(_av_lock);
-    AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
-    if (pVideoCodec == NULL) {
-      movies_cat.info() 
-        << "Couldn't find codec\n";
-      cleanup();
-      return;
-    }
-    if (avcodec_open(_video_ctx, pVideoCodec) < 0) {
-      movies_cat.info() 
-        << "Couldn't open codec\n";
-      cleanup();
-      return;
-    }
-
-    _size_x = _video_ctx->width;
-    _size_y = _video_ctx->height;
-    _num_components = 3; // Don't know how to implement RGBA movies yet.
-    _length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
-    _can_seek = true;
-    _can_seek_fast = true;
-
+  AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
+  if (pVideoCodec == NULL) {
+    movies_cat.info() 
+      << "Couldn't find codec\n";
+    cleanup();
+    return;
+  }
+  if (avcodec_open(_video_ctx, pVideoCodec) < 0) {
+    movies_cat.info() 
+      << "Couldn't open codec\n";
+    cleanup();
+    return;
+  }
+  
+  _size_x = _video_ctx->width;
+  _size_y = _video_ctx->height;
+  _num_components = 3; // Don't know how to implement RGBA movies yet.
+  _length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
+  _can_seek = true;
+  _can_seek_fast = true;
+  
 #ifdef HAVE_SWSCALE
-    _convert_ctx = sws_getContext(_size_x, _size_y,
-                                  _video_ctx->pix_fmt, _size_x, _size_y,
-                                  PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+  _convert_ctx = sws_getContext(_size_x, _size_y,
+                                _video_ctx->pix_fmt, _size_x, _size_y,
+                                PIX_FMT_BGR24, SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
 #endif  // HAVE_SWSCALE
-  }
 
   _frame = avcodec_alloc_frame();
   _frame_out = avcodec_alloc_frame();
+
   if ((_frame == 0)||(_frame_out == 0)) {
     cleanup();
     return;
   }
+
   _packet0 = new AVPacket;
   _packet1 = new AVPacket;
   memset(_packet0, 0, sizeof(AVPacket));
@@ -162,9 +167,7 @@ init_from(FfmpegVideo *source) {
   
   fetch_packet(0.0);
   _initial_dts = _packet0->dts;
-  _packet_time = 0.0;
-  _begin_time = -1.0;
-  _end_time = 0.0;
+  fetch_frame(-1);
 
 #ifdef HAVE_THREADS
   set_max_readahead_frames(ffmpeg_max_readahead_frames);
@@ -398,10 +401,11 @@ fetch_buffer(double time) {
   Buffer *frame = NULL;
   if (_thread_status == TS_stopped) {
     // Non-threaded case.  Just get the next frame directly.
-    frame = do_alloc_frame();
-    
     fetch_time(time);
-    export_frame(frame);
+    if (_frame_ready) {
+      frame = do_alloc_frame();
+      export_frame(frame);
+    }
 
   } else {
     // Threaded case.  Wait for the thread to serve up the required
@@ -473,6 +477,9 @@ release_buffer(Buffer *buffer) {
 void FfmpegVideoCursor::
 cleanup() {
   stop_thread();
+  
+  // Hold the global lock while we free avcodec objects.
+  ReMutexHolder av_holder(_av_lock);
 
   if (_frame) {
     av_free(_frame);
@@ -500,26 +507,22 @@ cleanup() {
     delete _packet1;
     _packet1 = NULL;
   }
-
-  {
-    MutexHolder av_holder(_av_lock);
-    
+  
 #ifdef HAVE_SWSCALE
-    if (_convert_ctx != NULL) {
-      sws_freeContext(_convert_ctx);
-    }
-    _convert_ctx = NULL;
+  if (_convert_ctx != NULL) {
+    sws_freeContext(_convert_ctx);
+  }
+  _convert_ctx = NULL;
 #endif  // HAVE_SWSCALE
-    
-    if ((_video_ctx)&&(_video_ctx->codec)) {
-      avcodec_close(_video_ctx);
-    }
-    _video_ctx = NULL;
-    
-    if (_format_ctx) {
-      _ffvfile.close();
-      _format_ctx = NULL;
-    }
+  
+  if ((_video_ctx)&&(_video_ctx->codec)) {
+    avcodec_close(_video_ctx);
+  }
+  _video_ctx = NULL;
+  
+  if (_format_ctx) {
+    _ffvfile.close();
+    _format_ctx = NULL;
   }
 
   _video_index = -1;
@@ -597,9 +600,15 @@ do_poll() {
       nassertr(frame != NULL, false);
       _lock.release();
       fetch_frame(-1);
-      export_frame(frame);
-      _lock.acquire();
-      _readahead_frames.push_back(frame);
+      if (_frame_ready) {
+        export_frame(frame);
+        _lock.acquire();
+        _readahead_frames.push_back(frame);
+      } else {
+        // No frame.
+        _lock.acquire();
+        do_recycle_frame(frame);
+      }
       return true;
     }
 
@@ -615,10 +624,17 @@ do_poll() {
       nassertr(frame != NULL, false);
       _lock.release();
       fetch_time(seek_time);
-      export_frame(frame);
-      _lock.acquire();
-      do_recycle_all_frames();
-      _readahead_frames.push_back(frame);
+      if (_frame_ready) {
+        export_frame(frame);
+        _lock.acquire();
+        do_recycle_all_frames();
+        _readahead_frames.push_back(frame);
+      } else {
+        _lock.acquire();
+        do_recycle_all_frames();
+        do_recycle_frame(frame);
+      }
+
       if (_thread_status == TS_seeking) {
         // After seeking, we automatically transition to readahead.
         _thread_status = TS_readahead;
@@ -752,8 +768,11 @@ fetch_frame(double time) {
     // Get the next packet.  The first packet beyond the time we're
     // looking for marks the point to stop.
     if (fetch_packet(time)) {
-      ffmpeg_cat.debug()
-        << "end of video\n";
+      if (ffmpeg_cat.is_debug()) {
+        ffmpeg_cat.debug()
+          << "end of video\n";
+      }
+      _frame_ready = false;
       return true;
     }
     while (_packet_time <= time) {
@@ -766,8 +785,11 @@ fetch_frame(double time) {
 #endif
       flip_packets();
       if (fetch_packet(time)) {
-        ffmpeg_cat.debug()
-          << "end of video\n";
+        if (ffmpeg_cat.is_debug()) {
+          ffmpeg_cat.debug()
+            << "end of video\n";
+        }
+        _frame_ready = false;
         return true;
       }
     }
@@ -798,8 +820,8 @@ fetch_frame(double time) {
   }
 
   _end_time = _packet_time;
-
-  return (finished != 0);
+  _frame_ready = true;
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -829,7 +851,9 @@ seek(double t) {
     return;
   }
   {
-    MutexHolder av_holder(_av_lock);
+    // Hold the global lock while we close and re-open the video
+    // stream.
+    ReMutexHolder av_holder(_av_lock);
 
     avcodec_close(_video_ctx);
     AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
@@ -845,9 +869,12 @@ seek(double t) {
   }
 
   fetch_packet(t);
+  fetch_frame(-1);
+  /*
   if (_packet_time > t) {
     _packet_time = t;
   }
+  */
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -940,14 +967,19 @@ export_frame(MovieVideoCursor::Buffer *buffer) {
   static PStatCollector export_frame_collector("*:FFMPEG Convert Video to BGR");
   PStatTimer timer(export_frame_collector);
 
+  if (!_frame_ready) {
+    // No frame data ready, just fill with black.
+    memset(buffer->_block, 0, buffer->_block_size);
+    return;
+  }
+
   _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3);
   _frame_out->linesize[0] = _size_x * -3;
   buffer->_begin_time = _begin_time;
   buffer->_end_time = _end_time;
 #ifdef HAVE_SWSCALE
-  nassertv(_convert_ctx != 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);

+ 3 - 1
panda/src/movies/ffmpegVideoCursor.h

@@ -26,6 +26,7 @@
 #include "genericThread.h"
 #include "threadPriority.h"
 #include "pmutex.h"
+#include "reMutex.h"
 #include "conditionVar.h"
 
 struct AVFormatContext;
@@ -72,7 +73,7 @@ private:
   PT(GenericThread) _thread;
 
   // This global Mutex protects calls to avcodec_open/close/etc.
-  static Mutex _av_lock;
+  static ReMutex _av_lock;
 
   // Protects _readahead_frames, _recycled_buffers, and all the
   // immediately following members.
@@ -128,6 +129,7 @@ private:
   double _min_fseek;
   double _begin_time;
   double _end_time;
+  bool _frame_ready;
   
 public:
   static void register_with_read_factory();