Panda3D
ffmpegVideoCursor.cxx
1 // Filename: ffmpegVideoCursor.cxx
2 // Created by: jyelon (01Aug2007)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "ffmpegVideoCursor.h"
16 #include "config_ffmpeg.h"
17 #include "pStatCollector.h"
18 #include "pStatTimer.h"
19 #include "mutexHolder.h"
20 #include "reMutexHolder.h"
21 #include "ffmpegVideo.h"
22 #include "bamReader.h"
23 extern "C" {
24  #include "libavcodec/avcodec.h"
25  #include "libavformat/avformat.h"
26 #ifdef HAVE_SWSCALE
27  #include "libswscale/swscale.h"
28 #endif
29 }
30 
31 ReMutex FfmpegVideoCursor::_av_lock;
32 TypeHandle FfmpegVideoCursor::_type_handle;
33 TypeHandle FfmpegVideoCursor::FfmpegBuffer::_type_handle;
34 
35 PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decoding:Fetch");
36 PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
37 PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR");
38 
39 
40 #if LIBAVFORMAT_VERSION_MAJOR < 53
41  #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
42 #endif
43 
44 ////////////////////////////////////////////////////////////////////
45 // Function: FfmpegVideoCursor::Default Constructor
46 // Access: Private
47 // Description: This constructor is only used when reading from a bam
48 // file.
49 ////////////////////////////////////////////////////////////////////
50 FfmpegVideoCursor::
51 FfmpegVideoCursor() :
52  _max_readahead_frames(0),
53  _thread_priority(ffmpeg_thread_priority),
54  _lock("FfmpegVideoCursor::_lock"),
55  _action_cvar(_lock),
56  _thread_status(TS_stopped),
57  _seek_frame(0),
58  _packet(NULL),
59  _format_ctx(NULL),
60  _video_ctx(NULL),
61  _convert_ctx(NULL),
62  _video_index(-1),
63  _frame(NULL),
64  _frame_out(NULL),
65  _eof_known(false)
66 {
67 }
68 
69 ////////////////////////////////////////////////////////////////////
70 // Function: FfmpegVideoCursor::init_from
71 // Access: Private
72 // Description: Specifies the source of the video cursor. This is
73 // normally called only by the constructor or when
74 // reading from a bam file.
75 ////////////////////////////////////////////////////////////////////
76 void FfmpegVideoCursor::
77 init_from(FfmpegVideo *source) {
78  nassertv(_thread == NULL && _thread_status == TS_stopped);
79  nassertv(source != NULL);
80  _source = source;
81  _filename = _source->get_filename();
82 
83  if (!open_stream()) {
84  cleanup();
85  return;
86  }
87 
88  ReMutexHolder av_holder(_av_lock);
89 
90 #ifdef HAVE_SWSCALE
91  nassertv(_convert_ctx == NULL);
92  _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt,
93 #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 74, 100)
94  _size_x, _size_y, AV_PIX_FMT_BGR24,
95 #else
96  _size_x, _size_y, PIX_FMT_BGR24,
97 #endif
98  SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
99 #endif // HAVE_SWSCALE
100 
101 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
102  _frame = av_frame_alloc();
103  _frame_out = av_frame_alloc();
104 #else
105  _frame = avcodec_alloc_frame();
106  _frame_out = avcodec_alloc_frame();
107 #endif
108 
109  if ((_frame == 0)||(_frame_out == 0)) {
110  cleanup();
111  return;
112  }
113 
114  _packet = new AVPacket;
115  memset(_packet, 0, sizeof(AVPacket));
116 
117  fetch_packet(0);
118  fetch_frame(-1);
119  _initial_dts = _begin_frame;
120 
121  _current_frame = -1;
122  _eof_known = false;
123  _eof_frame = 0;
124 
125 #ifdef HAVE_THREADS
126  set_max_readahead_frames(ffmpeg_max_readahead_frames);
127 #endif // HAVE_THREADS
128 }
129 
130 ////////////////////////////////////////////////////////////////////
131 // Function: FfmpegVideoCursor::Constructor
132 // Access: Published
133 // Description:
134 ////////////////////////////////////////////////////////////////////
135 FfmpegVideoCursor::
136 FfmpegVideoCursor(FfmpegVideo *src) :
137  _max_readahead_frames(0),
138  _thread_priority(ffmpeg_thread_priority),
139  _lock("FfmpegVideoCursor::_lock"),
140  _action_cvar(_lock),
141  _thread_status(TS_stopped),
142  _seek_frame(0),
143  _packet(NULL),
144  _format_ctx(NULL),
145  _video_ctx(NULL),
146  _convert_ctx(NULL),
147  _video_index(-1),
148  _frame(NULL),
149  _frame_out(NULL),
150  _eof_known(false)
151 {
152  init_from(src);
153 }
154 
155 ////////////////////////////////////////////////////////////////////
156 // Function: FfmpegVideoCursor::Destructor
157 // Access: Published
158 // Description:
159 ////////////////////////////////////////////////////////////////////
160 FfmpegVideoCursor::
161 ~FfmpegVideoCursor() {
162  cleanup();
163 }
164 
165 ////////////////////////////////////////////////////////////////////
166 // Function: FfmpegVideoCursor::set_max_readahead_frames
167 // Access: Published
168 // Description: Specifies the maximum number of frames that a
169 // sub-thread will attempt to read ahead of the current
170 // frame. Setting this to a nonzero allows the video
171 // decoding to take place in a sub-thread, which
172 // smoothes out the video decoding time by spreading it
173 // evenly over several frames. Set this number larger
174 // to increase the buffer between the currently visible
175 // frame and the first undecoded frame; set it smaller
176 // to reduce memory consumption.
177 //
178 // Setting this to zero forces the video to be decoded
179 // in the main thread. If threading is not available in
180 // the Panda build, this value is always zero.
181 ////////////////////////////////////////////////////////////////////
183 set_max_readahead_frames(int max_readahead_frames) {
184 #ifndef HAVE_THREADS
185  if (max_readahead_frames > 0) {
186  ffmpeg_cat.warning()
187  << "Couldn't set max_readahead_frames to " << max_readahead_frames
188  << ": threading not available.\n";
189  max_readahead_frames = 0;
190  }
191 #endif // HAVE_THREADS
192 
193  _max_readahead_frames = max_readahead_frames;
194  if (_max_readahead_frames > 0) {
195  if (_thread_status == TS_stopped) {
196  start_thread();
197  }
198  } else {
199  if (_thread_status != TS_stopped) {
200  stop_thread();
201  }
202  }
203 }
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: FfmpegVideoCursor::get_max_readahead_frames
207 // Access: Published
208 // Description: Returns the maximum number of frames that a
209 // sub-thread will attempt to read ahead of the current
210 // frame. See set_max_readahead_frames().
211 ////////////////////////////////////////////////////////////////////
214  return _max_readahead_frames;
215 }
216 
217 ////////////////////////////////////////////////////////////////////
218 // Function: FfmpegVideoCursor::set_thread_priority
219 // Access: Published
220 // Description: Changes the thread priority of the thread that
221 // decodes the ffmpeg video stream (if
222 // max_readahead_frames is nonzero). Normally you
223 // shouldn't mess with this, but there may be special
224 // cases where a precise balance of CPU utilization
225 // between the main thread and the various ffmpeg
226 // service threads may be needed.
227 ////////////////////////////////////////////////////////////////////
229 set_thread_priority(ThreadPriority thread_priority) {
230  if (_thread_priority != thread_priority) {
231  _thread_priority = thread_priority;
232  if (is_thread_started()) {
233  stop_thread();
234  start_thread();
235  }
236  }
237 }
238 
239 ////////////////////////////////////////////////////////////////////
240 // Function: FfmpegVideoCursor::get_thread_priority
241 // Access: Published
242 // Description: Returns the current thread priority of the thread that
243 // decodes the ffmpeg video stream (if
244 // max_readahead_frames is nonzero). See
245 // set_thread_priority().
246 ////////////////////////////////////////////////////////////////////
247 ThreadPriority FfmpegVideoCursor::
249  return _thread_priority;
250 }
251 
252 ////////////////////////////////////////////////////////////////////
253 // Function: FfmpegVideoCursor::start_thread
254 // Access: Published
255 // Description: Explicitly starts the ffmpeg decoding thread after it
256 // has been stopped by a call to stop_thread(). The
257 // thread is normally started automatically, so there is
258 // no need to call this method unless you have
259 // previously called stop_thread() for some reason.
260 ////////////////////////////////////////////////////////////////////
263  MutexHolder holder(_lock);
264 
265  if (_thread_status == TS_stopped && _max_readahead_frames > 0) {
266  // Get a unique name for the thread's sync name.
267  ostringstream strm;
268  strm << (void *)this;
269  _sync_name = strm.str();
270 
271  // Create and start the thread object.
272  _thread_status = TS_wait;
273  _thread = new GenericThread(_filename.get_basename(), _sync_name, st_thread_main, this);
274  if (!_thread->start(_thread_priority, true)) {
275  // Couldn't start the thread.
276  _thread = NULL;
277  _thread_status = TS_stopped;
278  }
279  }
280 }
281 
282 ////////////////////////////////////////////////////////////////////
283 // Function: FfmpegVideoCursor::stop_thread
284 // Access: Published
285 // Description: Explicitly stops the ffmpeg decoding thread. There
286 // is normally no reason to do this unless you want to
287 // maintain precise control over what threads are
288 // consuming CPU resources. Calling this method will
289 // make the video update in the main thread, regardless
290 // of the setting of max_readahead_frames, until you
291 // call start_thread() again.
292 ////////////////////////////////////////////////////////////////////
295  if (_thread_status != TS_stopped) {
296  PT(GenericThread) thread = _thread;
297  {
298  MutexHolder holder(_lock);
299  if (_thread_status != TS_stopped) {
300  _thread_status = TS_shutdown;
301  }
302  _action_cvar.notify();
303  _thread = NULL;
304  }
305 
306  // Now that we've released the lock, we can join the thread.
307  thread->join();
308  }
309 
310  // This is a good time to clean up all of the allocated frame
311  // objects. It's not really necessary to be holding the lock, since
312  // the thread is gone, but we'll grab it anyway just in case someone
313  // else starts the thread up again.
314  MutexHolder holder(_lock);
315 
316  _readahead_frames.clear();
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function: FfmpegVideoCursor::is_thread_started
321 // Access: Published
322 // Description: Returns true if the thread has been started, false if
323 // not. This will always return false if
324 // max_readahead_frames is 0.
325 ////////////////////////////////////////////////////////////////////
328  return (_thread_status != TS_stopped);
329 }
330 
331 ////////////////////////////////////////////////////////////////////
332 // Function: FfmpegVideoCursor::set_time
333 // Access: Published, Virtual
334 // Description: See MovieVideoCursor::set_time().
335 ////////////////////////////////////////////////////////////////////
337 set_time(double timestamp, int loop_count) {
338  int frame = (int)(timestamp / _video_timebase + 0.5);
339 
340  if (_eof_known) {
341  if (loop_count == 0) {
342  frame = frame % _eof_frame;
343  } else {
344  int last_frame = _eof_frame * loop_count;
345  if (frame < last_frame) {
346  frame = frame % _eof_frame;
347  } else {
348  frame = _eof_frame - 1;
349  }
350  }
351  }
352 
353  // No point in trying to position before the first frame.
354  frame = max(frame, _initial_dts);
355 
356  if (ffmpeg_cat.is_spam() && frame != _current_frame) {
357  ffmpeg_cat.spam()
358  << "set_time(" << timestamp << "): " << frame
359  << ", loop_count = " << loop_count << "\n";
360  }
361 
362  _current_frame = frame;
363  if (_current_frame_buffer != NULL) {
364  // If we've previously returned a frame, don't bother asking for a
365  // next one if that frame is still valid.
366  return (_current_frame >= _current_frame_buffer->_end_frame ||
367  _current_frame < _current_frame_buffer->_begin_frame);
368  }
369 
370  // If our last request didn't return a frame, try again.
371  return true;
372 }
373 
374 ////////////////////////////////////////////////////////////////////
375 // Function: FfmpegVideoCursor::fetch_buffer
376 // Access: Public, Virtual
377 // Description: See MovieVideoCursor::fetch_buffer.
378 ////////////////////////////////////////////////////////////////////
379 PT(MovieVideoCursor::Buffer) FfmpegVideoCursor::
380 fetch_buffer() {
381  MutexHolder holder(_lock);
382 
383  // If there was an error at any point, just return NULL.
384  if (_format_ctx == (AVFormatContext *)NULL) {
385  return NULL;
386  }
387 
388  PT(FfmpegBuffer) frame;
389  if (_thread_status == TS_stopped) {
390  // Non-threaded case. Just get the next frame directly.
391  advance_to_frame(_current_frame);
392  if (_frame_ready) {
393  frame = do_alloc_frame();
394  export_frame(frame);
395  }
396 
397  } else {
398  // Threaded case. Wait for the thread to serve up the required
399  // frames.
400  if (!_readahead_frames.empty()) {
401  frame = _readahead_frames.front();
402  _readahead_frames.pop_front();
403  _action_cvar.notify();
404  while (frame->_end_frame < _current_frame && !_readahead_frames.empty()) {
405  // This frame is too old. Discard it.
406  if (ffmpeg_cat.is_debug()) {
407  ffmpeg_cat.debug()
408  << "ffmpeg for " << _filename.get_basename()
409  << " at frame " << _current_frame << ", discarding frame at "
410  << frame->_begin_frame << "\n";
411  }
412  frame = _readahead_frames.front();
413  _readahead_frames.pop_front();
414  }
415  if (frame->_begin_frame > _current_frame) {
416  // This frame is too new. Empty all remaining frames and seek
417  // backwards.
418  if (ffmpeg_cat.is_debug()) {
419  ffmpeg_cat.debug()
420  << "ffmpeg for " << _filename.get_basename()
421  << " at frame " << _current_frame << ", encountered too-new frame at "
422  << frame->_begin_frame << "\n";
423  }
424  do_clear_all_frames();
425  if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) {
426  _thread_status = TS_seek;
427  _seek_frame = _current_frame;
428  _action_cvar.notify();
429  }
430  }
431  }
432  if (frame == NULL || frame->_end_frame < _current_frame) {
433  // No frame available, or the frame is too old. Seek.
434  if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) {
435  _thread_status = TS_seek;
436  _seek_frame = _current_frame;
437  _action_cvar.notify();
438  }
439  }
440  }
441 
442  if (frame != NULL) {
443  bool too_old = (frame->_end_frame < _current_frame && !ffmpeg_show_seek_frames);
444  bool too_new = frame->_begin_frame > _current_frame;
445  if (too_old || too_new) {
446  // The frame is too old or too new. Just recycle it.
447  frame = NULL;
448  }
449  }
450 
451  if (frame != NULL) {
452  _current_frame_buffer = frame;
453  if (ffmpeg_cat.is_debug()) {
454  ffmpeg_cat.debug()
455  << "ffmpeg for " << _filename.get_basename()
456  << " at frame " << _current_frame << ", returning frame at "
457  << frame->_begin_frame << "\n";
458  }
459  } else {
460  if (ffmpeg_cat.is_debug()) {
461  ffmpeg_cat.debug()
462  << "ffmpeg for " << _filename.get_basename()
463  << " at frame " << _current_frame << ", returning NULL\n";
464  }
465  }
466  return frame.p();
467 }
468 
469 ////////////////////////////////////////////////////////////////////
470 // Function: FfmpegVideoCursor::make_new_buffer
471 // Access: Protected, Virtual
472 // Description: May be called by a derived class to allocate a new
473 // Buffer object.
474 ////////////////////////////////////////////////////////////////////
475 PT(MovieVideoCursor::Buffer) FfmpegVideoCursor::
476 make_new_buffer() {
477  PT(FfmpegBuffer) frame = new FfmpegBuffer(size_x() * size_y() * get_num_components(), _video_timebase);
478  return frame.p();
479 }
480 
481 ////////////////////////////////////////////////////////////////////
482 // Function: FfmpegVideoCursor::open_stream
483 // Access: Private
484 // Description: Opens the stream for the first time, or when needed
485 // internally.
486 ////////////////////////////////////////////////////////////////////
487 bool FfmpegVideoCursor::
488 open_stream() {
489  nassertr(!_ffvfile.is_open(), false);
490 
491  // Hold the global lock while we open the file and create avcodec
492  // objects.
493  ReMutexHolder av_holder(_av_lock);
494 
495  if (!_source->get_subfile_info().is_empty()) {
496  // Read a subfile.
497  if (!_ffvfile.open_subfile(_source->get_subfile_info())) {
498  ffmpeg_cat.info()
499  << "Couldn't open " << _source->get_subfile_info() << "\n";
500  close_stream();
501  return false;
502  }
503 
504  } else {
505  // Read a filename.
506  if (!_ffvfile.open_vfs(_filename)) {
507  ffmpeg_cat.info()
508  << "Couldn't open " << _filename << "\n";
509  close_stream();
510  return false;
511  }
512  }
513 
514  nassertr(_format_ctx == NULL, false);
515  _format_ctx = _ffvfile.get_format_context();
516  nassertr(_format_ctx != NULL, false);
517 
518 #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 6, 0)
519  if (avformat_find_stream_info(_format_ctx, NULL) < 0) {
520 #else
521  if (av_find_stream_info(_format_ctx) < 0) {
522 #endif
523  ffmpeg_cat.info()
524  << "Couldn't find stream info\n";
525  close_stream();
526  return false;
527  }
528 
529  // Find the video stream
530  nassertr(_video_ctx == NULL, false);
531  for (int i = 0; i < (int)_format_ctx->nb_streams; ++i) {
532  if (_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
533  _video_index = i;
534  _video_ctx = _format_ctx->streams[i]->codec;
535  _video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
536  _min_fseek = (int)(3.0 / _video_timebase);
537  }
538  }
539 
540  if (_video_ctx == NULL) {
541  ffmpeg_cat.info()
542  << "Couldn't find video_ctx\n";
543  close_stream();
544  return false;
545  }
546 
547  AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
548  if (pVideoCodec == NULL) {
549  ffmpeg_cat.info()
550  << "Couldn't find codec\n";
551  close_stream();
552  return false;
553  }
554 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 8, 0)
555  if (avcodec_open2(_video_ctx, pVideoCodec, NULL) < 0) {
556 #else
557  if (avcodec_open(_video_ctx, pVideoCodec) < 0) {
558 #endif
559  ffmpeg_cat.info()
560  << "Couldn't open codec\n";
561  close_stream();
562  return false;
563  }
564 
565  _size_x = _video_ctx->width;
566  _size_y = _video_ctx->height;
567  _num_components = 3; // Don't know how to implement RGBA movies yet.
568  _length = (double)_format_ctx->duration / (double)AV_TIME_BASE;
569  _can_seek = true;
570  _can_seek_fast = true;
571 
572  return true;
573 }
574 
575 ////////////////////////////////////////////////////////////////////
576 // Function: FfmpegVideoCursor::close_stream
577 // Access: Private
578 // Description: Closes the stream, during cleanup or when needed
579 // internally.
580 ////////////////////////////////////////////////////////////////////
581 void FfmpegVideoCursor::
582 close_stream() {
583  // Hold the global lock while we free avcodec objects.
584  ReMutexHolder av_holder(_av_lock);
585 
586  if ((_video_ctx)&&(_video_ctx->codec)) {
587  avcodec_close(_video_ctx);
588  }
589  _video_ctx = NULL;
590 
591  _ffvfile.close();
592  _format_ctx = NULL;
593 
594  _video_index = -1;
595 }
596 
597 ////////////////////////////////////////////////////////////////////
598 // Function: FfmpegVideoCursor::cleanup
599 // Access: Private
600 // Description: Reset to a standard inactive state.
601 ////////////////////////////////////////////////////////////////////
602 void FfmpegVideoCursor::
603 cleanup() {
604  stop_thread();
605  close_stream();
606 
607  ReMutexHolder av_holder(_av_lock);
608 
609 #ifdef HAVE_SWSCALE
610  if (_convert_ctx != NULL) {
611  sws_freeContext(_convert_ctx);
612  }
613  _convert_ctx = NULL;
614 #endif // HAVE_SWSCALE
615 
616  if (_frame) {
617  av_free(_frame);
618  _frame = NULL;
619  }
620 
621  if (_frame_out) {
622  _frame_out->data[0] = 0;
623  av_free(_frame_out);
624  _frame_out = NULL;
625  }
626 
627  if (_packet) {
628  if (_packet->data) {
629 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
630  av_packet_unref(_packet);
631 #else
632  av_free_packet(_packet);
633 #endif
634  }
635  delete _packet;
636  _packet = NULL;
637  }
638 }
639 
640 ////////////////////////////////////////////////////////////////////
641 // Function: FfmpegVideoCursor::st_thread_main
642 // Access: Private, Static
643 // Description: The thread main function, static version (for passing
644 // to GenericThread).
645 ////////////////////////////////////////////////////////////////////
646 void FfmpegVideoCursor::
647 st_thread_main(void *self) {
648  ((FfmpegVideoCursor *)self)->thread_main();
649 }
650 
651 ////////////////////////////////////////////////////////////////////
652 // Function: FfmpegVideoCursor::thread_main
653 // Access: Private
654 // Description: The thread main function.
655 ////////////////////////////////////////////////////////////////////
656 void FfmpegVideoCursor::
657 thread_main() {
658  if (ffmpeg_cat.is_spam()) {
659  ffmpeg_cat.spam()
660  << "ffmpeg thread for " << _filename.get_basename() << " starting.\n";
661  }
662 
663  // First, push the first frame onto the readahead queue.
664  if (_frame_ready) {
665  PT(FfmpegBuffer) frame = do_alloc_frame();
666  export_frame(frame);
667  MutexHolder holder(_lock);
668  _readahead_frames.push_back(frame);
669  }
670 
671  // Now repeatedly wait for something interesting to do, until we're told
672  // to shut down.
673  MutexHolder holder(_lock);
674  while (_thread_status != TS_shutdown) {
675  nassertv(_thread_status != TS_stopped);
676  _action_cvar.wait();
677 
678  while (do_poll()) {
679  // Keep doing stuff as long as there's something to do.
680  _lock.release();
681  PStatClient::thread_tick(_sync_name);
683  _lock.acquire();
684  }
685  }
686 
687  _thread_status = TS_stopped;
688  if (ffmpeg_cat.is_spam()) {
689  ffmpeg_cat.spam()
690  << "ffmpeg thread for " << _filename.get_basename() << " stopped.\n";
691  }
692 }
693 
694 ////////////////////////////////////////////////////////////////////
695 // Function: FfmpegVideoCursor::do_poll
696 // Access: Private
697 // Description: Called within the sub-thread. Assumes the lock is
698 // already held. If there is something for the thread
699 // to do, does it and returns true. If there is nothing
700 // for the thread to do, returns false.
701 ////////////////////////////////////////////////////////////////////
702 bool FfmpegVideoCursor::
703 do_poll() {
704  switch (_thread_status) {
705  case TS_stopped:
706  case TS_seeking:
707  // This shouldn't be possible while the thread is running.
708  nassertr(false, false);
709  return false;
710 
711  case TS_wait:
712  // The video hasn't started playing yet.
713  return false;
714 
715  case TS_readahead:
716  if ((int)_readahead_frames.size() < _max_readahead_frames) {
717  // Time to read the next frame.
718  PT(FfmpegBuffer) frame = do_alloc_frame();
719  nassertr(frame != NULL, false);
720  _lock.release();
721  fetch_frame(-1);
722  if (_frame_ready) {
723  export_frame(frame);
724  _lock.acquire();
725  _readahead_frames.push_back(frame);
726  } else {
727  // No frame.
728  _lock.acquire();
729  }
730  return true;
731  }
732 
733  // No room for the next frame yet. Wait for more.
734  return false;
735 
736  case TS_seek:
737  // Seek to a specific frame.
738  {
739  int seek_frame = _seek_frame;
740  _thread_status = TS_seeking;
741  PT(FfmpegBuffer) frame = do_alloc_frame();
742  nassertr(frame != NULL, false);
743  _lock.release();
744  advance_to_frame(seek_frame);
745  if (_frame_ready) {
746  export_frame(frame);
747  _lock.acquire();
748  do_clear_all_frames();
749  _readahead_frames.push_back(frame);
750  } else {
751  _lock.acquire();
752  do_clear_all_frames();
753  }
754 
755  if (_thread_status == TS_seeking) {
756  // After seeking, we automatically transition to readahead.
757  _thread_status = TS_readahead;
758  }
759  }
760  return true;
761 
762  case TS_shutdown:
763  // Time to stop the thread.
764  return false;
765  }
766 
767  return false;
768 }
769 
770 ////////////////////////////////////////////////////////////////////
771 // Function: FfmpegVideoCursor::do_alloc_frame
772 // Access: Private
773 // Description: Allocates a new Buffer object. Assumes the lock is
774 // held.
775 ////////////////////////////////////////////////////////////////////
776 PT(FfmpegVideoCursor::FfmpegBuffer) FfmpegVideoCursor::
777 do_alloc_frame() {
778  PT(Buffer) buffer = make_new_buffer();
779  return (FfmpegBuffer *)buffer.p();
780 }
781 
782 ////////////////////////////////////////////////////////////////////
783 // Function: FfmpegVideoCursor::do_clear_all_frames
784 // Access: Private
785 // Description: Empties the entire readahead_frames queue.
786 // Assumes the lock is held.
787 ////////////////////////////////////////////////////////////////////
788 void FfmpegVideoCursor::
789 do_clear_all_frames() {
790  _readahead_frames.clear();
791 }
792 
793 ////////////////////////////////////////////////////////////////////
794 // Function: FfmpegVideoCursor::fetch_packet
795 // Access: Private
796 // Description: Called within the sub-thread. Fetches a video packet
797 // and stores it in the packet0 buffer. Sets packet_frame
798 // to the packet's timestamp. If a packet could not be
799 // read, the packet is cleared and the packet_frame is
800 // set to the specified default value. Returns true on
801 // failure (such as the end of the video), or false on
802 // success.
803 ////////////////////////////////////////////////////////////////////
804 bool FfmpegVideoCursor::
805 fetch_packet(int default_frame) {
806  if (ffmpeg_global_lock) {
807  ReMutexHolder av_holder(_av_lock);
808  return do_fetch_packet(default_frame);
809  } else {
810  return do_fetch_packet(default_frame);
811  }
812 }
813 
814 ////////////////////////////////////////////////////////////////////
815 // Function: FfmpegVideoCursor::do_fetch_packet
816 // Access: Private
817 // Description: As above, with the ffmpeg global lock held (if
818 // configured on).
819 ////////////////////////////////////////////////////////////////////
820 bool FfmpegVideoCursor::
821 do_fetch_packet(int default_frame) {
822  if (_packet->data) {
823 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
824  av_packet_unref(_packet);
825 #else
826  av_free_packet(_packet);
827 #endif
828  }
829  while (av_read_frame(_format_ctx, _packet) >= 0) {
830  if (_packet->stream_index == _video_index) {
831  _packet_frame = _packet->dts;
832  return false;
833  }
834 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
835  av_packet_unref(_packet);
836 #else
837  av_free_packet(_packet);
838 #endif
839  }
840  _packet->data = 0;
841 
842  if (!_eof_known && default_frame != 0) {
843  _eof_frame = _packet_frame;
844  _eof_known = true;
845  }
846 
847  if (ffmpeg_cat.is_spam()) {
848  if (_eof_known) {
849  ffmpeg_cat.spam()
850  << "end of video at frame " << _eof_frame << "\n";
851  } else {
852  ffmpeg_cat.spam()
853  << "end of video\n";
854  }
855  }
856  _packet_frame = default_frame;
857  return true;
858 }
859 
860 ////////////////////////////////////////////////////////////////////
861 // Function: FfmpegVideoCursor::fetch_frame
862 // Access: Private
863 // Description: Called within the sub-thread. Slides forward until
864 // the indicated frame, then fetches a frame from the
865 // stream and stores it in the frame buffer. Sets
866 // _begin_frame and _end_frame to indicate the extents of
867 // the frame. Sets _frame_ready true to indicate a
868 // frame is now available, or false if it is not (for
869 // instance, because the end of the video was reached).
870 ////////////////////////////////////////////////////////////////////
871 void FfmpegVideoCursor::
872 fetch_frame(int frame) {
873  PStatTimer timer(_fetch_buffer_pcollector);
874 
875  int finished = 0;
876 
877  if (_packet_frame <= frame) {
878  finished = 0;
879 
880  // Get the next packet. The first packet beyond the frame we're
881  // looking for marks the point to stop.
882  while (_packet_frame <= frame) {
883  PStatTimer timer(_seek_pcollector);
884 
885  // Decode the previous packet, and get the next one.
886  decode_frame(finished);
887  _begin_frame = _packet_frame;
888  if (fetch_packet(frame)) {
889  _end_frame = _packet_frame;
890  _frame_ready = false;
891  return;
892  }
893  }
894 
895  } else {
896  // Just get the next frame.
897  finished = 0;
898  while (!finished && _packet->data) {
899  decode_frame(finished);
900  _begin_frame = _packet_frame;
901  fetch_packet(_begin_frame + 1);
902  }
903  }
904 
905  _end_frame = _packet_frame;
906  _frame_ready = true;
907 }
908 
909 ////////////////////////////////////////////////////////////////////
910 // Function: FfmpegVideoCursor::decode_frame
911 // Access: Private
912 // Description: Called within the sub-thread. Decodes the data in
913 // the specified packet into _frame.
914 ////////////////////////////////////////////////////////////////////
915 void FfmpegVideoCursor::
916 decode_frame(int &finished) {
917  if (ffmpeg_global_lock) {
918  ReMutexHolder av_holder(_av_lock);
919  do_decode_frame(finished);
920  } else {
921  do_decode_frame(finished);
922  }
923 }
924 
925 ////////////////////////////////////////////////////////////////////
926 // Function: FfmpegVideoCursor::do_decode_frame
927 // Access: Private
928 // Description: As above, with the ffmpeg global lock held (if
929 // configured on).
930 ////////////////////////////////////////////////////////////////////
931 void FfmpegVideoCursor::
932 do_decode_frame(int &finished) {
933 #if LIBAVCODEC_VERSION_INT < 3414272
934  avcodec_decode_video(_video_ctx, _frame,
935  &finished, _packet->data, _packet->size);
936 #else
937  avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
938 #endif
939 }
940 
941 ////////////////////////////////////////////////////////////////////
942 // Function: FfmpegVideoCursor::seek
943 // Access: Private
944 // Description: Called within the sub-thread. Seeks to a target
945 // location. Afterward, the packet_frame is guaranteed
946 // to be less than or equal to the specified frame.
947 ////////////////////////////////////////////////////////////////////
948 void FfmpegVideoCursor::
949 seek(int frame, bool backward) {
950  PStatTimer timer(_seek_pcollector);
951 
952  if (ffmpeg_support_seek) {
953  if (ffmpeg_global_lock) {
954  ReMutexHolder av_holder(_av_lock);
955  do_seek(frame, backward);
956  } else {
957  do_seek(frame, backward);
958  }
959 
960  } else {
961  // If seeking isn't supported, close-and-reopen.
962  if (backward) {
963  reset_stream();
964  }
965  }
966 }
967 
968 ////////////////////////////////////////////////////////////////////
969 // Function: FfmpegVideoCursor::do_seek
970 // Access: Private
971 // Description: As above, with the ffmpeg global lock held (if
972 // configured on). Also only if ffmpeg-support-seek is
973 // on.
974 ////////////////////////////////////////////////////////////////////
975 void FfmpegVideoCursor::
976 do_seek(int frame, bool backward) {
977  PN_int64 target_ts = (PN_int64)frame;
978  if (target_ts < (PN_int64)(_initial_dts)) {
979  // Attempts to seek before the first packet will fail.
980  target_ts = _initial_dts;
981  }
982  int flags = 0;
983  if (backward) {
984  flags = AVSEEK_FLAG_BACKWARD;
985  }
986 
987  if (av_seek_frame(_format_ctx, _video_index, target_ts, flags) < 0) {
988  if (ffmpeg_cat.is_spam()) {
989  ffmpeg_cat.spam()
990  << "Seek failure.\n";
991  }
992 
993  if (backward) {
994  // Now try to seek forward.
995  reset_stream();
996  seek(frame, false);
997  return;
998  }
999 
1000  // Try a binary search to get a little closer.
1001  if (binary_seek(_initial_dts, frame, frame, 1) < 0) {
1002  if (ffmpeg_cat.is_spam()) {
1003  ffmpeg_cat.spam()
1004  << "Seek double failure.\n";
1005  }
1006  reset_stream();
1007  return;
1008  }
1009  }
1010 
1011  fetch_packet(0);
1012  fetch_frame(-1);
1013 }
1014 
1015 ////////////////////////////////////////////////////////////////////
1016 // Function: FfmpegVideoCursor::binary_seek
1017 // Access: Private
1018 // Description: Casts about within the stream for a reasonably-close
1019 // frame to seek to. We're trying to get as close as
1020 // possible to target_frame.
1021 ////////////////////////////////////////////////////////////////////
1022 int FfmpegVideoCursor::
1023 binary_seek(int min_frame, int max_frame, int target_frame, int num_iterations) {
1024  int try_frame = (min_frame + max_frame) / 2;
1025  if (num_iterations > 5 || try_frame >= max_frame) {
1026  // Success.
1027  return 0;
1028  }
1029 
1030  if (av_seek_frame(_format_ctx, _video_index, try_frame, AVSEEK_FLAG_BACKWARD) < 0) {
1031  // Failure. Try lower.
1032  if (binary_seek(min_frame, try_frame - 1, target_frame, num_iterations + 1) < 0) {
1033  return -1;
1034  }
1035  } else {
1036  // Success. Try higher.
1037  if (binary_seek(try_frame + 1, max_frame, target_frame, num_iterations + 1) < 0) {
1038  return -1;
1039  }
1040  }
1041  return 0;
1042 }
1043 
1044 ////////////////////////////////////////////////////////////////////
1045 // Function: FfmpegVideoCursor::reset_stream
1046 // Access: Private
1047 // Description: Resets the stream to its initial, first-opened state
1048 // by closing and re-opening it.
1049 ////////////////////////////////////////////////////////////////////
1050 void FfmpegVideoCursor::
1051 reset_stream() {
1052  if (ffmpeg_cat.is_spam()) {
1053  ffmpeg_cat.spam()
1054  << "Resetting ffmpeg stream.\n";
1055  }
1056 
1057  close_stream();
1058  if (!open_stream()) {
1059  ffmpeg_cat.error()
1060  << "Stream error, invalidating movie.\n";
1061  cleanup();
1062  return;
1063  }
1064 
1065  fetch_packet(0);
1066  fetch_frame(-1);
1067 }
1068 
1069 ////////////////////////////////////////////////////////////////////
1070 // Function: FfmpegVideoCursor::advance_to_frame
1071 // Access: Private
1072 // Description: Called within the sub-thread. Advance until the
1073 // specified frame is in the export buffer.
1074 ////////////////////////////////////////////////////////////////////
1075 void FfmpegVideoCursor::
1076 advance_to_frame(int frame) {
1077  PStatTimer timer(_fetch_buffer_pcollector);
1078 
1079  if (frame < _begin_frame) {
1080  // Frame is in the past.
1081  if (ffmpeg_cat.is_spam()) {
1082  ffmpeg_cat.spam()
1083  << "Seeking backward to " << frame << " from " << _begin_frame << "\n";
1084  }
1085  seek(frame, true);
1086  if (_begin_frame > frame) {
1087  if (ffmpeg_cat.is_spam()) {
1088  ffmpeg_cat.spam()
1089  << "Ended up at " << _begin_frame << ", not far enough back!\n";
1090  }
1091  reset_stream();
1092  if (ffmpeg_cat.is_spam()) {
1093  ffmpeg_cat.spam()
1094  << "Reseek to 0, got " << _begin_frame << "\n";
1095  }
1096  }
1097  if (frame > _end_frame) {
1098  if (ffmpeg_cat.is_spam()) {
1099  ffmpeg_cat.spam()
1100  << "Now sliding forward to " << frame << " from " << _begin_frame << "\n";
1101  }
1102  fetch_frame(frame);
1103  }
1104 
1105  } else if (frame < _end_frame) {
1106  // Frame is in the present: already have the frame.
1107  if (ffmpeg_cat.is_spam()) {
1108  ffmpeg_cat.spam()
1109  << "Currently have " << frame << " within " << _begin_frame << " .. " << _end_frame << "\n";
1110  }
1111 
1112  } else if (frame < _end_frame + _min_fseek) {
1113  // Frame is in the near future.
1114  if (ffmpeg_cat.is_spam()) {
1115  ffmpeg_cat.spam()
1116  << "Sliding forward to " << frame << " from " << _begin_frame << "\n";
1117  }
1118  fetch_frame(frame);
1119 
1120  } else {
1121  // Frame is in the far future. Seek forward, then read.
1122  // There's a danger here: because keyframes are spaced
1123  // unpredictably, trying to seek forward could actually
1124  // move us backward in the stream! This must be avoided.
1125  // So the rule is, try the seek. If it hurts us by moving
1126  // us backward, we increase the minimum threshold distance
1127  // for forward-seeking in the future.
1128 
1129  if (ffmpeg_cat.is_spam()) {
1130  ffmpeg_cat.spam()
1131  << "Jumping forward to " << frame << " from " << _begin_frame << "\n";
1132  }
1133  int base = _begin_frame;
1134  seek(frame, false);
1135  if (_begin_frame < base) {
1136  _min_fseek += (base - _begin_frame);
1137  if (ffmpeg_cat.is_spam()) {
1138  ffmpeg_cat.spam()
1139  << "Wrong way! Increasing _min_fseek to " << _min_fseek << "\n";
1140  }
1141  }
1142  if (frame > _end_frame) {
1143  if (ffmpeg_cat.is_spam()) {
1144  ffmpeg_cat.spam()
1145  << "Correcting, sliding forward to " << frame << " from " << _begin_frame << "\n";
1146  }
1147  fetch_frame(frame);
1148  }
1149  }
1150 
1151  if (ffmpeg_cat.is_spam()) {
1152  ffmpeg_cat.spam()
1153  << "Wanted " << frame << ", got " << _begin_frame << "\n";
1154  }
1155 }
1156 
1157 ////////////////////////////////////////////////////////////////////
1158 // Function: FfmpegVideoCursor::export_frame
1159 // Access: Private
1160 // Description: Called within the sub-thread. Exports the contents
1161 // of the frame buffer into the indicated target buffer.
1162 ////////////////////////////////////////////////////////////////////
1163 void FfmpegVideoCursor::
1164 export_frame(FfmpegBuffer *buffer) {
1165  PStatTimer timer(_export_frame_pcollector);
1166 
1167  if (!_frame_ready) {
1168  // No frame data ready, just fill with black.
1169  if (ffmpeg_cat.is_spam()) {
1170  ffmpeg_cat.spam()
1171  << "ffmpeg for " << _filename.get_basename()
1172  << ", no frame available.\n";
1173  }
1174  memset(buffer->_block, 0, buffer->_block_size);
1175  return;
1176  }
1177 
1178  _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3);
1179  _frame_out->linesize[0] = _size_x * -3;
1180  buffer->_begin_frame = _begin_frame;
1181  buffer->_end_frame = _end_frame;
1182 
1183  if (ffmpeg_global_lock) {
1184  ReMutexHolder av_holder(_av_lock);
1185 #ifdef HAVE_SWSCALE
1186  nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
1187  sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
1188 #else
1189  img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
1190  (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
1191 #endif
1192  } else {
1193 #ifdef HAVE_SWSCALE
1194  nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
1195  sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
1196 #else
1197  img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
1198  (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
1199 #endif
1200  }
1201 }
1202 
1203 ////////////////////////////////////////////////////////////////////
1204 // Function: FfmpegVideoCursor::register_with_read_factory
1205 // Access: Public, Static
1206 // Description: Tells the BamReader how to create objects of type
1207 // FfmpegVideo.
1208 ////////////////////////////////////////////////////////////////////
1211  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
1212 }
1213 
1214 ////////////////////////////////////////////////////////////////////
1215 // Function: FfmpegVideoCursor::write_datagram
1216 // Access: Public, Virtual
1217 // Description: Writes the contents of this object to the datagram
1218 // for shipping out to a Bam file.
1219 ////////////////////////////////////////////////////////////////////
1222  MovieVideoCursor::write_datagram(manager, dg);
1223 
1224  // No need to write any additional data here--all of it comes
1225  // implicitly from the underlying MovieVideo, which we process in
1226  // finalize().
1227 }
1228 
1229 ////////////////////////////////////////////////////////////////////
1230 // Function: FfmpegVideoCursor::finalize
1231 // Access: Public, Virtual
1232 // Description: Called by the BamReader to perform any final actions
1233 // needed for setting up the object after all objects
1234 // have been read and all pointers have been completed.
1235 ////////////////////////////////////////////////////////////////////
1238  if (_source != (MovieVideo *)NULL) {
1239  FfmpegVideo *video;
1240  DCAST_INTO_V(video, _source);
1241  init_from(video);
1242  }
1243 }
1244 
1245 ////////////////////////////////////////////////////////////////////
1246 // Function: FfmpegVideoCursor::make_from_bam
1247 // Access: Private, Static
1248 // Description: This function is called by the BamReader's factory
1249 // when a new object of type FfmpegVideo is encountered
1250 // in the Bam file. It should create the FfmpegVideo
1251 // and extract its information from the file.
1252 ////////////////////////////////////////////////////////////////////
1253 TypedWritable *FfmpegVideoCursor::
1254 make_from_bam(const FactoryParams &params) {
1255  FfmpegVideoCursor *video = new FfmpegVideoCursor;
1256  DatagramIterator scan;
1257  BamReader *manager;
1258 
1259  parse_params(params, scan, manager);
1260  video->fillin(scan, manager);
1261 
1262  return video;
1263 }
1264 
1265 ////////////////////////////////////////////////////////////////////
1266 // Function: FfmpegVideoCursor::fillin
1267 // Access: Private
1268 // Description: This internal function is called by make_from_bam to
1269 // read in all of the relevant data from the BamFile for
1270 // the new FfmpegVideo.
1271 ////////////////////////////////////////////////////////////////////
1272 void FfmpegVideoCursor::
1273 fillin(DatagramIterator &scan, BamReader *manager) {
1274  MovieVideoCursor::fillin(scan, manager);
1275 
1276  // The MovieVideoCursor gets the underlying MovieVideo pointer. We
1277  // need a finalize callback so we can initialize ourselves once that
1278  // has been read completely.
1279  manager->register_finalize(this);
1280 }
1281 
1282 ////////////////////////////////////////////////////////////////////
1283 // Function: FfmpegVideoCursor::FfmpegBuffer::compare_timestamp
1284 // Access: Published, Virtual
1285 // Description: Used to sort different buffers to ensure they
1286 // correspond to the same source frame, particularly
1287 // important when synchronizing the different pages of a
1288 // multi-page texture.
1289 //
1290 // Returns 0 if the two buffers are of the same frame,
1291 // <0 if this one comes earlier than the other one, and
1292 // >0 if the other one comes earlier.
1293 ////////////////////////////////////////////////////////////////////
1295 compare_timestamp(const Buffer *other) const {
1296  const FfmpegBuffer *fother;
1297  DCAST_INTO_R(fother, other, 0);
1298  if (_end_frame * _video_timebase <= fother->_begin_frame * fother->_video_timebase) {
1299  return -1;
1300  } else if (_begin_frame * _video_timebase >= fother->_end_frame * fother->_video_timebase) {
1301  return 1;
1302  }
1303  return 0;
1304 }
1305 
1306 ////////////////////////////////////////////////////////////////////
1307 // Function: FfmpegVideoCursor::FfmpegBuffer::get_timestamp
1308 // Access: Published, Virtual
1309 // Description: Returns the nearest timestamp value of this
1310 // particular buffer. Ideally,
1311 // MovieVideoCursor::set_time() for this timestamp would
1312 // return this buffer again. This need be defined only
1313 // if compare_timestamp() is also defined.
1314 ////////////////////////////////////////////////////////////////////
1316 get_timestamp() const {
1317  int mid_frame = (_begin_frame + _end_frame - 1) / 2;
1318  return mid_frame * _video_timebase;
1319 }
bool is_thread_started() const
Returns true if the thread has been started, false if not.
virtual bool set_time(double timestamp, int loop_count)
See MovieVideoCursor::set_time().
void start_thread()
Explicitly starts the ffmpeg decoding thread after it has been stopped by a call to stop_thread()...
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
static void register_with_read_factory()
Tells the BamReader how to create objects of type FfmpegVideo.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:29
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
int get_max_readahead_frames() const
Returns the maximum number of frames that a sub-thread will attempt to read ahead of the current fram...
Definition: buffer.h:26
A lightweight class that represents a single element that may be timed and/or counted via stats...
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
virtual int compare_timestamp(const Buffer *other) const
Used to sort different buffers to ensure they correspond to the same source frame, particularly important when synchronizing the different pages of a multi-page texture.
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:886
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:27
A generic thread type that allows calling a C-style thread function without having to subclass...
Definition: genericThread.h:26
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
void set_max_readahead_frames(int max_readahead_frames)
Specifies the maximum number of frames that a sub-thread will attempt to read ahead of the current fr...
virtual double get_timestamp() const
Returns the nearest timestamp value of this particular buffer.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
A MovieVideo is actually any source that provides a sequence of video frames.
Definition: movieVideo.h:42
A class to retrieve the individual data elements previously stored in a Datagram. ...
ThreadPriority get_thread_priority() const
Returns the current thread priority of the thread that decodes the ffmpeg video stream (if max_readah...
void stop_thread()
Explicitly stops the ffmpeg decoding thread.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
A reentrant mutex.
Definition: reMutex.h:36
void set_thread_priority(ThreadPriority thread_priority)
Changes the thread priority of the thread that decodes the ffmpeg video stream (if max_readahead_fram...