From b4e931322f4c6b17159ae4ffdacfa9927d4ced53 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 15 Nov 2011 22:34:43 +0000 Subject: [PATCH] use integer frame numbers instead of floating-point time values in ffmpeg movies, for robustness --- panda/src/grutil/movieTexture.cxx | 31 +- panda/src/movies/ffmpegVideoCursor.I | 13 + panda/src/movies/ffmpegVideoCursor.cxx | 435 ++++++++++++------- panda/src/movies/ffmpegVideoCursor.h | 44 +- panda/src/movies/inkblotVideoCursor.cxx | 41 +- panda/src/movies/inkblotVideoCursor.h | 6 +- panda/src/movies/movieVideoCursor.I | 50 +-- panda/src/movies/movieVideoCursor.cxx | 145 ++----- panda/src/movies/movieVideoCursor.h | 28 +- panda/src/vision/webcamVideoCursorOpenCV.cxx | 8 +- panda/src/vision/webcamVideoCursorOpenCV.h | 2 +- panda/src/vision/webcamVideoCursorV4L.cxx | 8 +- panda/src/vision/webcamVideoCursorV4L.h | 2 +- 13 files changed, 444 insertions(+), 369 deletions(-) diff --git a/panda/src/grutil/movieTexture.cxx b/panda/src/grutil/movieTexture.cxx index 232df79599..0afc4a4fee 100644 --- a/panda/src/grutil/movieTexture.cxx +++ b/panda/src/grutil/movieTexture.cxx @@ -351,42 +351,33 @@ cull_callback(CullTraverser *, const CullTraverserData &) const { CDReader cdata(_cycler); double offset; + int true_loop_count = 1; if (cdata->_synchronize != 0) { offset = cdata->_synchronize->get_time(); } else { // Calculate the cursor position modulo the length of the movie. double now = ClockObject::get_global_clock()->get_frame_time(); - double clock = cdata->_clock; + offset = cdata->_clock; if (cdata->_playing) { - clock += now * cdata->_play_rate; - } - int true_loop_count = cdata->_loops_total; - if (true_loop_count <= 0) { - true_loop_count = 1000000000; - } - if (clock >= cdata->_video_length * true_loop_count) { - offset = cdata->_video_length; - } else { - offset = fmod(clock, cdata->_video_length); + offset += now * cdata->_play_rate; } + true_loop_count = cdata->_loops_total; } for (int i=0; i<((int)(cdata->_pages.size())); i++) { MovieVideoCursor *color = cdata->_pages[i]._color; MovieVideoCursor *alpha = cdata->_pages[i]._alpha; if (color && alpha) { - if ((offset >= color->next_start())|| - ((offset < color->last_start()) && (color->can_seek()))) { - color->fetch_into_texture_rgb(offset, (MovieTexture*)this, i); + if (color->set_time(offset, true_loop_count)) { + color->fetch_into_texture_rgb((MovieTexture*)this, i); } - if ((offset >= alpha->next_start())|| - ((offset < alpha->last_start()) && (alpha->can_seek()))) { - alpha->fetch_into_texture_alpha(offset, (MovieTexture*)this, i, cdata_tex->_alpha_file_channel); + if (alpha->set_time(offset, true_loop_count)) { + alpha->fetch_into_texture_alpha((MovieTexture*)this, i, cdata_tex->_alpha_file_channel); } } else if (color) { - if ((offset >= color->next_start())|| - ((offset < color->last_start()) && (color->can_seek()))) { - color->fetch_into_texture(offset, (MovieTexture*)this, i); + bool result = color->set_time(offset, true_loop_count); + if (result) { + color->fetch_into_texture((MovieTexture*)this, i); } } } diff --git a/panda/src/movies/ffmpegVideoCursor.I b/panda/src/movies/ffmpegVideoCursor.I index 960299b1a5..b25750b222 100644 --- a/panda/src/movies/ffmpegVideoCursor.I +++ b/panda/src/movies/ffmpegVideoCursor.I @@ -12,3 +12,16 @@ // //////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::FfmpegBuffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FfmpegVideoCursor::FfmpegBuffer:: +FfmpegBuffer(size_t block_size) : + Buffer(block_size), + _begin_frame(-1), + _end_frame(0) +{ +} diff --git a/panda/src/movies/ffmpegVideoCursor.cxx b/panda/src/movies/ffmpegVideoCursor.cxx index 25a9a83198..ec19a40dad 100644 --- a/panda/src/movies/ffmpegVideoCursor.cxx +++ b/panda/src/movies/ffmpegVideoCursor.cxx @@ -49,7 +49,7 @@ FfmpegVideoCursor() : _lock("FfmpegVideoCursor::_lock"), _action_cvar(_lock), _thread_status(TS_stopped), - _seek_time(0.0), + _seek_frame(0), _packet0(NULL), _packet1(NULL), _format_ctx(NULL), @@ -58,7 +58,7 @@ FfmpegVideoCursor() : _video_index(-1), _frame(NULL), _frame_out(NULL), - _min_fseek(3.0) + _eof_known(false) { } @@ -103,10 +103,14 @@ init_from(FfmpegVideo *source) { memset(_packet0, 0, sizeof(AVPacket)); memset(_packet1, 0, sizeof(AVPacket)); - fetch_packet(0.0); + fetch_packet(0); _initial_dts = _packet0->dts; fetch_frame(-1); + _current_frame = -1; + _eof_known = false; + _eof_frame = 0; + #ifdef HAVE_THREADS set_max_readahead_frames(ffmpeg_max_readahead_frames); #endif // HAVE_THREADS @@ -124,7 +128,7 @@ FfmpegVideoCursor(FfmpegVideo *src) : _lock("FfmpegVideoCursor::_lock"), _action_cvar(_lock), _thread_status(TS_stopped), - _seek_time(0.0), + _seek_frame(0), _packet0(NULL), _packet1(NULL), _format_ctx(NULL), @@ -133,7 +137,7 @@ FfmpegVideoCursor(FfmpegVideo *src) : _video_index(-1), _frame(NULL), _frame_out(NULL), - _min_fseek(3.0) + _eof_known(false) { init_from(src); } @@ -299,14 +303,7 @@ stop_thread() { // else starts the thread up again. MutexHolder holder(_lock); - Buffers::iterator bi; - for (bi = _readahead_frames.begin(); bi != _readahead_frames.end(); ++bi) { - internal_free_buffer(*bi); - } _readahead_frames.clear(); - for (bi = _recycled_frames.begin(); bi != _recycled_frames.end(); ++bi) { - internal_free_buffer(*bi); - } _recycled_frames.clear(); } @@ -322,13 +319,52 @@ is_thread_started() const { return (_thread_status != TS_stopped); } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::set_time +// Access: Published, Virtual +// Description: See MovieVideoCursor::set_time(). +//////////////////////////////////////////////////////////////////// +bool FfmpegVideoCursor:: +set_time(double time, int loop_count) { + int frame = (int)(time / _video_timebase + 0.5); + + if (_eof_known) { + if (loop_count == 0) { + frame = frame % _eof_frame; + } else { + int last_frame = _eof_frame * loop_count; + if (frame < last_frame) { + frame = frame % _eof_frame; + } else { + frame = _eof_frame - 1; + } + } + } + + if (ffmpeg_cat.is_spam() /* && frame != _current_frame*/) { + ffmpeg_cat.spam() + << "set_time(" << time << "): " << frame << ", loop_count = " << loop_count << "\n"; + } + + _current_frame = frame; + if (_current_frame_buffer != NULL) { + // If we've previously returned a frame, don't bother asking for a + // next one if that frame is still valid. + return (_current_frame >= _current_frame_buffer->_end_frame || + _current_frame < _current_frame_buffer->_begin_frame); + } + + // If our last request didn't return a frame, try again. + return true; +} + //////////////////////////////////////////////////////////////////// // Function: FfmpegVideoCursor::fetch_buffer // Access: Public, Virtual // Description: See MovieVideoCursor::fetch_buffer. //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *FfmpegVideoCursor:: -fetch_buffer(double time) { +PT(MovieVideoCursor::Buffer) FfmpegVideoCursor:: +fetch_buffer() { MutexHolder holder(_lock); // If there was an error at any point, just return NULL. @@ -336,10 +372,10 @@ fetch_buffer(double time) { return NULL; } - Buffer *frame = NULL; + PT(FfmpegBuffer) frame; if (_thread_status == TS_stopped) { // Non-threaded case. Just get the next frame directly. - fetch_time(time); + advance_to_frame(_current_frame); if (_frame_ready) { frame = do_alloc_frame(); export_frame(frame); @@ -352,46 +388,70 @@ fetch_buffer(double time) { frame = _readahead_frames.front(); _readahead_frames.pop_front(); _action_cvar.notify(); - while (frame->_end_time < time && !_readahead_frames.empty()) { + while (frame->_end_frame < _current_frame && !_readahead_frames.empty()) { // This frame is too old. Discard it. if (ffmpeg_cat.is_debug()) { ffmpeg_cat.debug() << "ffmpeg for " << _filename.get_basename() - << " at time " << time << ", discarding frame at " - << frame->_begin_time << "\n"; + << " at frame " << _current_frame << ", discarding frame at " + << frame->_begin_frame << "\n"; } do_recycle_frame(frame); frame = _readahead_frames.front(); _readahead_frames.pop_front(); } - if (frame->_begin_time > time) { + if (frame->_begin_frame > _current_frame) { // This frame is too new. Empty all remaining frames and seek // backwards. + if (ffmpeg_cat.is_debug()) { + ffmpeg_cat.debug() + << "ffmpeg for " << _filename.get_basename() + << " at frame " << _current_frame << ", encountered too-new frame at " + << frame->_begin_frame << "\n"; + } do_recycle_all_frames(); if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) { _thread_status = TS_seek; - _seek_time = time; + _seek_frame = _current_frame; _action_cvar.notify(); } } } - if (frame == NULL || frame->_end_time < time) { + if (frame == NULL || frame->_end_frame < _current_frame) { // No frame available, or the frame is too old. Seek. if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) { _thread_status = TS_seek; - _seek_time = time; + _seek_frame = _current_frame; _action_cvar.notify(); } } } - if (frame != NULL && (frame->_end_time < time || frame->_begin_time > time)) { + if (frame != NULL && (frame->_end_frame < _current_frame || frame->_begin_frame > _current_frame)) { // The frame is too old or too new. Just recycle it. do_recycle_frame(frame); frame = NULL; } - - return frame; + + if (frame != NULL) { + if (_current_frame_buffer != NULL) { + do_recycle_frame(_current_frame_buffer); + } + _current_frame_buffer = frame; + if (ffmpeg_cat.is_debug()) { + ffmpeg_cat.debug() + << "ffmpeg for " << _filename.get_basename() + << " at frame " << _current_frame << ", returning frame at " + << frame->_begin_frame << "\n"; + } + } else { + if (ffmpeg_cat.is_debug()) { + ffmpeg_cat.debug() + << "ffmpeg for " << _filename.get_basename() + << " at frame " << _current_frame << ", returning NULL\n"; + } + } + return frame.p(); } //////////////////////////////////////////////////////////////////// @@ -403,8 +463,18 @@ fetch_buffer(double time) { //////////////////////////////////////////////////////////////////// void FfmpegVideoCursor:: release_buffer(Buffer *buffer) { - MutexHolder holder(_lock); - do_recycle_frame(buffer); +} + +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::make_new_buffer +// Access: Protected, Virtual +// Description: May be called by a derived class to allocate a new +// Buffer object. +//////////////////////////////////////////////////////////////////// +PT(MovieVideoCursor::Buffer) FfmpegVideoCursor:: +make_new_buffer() { + PT(FfmpegBuffer) frame = new FfmpegBuffer(size_x() * size_y() * get_num_components()); + return frame.p(); } //////////////////////////////////////////////////////////////////// @@ -424,7 +494,7 @@ open_stream() { if (!_source->get_subfile_info().is_empty()) { // Read a subfile. if (!_ffvfile.open_subfile(_source->get_subfile_info())) { - movies_cat.info() + ffmpeg_cat.info() << "Couldn't open " << _source->get_subfile_info() << "\n"; close_stream(); return false; @@ -433,7 +503,7 @@ open_stream() { } else { // Read a filename. if (!_ffvfile.open_vfs(_filename)) { - movies_cat.info() + ffmpeg_cat.info() << "Couldn't open " << _filename << "\n"; close_stream(); return false; @@ -445,7 +515,7 @@ open_stream() { nassertr(_format_ctx != NULL, false); if (av_find_stream_info(_format_ctx) < 0) { - movies_cat.info() + ffmpeg_cat.info() << "Couldn't find stream info\n"; close_stream(); return false; @@ -458,11 +528,12 @@ open_stream() { _video_index = i; _video_ctx = _format_ctx->streams[i]->codec; _video_timebase = av_q2d(_format_ctx->streams[i]->time_base); + _min_fseek = (int)(3.0 / _video_timebase); } } if (_video_ctx == NULL) { - movies_cat.info() + ffmpeg_cat.info() << "Couldn't find video_ctx\n"; close_stream(); return false; @@ -470,13 +541,13 @@ open_stream() { AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); if (pVideoCodec == NULL) { - movies_cat.info() + ffmpeg_cat.info() << "Couldn't find codec\n"; close_stream(); return false; } if (avcodec_open(_video_ctx, pVideoCodec) < 0) { - movies_cat.info() + ffmpeg_cat.info() << "Couldn't open codec\n"; close_stream(); return false; @@ -485,10 +556,9 @@ open_stream() { _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; + _length = (double)_format_ctx->duration / (double)AV_TIME_BASE; _can_seek = true; _can_seek_fast = true; - _eof_reached = false; return true; } @@ -581,8 +651,8 @@ st_thread_main(void *self) { void FfmpegVideoCursor:: thread_main() { MutexHolder holder(_lock); - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "ffmpeg thread for " << _filename.get_basename() << " starting.\n"; } @@ -600,8 +670,8 @@ thread_main() { } _thread_status = TS_stopped; - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "ffmpeg thread for " << _filename.get_basename() << " stopped.\n"; } } @@ -630,7 +700,7 @@ do_poll() { case TS_readahead: if ((int)_readahead_frames.size() < _max_readahead_frames) { // Time to read the next frame. - Buffer *frame = do_alloc_frame(); + PT(FfmpegBuffer) frame = do_alloc_frame(); nassertr(frame != NULL, false); _lock.release(); fetch_frame(-1); @@ -650,14 +720,14 @@ do_poll() { return false; case TS_seek: - // Seek to a specific time. + // Seek to a specific frame. { - double seek_time = _seek_time; + int seek_frame = _seek_frame; _thread_status = TS_seeking; - Buffer *frame = do_alloc_frame(); + PT(FfmpegBuffer) frame = do_alloc_frame(); nassertr(frame != NULL, false); _lock.release(); - fetch_time(seek_time); + advance_to_frame(seek_frame); if (_frame_ready) { export_frame(frame); _lock.acquire(); @@ -691,14 +761,15 @@ do_poll() { // previously-recycled object. Assumes the lock is // held. //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *FfmpegVideoCursor:: +PT(FfmpegVideoCursor::FfmpegBuffer) FfmpegVideoCursor:: do_alloc_frame() { if (!_recycled_frames.empty()) { - Buffer *frame = _recycled_frames.front(); + PT(FfmpegBuffer) frame = _recycled_frames.front(); _recycled_frames.pop_front(); return frame; } - return internal_alloc_buffer(); + PT(Buffer) buffer = make_new_buffer(); + return (FfmpegBuffer *)buffer.p(); } //////////////////////////////////////////////////////////////////// @@ -708,7 +779,7 @@ do_alloc_frame() { // future reuse. Assumes the lock is held. //////////////////////////////////////////////////////////////////// void FfmpegVideoCursor:: -do_recycle_frame(Buffer *frame) { +do_recycle_frame(FfmpegBuffer *frame) { _recycled_frames.push_back(frame); } @@ -721,12 +792,12 @@ do_recycle_frame(Buffer *frame) { void FfmpegVideoCursor:: do_recycle_all_frames() { while (!_readahead_frames.empty()) { - Buffer *frame = _readahead_frames.front(); + PT(FfmpegBuffer) frame = _readahead_frames.front(); _readahead_frames.pop_front(); - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "ffmpeg for " << _filename.get_basename() - << " recycling frame at " << frame->_begin_time << "\n"; + << " recycling frame at " << frame->_begin_frame << "\n"; } _recycled_frames.push_back(frame); } @@ -736,32 +807,42 @@ do_recycle_all_frames() { // Function: FfmpegVideoCursor::fetch_packet // Access: Private // Description: Called within the sub-thread. Fetches a video packet -// and stores it in the packet0 buffer. Sets packet_time +// and stores it in the packet0 buffer. Sets packet_frame // to the packet's timestamp. If a packet could not be -// read, the packet is cleared and the packet_time is +// read, the packet is cleared and the packet_frame is // set to the specified default value. Returns true on // failure (such as the end of the video), or false on // success. //////////////////////////////////////////////////////////////////// bool FfmpegVideoCursor:: -fetch_packet(double default_time) { +fetch_packet(int default_frame) { if (_packet0->data) { av_free_packet(_packet0); } while (av_read_frame(_format_ctx, _packet0) >= 0) { if (_packet0->stream_index == _video_index) { - _packet_time = _packet0->dts * _video_timebase; + _packet_frame = _packet0->dts; return false; } av_free_packet(_packet0); } _packet0->data = 0; - _packet_time = default_time; - _eof_reached = false; - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "end of video\n"; + + if (!_eof_known && default_frame != 0) { + _eof_frame = _packet_frame; + _eof_known = true; } + + if (ffmpeg_cat.is_spam()) { + if (_eof_known) { + ffmpeg_cat.spam() + << "end of video at frame " << _eof_frame << "\n"; + } else { + ffmpeg_cat.spam() + << "end of video\n"; + } + } + _packet_frame = default_frame; return true; } @@ -781,33 +862,35 @@ flip_packets() { // Function: FfmpegVideoCursor::fetch_frame // Access: Private // Description: Called within the sub-thread. Slides forward until -// the indicated time, then fetches a frame from the +// the indicated frame, then fetches a frame from the // stream and stores it in the frame buffer. Sets -// _begin_time and _end_time to indicate the extents of -// the frame. Returns true if the end of the video is -// reached. +// _begin_frame and _end_frame to indicate the extents of +// the frame. Sets _frame_ready true to indicate a +// frame is now available, or false if it is not (for +// instance, because the end of the video was reached). //////////////////////////////////////////////////////////////////// -bool FfmpegVideoCursor:: -fetch_frame(double time) { +void FfmpegVideoCursor:: +fetch_frame(int frame) { static PStatCollector fetch_buffer_pcollector("*:FFMPEG Video Decoding:Fetch"); PStatTimer timer(fetch_buffer_pcollector); int finished = 0; - if (_packet_time <= time) { + if (_packet_frame <= frame) { _video_ctx->skip_frame = AVDISCARD_BIDIR; // Put the current packet aside in case we discover it's the // packet to keep. flip_packets(); - // Get the next packet. The first packet beyond the time we're + // Get the next packet. The first packet beyond the frame we're // looking for marks the point to stop. - _begin_time = _packet_time; - if (fetch_packet(time)) { + _begin_frame = _packet_frame; + if (fetch_packet(frame)) { + _end_frame = _packet_frame; _frame_ready = false; - return true; + return; } - while (_packet_time <= time) { + while (_packet_frame <= frame) { static PStatCollector seek_pcollector("*:FFMPEG Video Decoding:Seek"); PStatTimer timer(seek_pcollector); @@ -819,10 +902,11 @@ fetch_frame(double time) { avcodec_decode_video2(_video_ctx, _frame, &finished, _packet1); #endif flip_packets(); - _begin_time = _packet_time; - if (fetch_packet(time)) { + _begin_frame = _packet_frame; + if (fetch_packet(frame)) { + _end_frame = _packet_frame; _frame_ready = false; - return true; + return; } } _video_ctx->skip_frame = AVDISCARD_DEFAULT; @@ -839,7 +923,6 @@ fetch_frame(double time) { } else { // Just get the next frame. - _begin_time = _packet_time; finished = 0; while (!finished && _packet0->data) { #if LIBAVCODEC_VERSION_INT < 3414272 @@ -848,28 +931,28 @@ fetch_frame(double time) { #else avcodec_decode_video2(_video_ctx, _frame, &finished, _packet0); #endif - fetch_packet(_begin_time + 1.0); + _begin_frame = _packet_frame; + fetch_packet(_begin_frame + 1); } } - _end_time = _packet_time; + _end_frame = _packet_frame; _frame_ready = true; - return false; } //////////////////////////////////////////////////////////////////// // Function: FfmpegVideoCursor::seek // Access: Private // Description: Called within the sub-thread. Seeks to a target -// location. Afterward, the packet_time is guaranteed -// to be less than or equal to the specified time. +// location. Afterward, the packet_frame is guaranteed +// to be less than or equal to the specified frame. //////////////////////////////////////////////////////////////////// void FfmpegVideoCursor:: -seek(double t, bool backward) { +seek(int frame, bool backward) { static PStatCollector seek_pcollector("*:FFMPEG Video Decoding:Seek"); PStatTimer timer(seek_pcollector); - PN_int64 target_ts = (PN_int64)(t / _video_timebase); + 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; @@ -880,15 +963,26 @@ seek(double t, bool backward) { //reset_stream(); } - if (av_seek_frame(_format_ctx, _video_index, target_ts, flags) < 0) { - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "Seek failure.\n"; } - reset_stream(); - - av_seek_frame(_format_ctx, _video_index, target_ts, 0); + + if (backward) { + // Now try to seek forward. + reset_stream(); + return seek(frame, false); + } + + // 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(); + } } { @@ -918,6 +1012,35 @@ seek(double t, bool backward) { fetch_frame(-1); } +//////////////////////////////////////////////////////////////////// +// Function: FfmpegVideoCursor::binary_seek +// Access: Private +// Description: Casts about within the stream for a reasonably-close +// frame to seek to. We're trying to get as close as +// possible to target_frame. +//////////////////////////////////////////////////////////////////// +int FfmpegVideoCursor:: +binary_seek(int min_frame, int max_frame, int target_frame, int num_iterations) { + int try_frame = (min_frame + max_frame) / 2; + if (num_iterations > 5 || try_frame >= max_frame) { + // Success. + return 0; + } + + if (av_seek_frame(_format_ctx, _video_index, try_frame, AVSEEK_FLAG_BACKWARD) < 0) { + // Failure. Try lower. + if (binary_seek(min_frame, try_frame - 1, target_frame, num_iterations + 1) < 0) { + return -1; + } + } else { + // Success. Try higher. + if (binary_seek(try_frame + 1, max_frame, target_frame, num_iterations + 1) < 0) { + return -1; + } + } + return 0; +} + //////////////////////////////////////////////////////////////////// // Function: FfmpegVideoCursor::reset_stream // Access: Private @@ -926,8 +1049,8 @@ seek(double t, bool backward) { //////////////////////////////////////////////////////////////////// void FfmpegVideoCursor:: reset_stream() { - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "Resetting ffmpeg stream.\n"; } @@ -939,77 +1062,65 @@ reset_stream() { return; } - fetch_packet(0.0); + fetch_packet(0); _initial_dts = _packet0->dts; fetch_frame(-1); } //////////////////////////////////////////////////////////////////// -// Function: FfmpegVideoCursor::fetch_time +// Function: FfmpegVideoCursor::advance_to_frame // Access: Private // Description: Called within the sub-thread. Advance until the -// specified time is in the export buffer. +// specified frame is in the export buffer. //////////////////////////////////////////////////////////////////// void FfmpegVideoCursor:: -fetch_time(double time) { +advance_to_frame(int frame) { static PStatCollector fetch_buffer_pcollector("*:FFMPEG Video Decoding:Fetch"); PStatTimer timer(fetch_buffer_pcollector); - if (time < _begin_time) { - // Time is in the past. - if (_eof_reached) { - // Go ahead and reset the video when we back up after having - // reached the end. This avoids potential probelsm with - // unseekable streams. - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Resetting to " << time << " after eof\n"; + if (frame < _begin_frame) { + // Frame is in the past. + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Seeking backward to " << frame << " from " << _begin_frame << "\n"; + } + seek(frame, true); + if (_begin_frame > frame) { + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Ended up at " << _begin_frame << ", not far enough back!\n"; } reset_stream(); - - } else { - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Seeking backward to " << time << " from " << _begin_time << "\n"; - } - seek(time, true); - if (_begin_time > time) { - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Ended up at " << _begin_time << ", not far enough back!\n"; - } - reset_stream(); - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Reseek to 0, got " << _begin_time << "\n"; - } + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Reseek to 0, got " << _begin_frame << "\n"; } } - if (time < _begin_time) { - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Now sliding forward to " << time << " from " << _begin_time << "\n"; + if (frame > _end_frame) { + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Now sliding forward to " << frame << " from " << _begin_frame << "\n"; } - fetch_frame(time); + fetch_frame(frame); } - } else if (time < _end_time) { - // Time is in the present: already have the frame. - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Currently have " << time << "\n"; + } else if (frame < _end_frame) { + // Frame is in the present: already have the frame. + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Currently have " << frame << " within " << _begin_frame << " .. " << _end_frame << "\n"; } - } else if (time < _end_time + _min_fseek) { - // Time is in the near future. - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Sliding forward to " << time << " from " << _begin_time << "\n"; + } else if (frame < _end_frame + _min_fseek) { + // Frame is in the near future. + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Sliding forward to " << frame << " from " << _begin_frame << "\n"; } - fetch_frame(time); + fetch_frame(frame); } else { - // Time is in the far future. Seek forward, then read. + // Frame is in the far future. Seek forward, then read. // There's a danger here: because keyframes are spaced // unpredictably, trying to seek forward could actually // move us backward in the stream! This must be avoided. @@ -1017,31 +1128,31 @@ fetch_time(double time) { // us backward, we increase the minimum threshold distance // for forward-seeking in the future. - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Jumping forward to " << time << " from " << _begin_time << "\n"; + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Jumping forward to " << frame << " from " << _begin_frame << "\n"; } - double base = _begin_time; - seek(time, false); - if (_begin_time < base) { - _min_fseek += (base - _begin_time); - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + int base = _begin_frame; + seek(frame, false); + if (_begin_frame < base) { + _min_fseek += (base - _begin_frame); + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "Wrong way! Increasing _min_fseek to " << _min_fseek << "\n"; } } - if (time < _begin_time) { - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Correcting, sliding forward to " << time << " from " << _begin_time << "\n"; + if (frame > _end_frame) { + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Correcting, sliding forward to " << frame << " from " << _begin_frame << "\n"; } - fetch_frame(time); + fetch_frame(frame); } } - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() - << "Wanted " << time << ", got " << _begin_time << "\n"; + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() + << "Wanted " << frame << ", got " << _begin_frame << "\n"; } } @@ -1052,14 +1163,14 @@ fetch_time(double time) { // of the frame buffer into the indicated target buffer. //////////////////////////////////////////////////////////////////// void FfmpegVideoCursor:: -export_frame(MovieVideoCursor::Buffer *buffer) { +export_frame(FfmpegBuffer *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. - if (ffmpeg_cat.is_debug()) { - ffmpeg_cat.debug() + if (ffmpeg_cat.is_spam()) { + ffmpeg_cat.spam() << "ffmpeg for " << _filename.get_basename() << ", no frame available.\n"; } @@ -1069,8 +1180,8 @@ export_frame(MovieVideoCursor::Buffer *buffer) { _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; + buffer->_begin_frame = _begin_frame; + buffer->_end_frame = _end_frame; #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); diff --git a/panda/src/movies/ffmpegVideoCursor.h b/panda/src/movies/ffmpegVideoCursor.h index 52ec9d4dad..be051cdfdb 100644 --- a/panda/src/movies/ffmpegVideoCursor.h +++ b/panda/src/movies/ffmpegVideoCursor.h @@ -61,9 +61,20 @@ PUBLISHED: bool is_thread_started() const; public: - virtual Buffer *fetch_buffer(double time); + virtual bool set_time(double time, int loop_count); + virtual PT(Buffer) fetch_buffer(); virtual void release_buffer(Buffer *buffer); +protected: + class FfmpegBuffer : public Buffer { + public: + INLINE FfmpegBuffer(size_t block_size); + int _begin_frame; + int _end_frame; + }; + + virtual PT(Buffer) make_new_buffer(); + private: bool open_stream(); void close_stream(); @@ -85,7 +96,7 @@ private: // Condition: the thread has something to do. ConditionVar _action_cvar; - typedef pdeque Buffers; + typedef pdeque Buffers; Buffers _readahead_frames; Buffers _recycled_frames; enum ThreadStatus { @@ -97,7 +108,10 @@ private: TS_shutdown, }; ThreadStatus _thread_status; - double _seek_time; + int _seek_frame; + + int _current_frame; + PT(FfmpegBuffer) _current_frame_buffer; private: // The following functions will be called in the sub-thread. @@ -105,21 +119,22 @@ private: void thread_main(); bool do_poll(); - Buffer *do_alloc_frame(); - void do_recycle_frame(Buffer *frame); + PT(FfmpegBuffer) do_alloc_frame(); + void do_recycle_frame(FfmpegBuffer *frame); void do_recycle_all_frames(); - bool fetch_packet(double default_time); + bool fetch_packet(int default_frame); void flip_packets(); - bool fetch_frame(double time); - void seek(double t, bool backward); - void fetch_time(double time); + void fetch_frame(int frame); + 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); void reset_stream(); - void export_frame(Buffer *buffer); + void export_frame(FfmpegBuffer *buffer); // The following data members will be accessed by the sub-thread. AVPacket *_packet0, *_packet1; - double _packet_time; + int _packet_frame; AVFormatContext *_format_ctx; AVCodecContext *_video_ctx; SwsContext *_convert_ctx; @@ -131,10 +146,11 @@ private: AVFrame *_frame_out; int _initial_dts; double _min_fseek; - double _begin_time; - double _end_time; + int _begin_frame; + int _end_frame; bool _frame_ready; - bool _eof_reached; + bool _eof_known; + int _eof_frame; public: static void register_with_read_factory(); diff --git a/panda/src/movies/inkblotVideoCursor.cxx b/panda/src/movies/inkblotVideoCursor.cxx index f9e5fa6716..8e5ffea333 100644 --- a/panda/src/movies/inkblotVideoCursor.cxx +++ b/panda/src/movies/inkblotVideoCursor.cxx @@ -67,7 +67,8 @@ InkblotVideoCursor(InkblotVideo *src) : memset(_cells2, 255, padx * pady); _can_seek = true; _can_seek_fast = false; - _frames_read = 0; + _current_frame = 0; + _last_frame = -1; } //////////////////////////////////////////////////////////////////// @@ -81,33 +82,43 @@ InkblotVideoCursor:: delete[] _cells2; } +//////////////////////////////////////////////////////////////////// +// Function: InkblotVideoCursor::set_time +// Access: Published, Virtual +// Description: See MovieVideoCursor::set_time(). +//////////////////////////////////////////////////////////////////// +bool InkblotVideoCursor:: +set_time(double time, int loop_count) { + int frame = (int)(time / _fps); + if (frame == _current_frame) { + return false; + } + + _current_frame = frame; + return true; +} + //////////////////////////////////////////////////////////////////// // Function: InkblotVideoCursor::fetch_buffer // Access: Published, Virtual // Description: See MovieVideoCursor::fetch_buffer. //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *InkblotVideoCursor:: -fetch_buffer(double time) { - Buffer *buffer = get_standard_buffer(); +PT(MovieVideoCursor::Buffer) InkblotVideoCursor:: +fetch_buffer() { + PT(Buffer) buffer = get_standard_buffer(); int padx = size_x() + 2; int pady = size_y() + 2; - if (time < _next_start) { + if (_current_frame < _last_frame) { // Rewind to beginning. memset(_cells, 255, padx * pady); memset(_cells2, 255, padx * pady); - _last_start = -1.0; - _next_start = 0.0; - _frames_read = 0; + _last_frame = 0; } - nassertr(time >= _next_start, NULL); - - while (_next_start <= time) { - _last_start = (_frames_read * 1.0) / _fps; - _frames_read += 1; - _next_start = (_frames_read * 1.0) / _fps; + while (_last_frame <= _current_frame) { + ++_last_frame; for (int y=1; y_begin_time = _last_start; - buffer->_end_time = _next_start; return buffer; } diff --git a/panda/src/movies/inkblotVideoCursor.h b/panda/src/movies/inkblotVideoCursor.h index 94584eeaf9..9680ab532f 100644 --- a/panda/src/movies/inkblotVideoCursor.h +++ b/panda/src/movies/inkblotVideoCursor.h @@ -32,13 +32,15 @@ class EXPCL_PANDA_MOVIES InkblotVideoCursor : public MovieVideoCursor { virtual ~InkblotVideoCursor(); public: - virtual Buffer *fetch_buffer(double time); + virtual bool set_time(double time, int loop_count); + virtual PT(Buffer) fetch_buffer(); protected: + int _current_frame; + int _last_frame; unsigned char *_cells; unsigned char *_cells2; int _fps; - int _frames_read; public: static TypeHandle get_class_type() { diff --git a/panda/src/movies/movieVideoCursor.I b/panda/src/movies/movieVideoCursor.I index 6267316fb3..a43ed59488 100644 --- a/panda/src/movies/movieVideoCursor.I +++ b/panda/src/movies/movieVideoCursor.I @@ -130,34 +130,6 @@ aborted() const { return _aborted; } -//////////////////////////////////////////////////////////////////// -// Function: MovieVideoCursor::last_start -// Access: Published -// Description: Returns the start time of the last frame you read. -// -// MovieVideoCursor streams have variable frame rates. Each -// frame will specify how long it is to be displayed. -// These lengths may not be equal from frame to frame. -//////////////////////////////////////////////////////////////////// -INLINE double MovieVideoCursor:: -last_start() const { - return _last_start; -} - -//////////////////////////////////////////////////////////////////// -// Function: MovieVideoCursor::next_start -// Access: Published -// Description: Returns the start time of the next frame you can read. -// -// MovieVideoCursor streams have variable frame rates. Each -// frame will specify how long it is to be displayed. -// These lengths may not be equal from frame to frame. -//////////////////////////////////////////////////////////////////// -INLINE double MovieVideoCursor:: -next_start() const { - return _next_start; -} - //////////////////////////////////////////////////////////////////// // Function: MovieVideoCursor::streaming // Access: Published @@ -191,3 +163,25 @@ ready() const { return _ready; } + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideoCursor::Buffer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MovieVideoCursor::Buffer:: +Buffer(size_t block_size) : + _block_size(block_size) +{ + _block = (unsigned char *)PANDA_MALLOC_ARRAY(_block_size); +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieVideoCursor::Buffer::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MovieVideoCursor::Buffer:: +~Buffer() { + PANDA_FREE_ARRAY(_block); +} diff --git a/panda/src/movies/movieVideoCursor.cxx b/panda/src/movies/movieVideoCursor.cxx index fb50df6ca1..f694dbe12c 100644 --- a/panda/src/movies/movieVideoCursor.cxx +++ b/panda/src/movies/movieVideoCursor.cxx @@ -38,12 +38,9 @@ MovieVideoCursor(MovieVideo *src) : _can_seek(true), _can_seek_fast(true), _aborted(false), - _last_start(-1.0), - _next_start(0.0), _streaming(false), _ready(false) { - _standard_buffer._block = NULL; } //////////////////////////////////////////////////////////////////// @@ -53,9 +50,6 @@ MovieVideoCursor(MovieVideo *src) : //////////////////////////////////////////////////////////////////// MovieVideoCursor:: ~MovieVideoCursor() { - if (_standard_buffer._block != NULL) { - PANDA_FREE_ARRAY(_standard_buffer._block); - } } //////////////////////////////////////////////////////////////////// @@ -78,16 +72,33 @@ setup_texture(Texture *tex) const { tex->set_pad_size(fullx - size_x(), fully - size_y()); } +//////////////////////////////////////////////////////////////////// +// Function: MovieVideoCursor::set_time +// Access: Published, Virtual +// Description: Updates the cursor to the indicated time. If +// loop_count >= 1, the time is clamped to the movie's +// length * loop_count. If loop_count <= 0, the time is +// understood to be modulo the movie's length. + +// Returns true if a new frame is now available, false +// otherwise. If this returns true, you should +// immediately follow this with exactly *one* call to +// one of the fetch_*() methods. +//////////////////////////////////////////////////////////////////// +bool MovieVideoCursor:: +set_time(double time, int loop_count) { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: MovieVideoCursor::fetch_into_bitbucket // Access: Published, Virtual -// Description: Discards the next video frame. Still sets -// last_start and next_start. +// Description: Discards the next video frame. // // See fetch_buffer for more details. //////////////////////////////////////////////////////////////////// void MovieVideoCursor:: -fetch_into_bitbucket(double time) { +fetch_into_bitbucket() { // This generic implementation is layered on fetch_buffer. // It will work for any derived class, so it is never necessary to @@ -95,10 +106,8 @@ fetch_into_bitbucket(double time) { // implementation, but since this function is rarely used, it // probably isn't worth the trouble. - Buffer *buffer = fetch_buffer(time); + PT(Buffer) buffer = fetch_buffer(); if (buffer != NULL) { - _last_start = buffer->_begin_time; - _next_start = buffer->_end_time; release_buffer(buffer); } } @@ -112,7 +121,7 @@ fetch_into_bitbucket(double time) { // See fetch_buffer for more details. //////////////////////////////////////////////////////////////////// void MovieVideoCursor:: -fetch_into_texture(double time, Texture *t, int page) { +fetch_into_texture(Texture *t, int page) { static PStatCollector fetch_into_texture_collector("*:Decode Video into Texture"); PStatTimer timer(fetch_into_texture_collector); @@ -133,15 +142,12 @@ fetch_into_texture(double time, Texture *t, int page) { unsigned char *data = img.p() + page * t->get_expected_ram_page_size(); - Buffer *buffer = fetch_buffer(time); + PT(Buffer) buffer = fetch_buffer(); if (buffer == NULL) { // No image available. return; } - _last_start = buffer->_begin_time; - _next_start = buffer->_end_time; - if (t->get_x_size() == size_x() && t->get_num_components() == get_num_components()) { memcpy(data, buffer->_block, size_x() * size_y() * get_num_components()); @@ -182,7 +188,7 @@ fetch_into_texture(double time, Texture *t, int page) { // See fetch_buffer for more details. //////////////////////////////////////////////////////////////////// void MovieVideoCursor:: -fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) { +fetch_into_texture_alpha(Texture *t, int page, int alpha_src) { // This generic implementation is layered on fetch_buffer. // It will work for any derived class, so it is never necessary to @@ -197,15 +203,12 @@ fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) { nassertv(page < t->get_z_size()); nassertv((alpha_src >= 0) && (alpha_src <= get_num_components())); - Buffer *buffer = fetch_buffer(time); + PT(Buffer) buffer = fetch_buffer(); if (buffer == NULL) { // No image available. return; } - _last_start = buffer->_begin_time; - _next_start = buffer->_end_time; - t->set_keep_ram_image(true); PTA_uchar img = t->modify_ram_image(); @@ -248,7 +251,7 @@ fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) { // See fetch_buffer for more details. //////////////////////////////////////////////////////////////////// void MovieVideoCursor:: -fetch_into_texture_rgb(double time, Texture *t, int page) { +fetch_into_texture_rgb(Texture *t, int page) { // This generic implementation is layered on fetch_buffer. // It will work for any derived class, so it is never necessary to @@ -262,15 +265,12 @@ fetch_into_texture_rgb(double time, Texture *t, int page) { nassertv(t->get_component_width() == 1); nassertv(page < t->get_z_size()); - Buffer *buffer = fetch_buffer(time); + PT(Buffer) buffer = fetch_buffer(); if (buffer == NULL) { // No image available. return; } - _last_start = buffer->_begin_time; - _next_start = buffer->_end_time; - t->set_keep_ram_image(true); PTA_uchar img = t->modify_ram_image(); @@ -297,13 +297,11 @@ fetch_into_texture_rgb(double time, Texture *t, int page) { // Function: MovieVideoCursor::fetch_buffer // Access: Published, Virtual // Description: Reads the specified video frame and returns it in a -// pre-allocated buffer. The frame's begin and end -// times are stored in _begin_time and _end_time, within -// the buffer. After you have copied the data from the -// buffer, you should call release_buffer() to make the -// space available again to populate the next frame. -// You may not call fetch_buffer() again until you have -// called release_buffer(). +// pre-allocated buffer. After you have copied the data +// from the buffer, you should call release_buffer() to +// make the space available again to populate the next +// frame. You may not call fetch_buffer() again until +// you have called release_buffer(). // // If the movie reports that it can_seek, you may // also specify a timestamp less than next_start. @@ -316,39 +314,9 @@ fetch_into_texture_rgb(double time, Texture *t, int page) { // desired location. Only if can_seek_fast returns // true can it seek rapidly. //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *MovieVideoCursor:: -fetch_buffer(double time) { - Buffer *buffer = get_standard_buffer(); - - // The following is the implementation of the null video stream, ie, - // a stream of blinking red and blue frames. This method must be - // overridden by the subclass. - - buffer->_begin_time = floor(time); - buffer->_end_time = buffer->_begin_time + 1; - int flash = ((int)buffer->_begin_time) & 1; - - unsigned char *p = buffer->_block; - int src_width = get_num_components(); - for (int y = 0; y < size_y(); ++y) { - for (int x = 0; x < size_x(); ++x) { - if (flash) { - p[0] = 255; - p[1] = 128; - p[2] = 128; - } else { - p[0] = 128; - p[1] = 128; - p[2] = 255; - } - if (src_width == 4) { - p[3] = 255; - } - p += src_width; - } - } - - return buffer; +PT(MovieVideoCursor::Buffer) MovieVideoCursor:: +fetch_buffer() { + return NULL; } //////////////////////////////////////////////////////////////////// @@ -360,8 +328,7 @@ fetch_buffer(double time) { //////////////////////////////////////////////////////////////////// void MovieVideoCursor:: release_buffer(Buffer *buffer) { - nassertv(buffer == &_standard_buffer); - nassertv(buffer->_begin_time == _last_start && buffer->_end_time == _next_start); + nassertv(buffer == _standard_buffer); } //////////////////////////////////////////////////////////////////// @@ -374,43 +341,21 @@ release_buffer(Buffer *buffer) { //////////////////////////////////////////////////////////////////// MovieVideoCursor::Buffer *MovieVideoCursor:: get_standard_buffer() { - if (_standard_buffer._block == NULL) { - _standard_buffer._block_size = size_x() * size_y() * get_num_components(); - _standard_buffer._block = (unsigned char *)PANDA_MALLOC_ARRAY(_standard_buffer._block_size); - _standard_buffer._begin_time = -1.0; - _standard_buffer._end_time = 0.0; + if (_standard_buffer == NULL) { + _standard_buffer = make_new_buffer(); } - return &_standard_buffer; + return _standard_buffer; } //////////////////////////////////////////////////////////////////// -// Function: MovieVideoCursor::internal_alloc_buffer -// Access: Protected +// Function: MovieVideoCursor::make_new_buffer +// Access: Protected, Virtual // Description: May be called by a derived class to allocate a new -// Buffer object. The caller is responsible for -// eventually passing this object to -// internal_free_buffer(). +// Buffer object. //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *MovieVideoCursor:: -internal_alloc_buffer() { - Buffer *buffer = new Buffer; - buffer->_block_size = size_x() * size_y() * get_num_components(); - buffer->_block = (unsigned char *)PANDA_MALLOC_ARRAY(buffer->_block_size); - buffer->_begin_time = -1.0; - buffer->_end_time = 0.0; - return buffer; -} - -//////////////////////////////////////////////////////////////////// -// Function: MovieVideoCursor::internal_free_buffer -// Access: Protected -// Description: Frees a Buffer object allocated via -// internal_alloc_buffer(). -//////////////////////////////////////////////////////////////////// -void MovieVideoCursor:: -internal_free_buffer(Buffer *buffer) { - PANDA_FREE_ARRAY(buffer->_block); - delete buffer; +PT(MovieVideoCursor::Buffer) MovieVideoCursor:: +make_new_buffer() { + return new Buffer(size_x() * size_y() * get_num_components()); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/movies/movieVideoCursor.h b/panda/src/movies/movieVideoCursor.h index 2f9c65228d..08deaa2e4d 100644 --- a/panda/src/movies/movieVideoCursor.h +++ b/panda/src/movies/movieVideoCursor.h @@ -53,31 +53,31 @@ PUBLISHED: INLINE bool can_seek() const; INLINE bool can_seek_fast() const; INLINE bool aborted() const; - INLINE double last_start() const; - INLINE double next_start() const; + INLINE bool ready() const; INLINE bool streaming() const; void setup_texture(Texture *tex) const; - virtual void fetch_into_bitbucket(double time); - virtual void fetch_into_texture(double time, Texture *t, int page); - virtual void fetch_into_texture_rgb(double time, Texture *t, int page); - virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src); + + virtual bool set_time(double time, int loop_count); + virtual void fetch_into_bitbucket(); + virtual void fetch_into_texture(Texture *t, int page); + virtual void fetch_into_texture_rgb(Texture *t, int page); + virtual void fetch_into_texture_alpha(Texture *t, int page, int alpha_src); public: - class Buffer : public MemoryBase { + class Buffer : public ReferenceCount { public: + INLINE Buffer(size_t block_size); + INLINE virtual ~Buffer(); unsigned char *_block; size_t _block_size; - double _begin_time; - double _end_time; }; - virtual Buffer *fetch_buffer(double time); + virtual PT(Buffer) fetch_buffer(); virtual void release_buffer(Buffer *buffer); protected: Buffer *get_standard_buffer(); - Buffer *internal_alloc_buffer(); - void internal_free_buffer(Buffer *buffer); + virtual PT(Buffer) make_new_buffer(); protected: PT(MovieVideo) _source; @@ -88,12 +88,10 @@ protected: bool _can_seek; bool _can_seek_fast; bool _aborted; - double _last_start; - double _next_start; bool _streaming; bool _ready; - Buffer _standard_buffer; + PT(Buffer) _standard_buffer; public: virtual void write_datagram(BamWriter *manager, Datagram &dg); diff --git a/panda/src/vision/webcamVideoCursorOpenCV.cxx b/panda/src/vision/webcamVideoCursorOpenCV.cxx index 035852b456..d81c519775 100644 --- a/panda/src/vision/webcamVideoCursorOpenCV.cxx +++ b/panda/src/vision/webcamVideoCursorOpenCV.cxx @@ -34,8 +34,6 @@ WebcamVideoCursorOpenCV(WebcamVideoOpenCV *src) : MovieVideoCursor(src) { _can_seek = false; _can_seek_fast = false; _aborted = false; - _last_start = -1.0; - _next_start = 0.0; _streaming = true; _ready = false; @@ -65,13 +63,13 @@ WebcamVideoCursorOpenCV:: // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *WebcamVideoCursorOpenCV:: -fetch_buffer(double time) { +PT(MovieVideoCursor::Buffer) WebcamVideoCursorOpenCV:: +fetch_buffer() { if (!_ready) { return NULL; } - Buffer *buffer = get_standard_buffer(); + PT(Buffer) buffer = get_standard_buffer(); unsigned char *dest = buffer->_block; int num_components = get_num_components(); nassertr(num_components == 3, NULL); diff --git a/panda/src/vision/webcamVideoCursorOpenCV.h b/panda/src/vision/webcamVideoCursorOpenCV.h index 48f1ce5549..4e8a6f8e7a 100644 --- a/panda/src/vision/webcamVideoCursorOpenCV.h +++ b/panda/src/vision/webcamVideoCursorOpenCV.h @@ -31,7 +31,7 @@ class WebcamVideoCursorOpenCV : public MovieVideoCursor { public: WebcamVideoCursorOpenCV(WebcamVideoOpenCV *src); virtual ~WebcamVideoCursorOpenCV(); - virtual Buffer *fetch_buffer(double time); + virtual PT(Buffer) fetch_buffer(); private: bool get_frame_data(const unsigned char *&r, diff --git a/panda/src/vision/webcamVideoCursorV4L.cxx b/panda/src/vision/webcamVideoCursorV4L.cxx index a0bcbe2d97..8231a5d6d0 100644 --- a/panda/src/vision/webcamVideoCursorV4L.cxx +++ b/panda/src/vision/webcamVideoCursorV4L.cxx @@ -137,8 +137,6 @@ WebcamVideoCursorV4L(WebcamVideoV4L *src) : MovieVideoCursor(src) { _can_seek = false; _can_seek_fast = false; _aborted = false; - _last_start = -1.0; - _next_start = 0.0; _streaming = true; _ready = false; _format = (struct v4l2_format *) malloc(sizeof(struct v4l2_format)); @@ -312,13 +310,13 @@ WebcamVideoCursorV4L:: // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// -MovieVideoCursor::Buffer *WebcamVideoCursorV4L:: -fetch_buffer(double time) { +PT(MovieVideoCursor::Buffer) WebcamVideoCursorV4L:: +fetch_buffer() { if (!_ready) { return NULL; } - Buffer *buffer = get_standard_buffer(); + PT(Buffer) buffer = get_standard_buffer(); unsigned char *block = buffer->_block; struct v4l2_buffer vbuf; memset(&vbuf, 0, sizeof vbuf); diff --git a/panda/src/vision/webcamVideoCursorV4L.h b/panda/src/vision/webcamVideoCursorV4L.h index cf64012991..eeb51cc9a5 100644 --- a/panda/src/vision/webcamVideoCursorV4L.h +++ b/panda/src/vision/webcamVideoCursorV4L.h @@ -37,7 +37,7 @@ class WebcamVideoCursorV4L : public MovieVideoCursor { public: WebcamVideoCursorV4L(WebcamVideoV4L *src); virtual ~WebcamVideoCursorV4L(); - virtual Buffer *fetch_buffer(double time); + virtual PT(Buffer) fetch_buffer(); private: int _fd;