mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
Add movies-sync-pages
This commit is contained in:
parent
d1c257fba9
commit
6eb257bb8a
@ -84,6 +84,17 @@ ConfigVariableInt pfm_vis_max_indices
|
|||||||
"a single generated mesh. If the mesh would require more than that, "
|
"a single generated mesh. If the mesh would require more than that, "
|
||||||
"the mesh is subdivided into smaller pieces."));
|
"the mesh is subdivided into smaller pieces."));
|
||||||
|
|
||||||
|
ConfigVariableBool movies_sync_pages
|
||||||
|
("movies-sync-pages", true,
|
||||||
|
PRC_DESC("Set this true to force multi-page MovieTextures to hold pages "
|
||||||
|
"back if necessary until all pages are ready to render at once, "
|
||||||
|
"so that the multiple pages of a single movie are always in sync "
|
||||||
|
"with each other. Set this false to allow individual pages to be "
|
||||||
|
"visible as soon as they come available, which means pages might "
|
||||||
|
"sometimes be out of sync. This only affects multi-page MovieTextures "
|
||||||
|
"such as cube maps, 3-d textures, or stereo textures, or textures "
|
||||||
|
"with separate color and alpha channel movie sources."));
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: init_libgrutil
|
// Function: init_libgrutil
|
||||||
// Description: Initializes the library. This must be called at
|
// Description: Initializes the library. This must be called at
|
||||||
|
@ -40,6 +40,8 @@ extern ConfigVariableBool pfm_reverse_dimensions;
|
|||||||
extern ConfigVariableInt pfm_vis_max_vertices;
|
extern ConfigVariableInt pfm_vis_max_vertices;
|
||||||
extern ConfigVariableInt pfm_vis_max_indices;
|
extern ConfigVariableInt pfm_vis_max_indices;
|
||||||
|
|
||||||
|
extern ConfigVariableBool movies_sync_pages;
|
||||||
|
|
||||||
extern EXPCL_PANDA_GRUTIL void init_libgrutil();
|
extern EXPCL_PANDA_GRUTIL void init_libgrutil();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,7 +68,8 @@ CData() :
|
|||||||
_clock(0.0),
|
_clock(0.0),
|
||||||
_playing(false),
|
_playing(false),
|
||||||
_loop_count(1),
|
_loop_count(1),
|
||||||
_play_rate(1.0)
|
_play_rate(1.0),
|
||||||
|
_has_offset(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +87,8 @@ CData(const CData ©) :
|
|||||||
_clock(0.0),
|
_clock(0.0),
|
||||||
_playing(false),
|
_playing(false),
|
||||||
_loop_count(1),
|
_loop_count(1),
|
||||||
_play_rate(1.0)
|
_play_rate(1.0),
|
||||||
|
_has_offset(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +345,9 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
|
|||||||
Texture::CDReader cdata_tex(Texture::_cycler);
|
Texture::CDReader cdata_tex(Texture::_cycler);
|
||||||
CDReader cdata(_cycler);
|
CDReader cdata(_cycler);
|
||||||
|
|
||||||
|
if (!cdata->_has_offset) {
|
||||||
|
// If we don't have a previously-computed timestamp (offset)
|
||||||
|
// cached, then compute a new one.
|
||||||
double offset;
|
double offset;
|
||||||
int true_loop_count = 1;
|
int true_loop_count = 1;
|
||||||
if (cdata->_synchronize != 0) {
|
if (cdata->_synchronize != 0) {
|
||||||
@ -356,24 +361,43 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
|
|||||||
}
|
}
|
||||||
true_loop_count = cdata->_loop_count;
|
true_loop_count = cdata->_loop_count;
|
||||||
}
|
}
|
||||||
|
((CData *)cdata.p())->_offset = offset;
|
||||||
|
((CData *)cdata.p())->_true_loop_count = true_loop_count;
|
||||||
|
((CData *)cdata.p())->_has_offset = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<((int)(cdata->_pages.size())); i++) {
|
bool in_sync = do_update_frames(cdata);
|
||||||
MovieVideoCursor *color = cdata->_pages[i]._color;
|
if (!in_sync) {
|
||||||
MovieVideoCursor *alpha = cdata->_pages[i]._alpha;
|
// If it didn't successfully sync, try again--once. The second
|
||||||
if (color && alpha) {
|
// time it might be able to fill in some more recent frames.
|
||||||
if (color->set_time(offset, true_loop_count)) {
|
in_sync = do_update_frames(cdata);
|
||||||
color->fetch_into_texture_rgb((MovieTexture*)this, i);
|
|
||||||
}
|
}
|
||||||
if (alpha->set_time(offset, true_loop_count)) {
|
|
||||||
alpha->fetch_into_texture_alpha((MovieTexture*)this, i, cdata_tex->_alpha_file_channel);
|
if (in_sync) {
|
||||||
}
|
// Now go back through and apply all the frames to the texture.
|
||||||
} else if (color) {
|
Pages::const_iterator pi;
|
||||||
bool result = color->set_time(offset, true_loop_count);
|
for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
|
||||||
if (result) {
|
const VideoPage &page = (*pi);
|
||||||
color->fetch_into_texture((MovieTexture*)this, i);
|
MovieVideoCursor *color = page._color;
|
||||||
|
MovieVideoCursor *alpha = page._alpha;
|
||||||
|
size_t i = pi - cdata->_pages.begin();
|
||||||
|
|
||||||
|
if (color != NULL && alpha != NULL) {
|
||||||
|
color->apply_to_texture_rgb(page._cbuffer, (MovieTexture*)this, i);
|
||||||
|
alpha->apply_to_texture_alpha(page._abuffer, (MovieTexture*)this, i, cdata_tex->_alpha_file_channel);
|
||||||
|
|
||||||
|
} else if (color != NULL) {
|
||||||
|
color->apply_to_texture(page._cbuffer, (MovieTexture*)this, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
((VideoPage &)page)._cbuffer.clear();
|
||||||
|
((VideoPage &)page)._abuffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the cached offset so we can update the frame next time.
|
||||||
|
((CData *)cdata.p())->_has_offset = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,6 +717,134 @@ unsynchronize() {
|
|||||||
cdata->_synchronize = 0;
|
cdata->_synchronize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: MovieTexture::do_update_frames
|
||||||
|
// Access: Private
|
||||||
|
// Description: Called internally to sync all of the frames to the
|
||||||
|
// current time. Returns true if successful, or false
|
||||||
|
// of some of the frames are out-of-date with each
|
||||||
|
// other.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
bool MovieTexture::
|
||||||
|
do_update_frames(const CData *cdata) const {
|
||||||
|
// Throughout this method, we cast the VideoPage to non-const to
|
||||||
|
// update the _cbuffer or _abuffer member. We can do this safely
|
||||||
|
// because this is only a transparent cache value.
|
||||||
|
nassertr(cdata->_has_offset, false);
|
||||||
|
|
||||||
|
// First, go through and get all of the current frames.
|
||||||
|
Pages::const_iterator pi;
|
||||||
|
for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
|
||||||
|
const VideoPage &page = (*pi);
|
||||||
|
MovieVideoCursor *color = page._color;
|
||||||
|
MovieVideoCursor *alpha = page._alpha;
|
||||||
|
|
||||||
|
if (color != NULL && page._cbuffer == NULL) {
|
||||||
|
if (color->set_time(cdata->_offset, cdata->_true_loop_count)) {
|
||||||
|
((VideoPage &)page)._cbuffer = color->fetch_buffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alpha != NULL && page._abuffer == NULL) {
|
||||||
|
if (alpha->set_time(cdata->_offset, cdata->_true_loop_count)) {
|
||||||
|
((VideoPage &)page)._abuffer = alpha->fetch_buffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!movies_sync_pages) {
|
||||||
|
// If movies-sync-pages is configured off, we don't care about
|
||||||
|
// syncing the pages, and we always return true here to render the
|
||||||
|
// pages we've got.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now make sure all of the frames are in sync with each other.
|
||||||
|
bool in_sync = true;
|
||||||
|
bool any_frames = false;
|
||||||
|
bool any_dropped = false;
|
||||||
|
PT(MovieVideoCursor::Buffer) newest;
|
||||||
|
for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
|
||||||
|
const VideoPage &page = (*pi);
|
||||||
|
if (page._cbuffer == NULL) {
|
||||||
|
if (page._color != NULL) {
|
||||||
|
// This page isn't ready at all.
|
||||||
|
in_sync = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nassertr(page._color != NULL, true);
|
||||||
|
any_frames = true;
|
||||||
|
if (newest == NULL) {
|
||||||
|
newest = page._cbuffer;
|
||||||
|
} else {
|
||||||
|
int ref = newest->compare_timestamp(page._cbuffer);
|
||||||
|
if (ref != 0) {
|
||||||
|
// This page is ready, but out-of-date.
|
||||||
|
in_sync = false;
|
||||||
|
any_dropped = true;
|
||||||
|
if (ref < 0) {
|
||||||
|
newest = page._cbuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page._abuffer == NULL) {
|
||||||
|
if (page._alpha != NULL) {
|
||||||
|
in_sync = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nassertr(page._alpha != NULL, true);
|
||||||
|
any_frames = true;
|
||||||
|
if (newest == NULL) {
|
||||||
|
newest = page._abuffer;
|
||||||
|
} else {
|
||||||
|
int ref = newest->compare_timestamp(page._abuffer);
|
||||||
|
if (ref != 0) {
|
||||||
|
in_sync = false;
|
||||||
|
any_dropped = true;
|
||||||
|
if (ref < 0) {
|
||||||
|
newest = page._abuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!any_frames) {
|
||||||
|
// If no frames at all are ready yet, just carry on.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_sync) {
|
||||||
|
// If we're not in sync, throw away pages that are older than the
|
||||||
|
// newest available frame.
|
||||||
|
if (newest != NULL) {
|
||||||
|
Pages::const_iterator pi;
|
||||||
|
for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
|
||||||
|
const VideoPage &page = (*pi);
|
||||||
|
if (page._cbuffer != NULL && newest->compare_timestamp(page._cbuffer) > 0) {
|
||||||
|
((VideoPage &)page)._cbuffer.clear();
|
||||||
|
any_dropped = true;
|
||||||
|
}
|
||||||
|
if (page._abuffer != NULL && newest->compare_timestamp(page._abuffer) > 0) {
|
||||||
|
((VideoPage &)page)._abuffer.clear();
|
||||||
|
any_dropped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any_dropped) {
|
||||||
|
// If we dropped one or more frames for being out-of-sync,
|
||||||
|
// implying that compare_timestamp() is implemented, then we
|
||||||
|
// also want to update our internal offset value so that
|
||||||
|
// future frames will get the same value.
|
||||||
|
((CData *)cdata)->_offset = newest->get_timestamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return in_sync;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: MovieTexture::register_with_read_factory
|
// Function: MovieTexture::register_with_read_factory
|
||||||
// Access: Public, Static
|
// Access: Public, Static
|
||||||
|
@ -101,6 +101,10 @@ protected:
|
|||||||
public:
|
public:
|
||||||
PT(MovieVideoCursor) _color;
|
PT(MovieVideoCursor) _color;
|
||||||
PT(MovieVideoCursor) _alpha;
|
PT(MovieVideoCursor) _alpha;
|
||||||
|
|
||||||
|
// The current (but not yet applied) frame for each video.
|
||||||
|
PT(MovieVideoCursor::Buffer) _cbuffer;
|
||||||
|
PT(MovieVideoCursor::Buffer) _abuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef pvector<VideoPage> Pages;
|
typedef pvector<VideoPage> Pages;
|
||||||
@ -124,6 +128,12 @@ protected:
|
|||||||
int _loop_count;
|
int _loop_count;
|
||||||
double _play_rate;
|
double _play_rate;
|
||||||
PT(AudioSound) _synchronize;
|
PT(AudioSound) _synchronize;
|
||||||
|
|
||||||
|
// The remaining values represent a local cache only; it is not
|
||||||
|
// preserved through the pipeline.
|
||||||
|
bool _has_offset;
|
||||||
|
double _offset;
|
||||||
|
int _true_loop_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
PipelineCycler<CData> _cycler;
|
PipelineCycler<CData> _cycler;
|
||||||
@ -133,6 +143,9 @@ protected:
|
|||||||
void do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex,
|
void do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex,
|
||||||
const LoaderOptions &options);
|
const LoaderOptions &options);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool do_update_frames(const CData *cdata) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void register_with_read_factory();
|
static void register_with_read_factory();
|
||||||
|
|
||||||
|
@ -19,9 +19,10 @@
|
|||||||
// Description:
|
// Description:
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE FfmpegVideoCursor::FfmpegBuffer::
|
INLINE FfmpegVideoCursor::FfmpegBuffer::
|
||||||
FfmpegBuffer(size_t block_size) :
|
FfmpegBuffer(size_t block_size, double video_timebase) :
|
||||||
Buffer(block_size),
|
Buffer(block_size),
|
||||||
_begin_frame(-1),
|
_begin_frame(-1),
|
||||||
_end_frame(0)
|
_end_frame(0),
|
||||||
|
_video_timebase(video_timebase)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ extern "C" {
|
|||||||
|
|
||||||
ReMutex FfmpegVideoCursor::_av_lock;
|
ReMutex FfmpegVideoCursor::_av_lock;
|
||||||
TypeHandle FfmpegVideoCursor::_type_handle;
|
TypeHandle FfmpegVideoCursor::_type_handle;
|
||||||
|
TypeHandle FfmpegVideoCursor::FfmpegBuffer::_type_handle;
|
||||||
|
|
||||||
PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decoding:Fetch");
|
PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decoding:Fetch");
|
||||||
PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
|
PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
|
||||||
@ -308,7 +309,6 @@ stop_thread() {
|
|||||||
MutexHolder holder(_lock);
|
MutexHolder holder(_lock);
|
||||||
|
|
||||||
_readahead_frames.clear();
|
_readahead_frames.clear();
|
||||||
_recycled_frames.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -329,8 +329,8 @@ is_thread_started() const {
|
|||||||
// Description: See MovieVideoCursor::set_time().
|
// Description: See MovieVideoCursor::set_time().
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
bool FfmpegVideoCursor::
|
bool FfmpegVideoCursor::
|
||||||
set_time(double time, int loop_count) {
|
set_time(double timestamp, int loop_count) {
|
||||||
int frame = (int)(time / _video_timebase + 0.5);
|
int frame = (int)(timestamp / _video_timebase + 0.5);
|
||||||
|
|
||||||
if (_eof_known) {
|
if (_eof_known) {
|
||||||
if (loop_count == 0) {
|
if (loop_count == 0) {
|
||||||
@ -403,7 +403,6 @@ fetch_buffer() {
|
|||||||
<< " at frame " << _current_frame << ", discarding frame at "
|
<< " at frame " << _current_frame << ", discarding frame at "
|
||||||
<< frame->_begin_frame << "\n";
|
<< frame->_begin_frame << "\n";
|
||||||
}
|
}
|
||||||
do_recycle_frame(frame);
|
|
||||||
frame = _readahead_frames.front();
|
frame = _readahead_frames.front();
|
||||||
_readahead_frames.pop_front();
|
_readahead_frames.pop_front();
|
||||||
}
|
}
|
||||||
@ -416,7 +415,7 @@ fetch_buffer() {
|
|||||||
<< " at frame " << _current_frame << ", encountered too-new frame at "
|
<< " at frame " << _current_frame << ", encountered too-new frame at "
|
||||||
<< frame->_begin_frame << "\n";
|
<< frame->_begin_frame << "\n";
|
||||||
}
|
}
|
||||||
do_recycle_all_frames();
|
do_clear_all_frames();
|
||||||
if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) {
|
if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) {
|
||||||
_thread_status = TS_seek;
|
_thread_status = TS_seek;
|
||||||
_seek_frame = _current_frame;
|
_seek_frame = _current_frame;
|
||||||
@ -439,15 +438,11 @@ fetch_buffer() {
|
|||||||
bool too_new = frame->_begin_frame > _current_frame;
|
bool too_new = frame->_begin_frame > _current_frame;
|
||||||
if (too_old || too_new) {
|
if (too_old || too_new) {
|
||||||
// The frame is too old or too new. Just recycle it.
|
// The frame is too old or too new. Just recycle it.
|
||||||
do_recycle_frame(frame);
|
|
||||||
frame = NULL;
|
frame = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame != NULL) {
|
if (frame != NULL) {
|
||||||
if (_current_frame_buffer != NULL) {
|
|
||||||
do_recycle_frame(_current_frame_buffer);
|
|
||||||
}
|
|
||||||
_current_frame_buffer = frame;
|
_current_frame_buffer = frame;
|
||||||
if (ffmpeg_cat.is_debug()) {
|
if (ffmpeg_cat.is_debug()) {
|
||||||
ffmpeg_cat.debug()
|
ffmpeg_cat.debug()
|
||||||
@ -465,17 +460,6 @@ fetch_buffer() {
|
|||||||
return frame.p();
|
return frame.p();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: FfmpegVideoCursor::release_buffer
|
|
||||||
// Access: Public, Virtual
|
|
||||||
// Description: Should be called after processing the Buffer object
|
|
||||||
// returned by fetch_buffer(), this releases the Buffer
|
|
||||||
// for future use again.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void FfmpegVideoCursor::
|
|
||||||
release_buffer(Buffer *buffer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::make_new_buffer
|
// Function: FfmpegVideoCursor::make_new_buffer
|
||||||
// Access: Protected, Virtual
|
// Access: Protected, Virtual
|
||||||
@ -484,7 +468,7 @@ release_buffer(Buffer *buffer) {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
PT(MovieVideoCursor::Buffer) FfmpegVideoCursor::
|
PT(MovieVideoCursor::Buffer) FfmpegVideoCursor::
|
||||||
make_new_buffer() {
|
make_new_buffer() {
|
||||||
PT(FfmpegBuffer) frame = new FfmpegBuffer(size_x() * size_y() * get_num_components());
|
PT(FfmpegBuffer) frame = new FfmpegBuffer(size_x() * size_y() * get_num_components(), _video_timebase);
|
||||||
return frame.p();
|
return frame.p();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,8 +667,10 @@ thread_main() {
|
|||||||
|
|
||||||
while (do_poll()) {
|
while (do_poll()) {
|
||||||
// Keep doing stuff as long as there's something to do.
|
// Keep doing stuff as long as there's something to do.
|
||||||
|
_lock.release();
|
||||||
PStatClient::thread_tick(_sync_name);
|
PStatClient::thread_tick(_sync_name);
|
||||||
Thread::consider_yield();
|
Thread::consider_yield();
|
||||||
|
_lock.acquire();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,7 +716,6 @@ do_poll() {
|
|||||||
} else {
|
} else {
|
||||||
// No frame.
|
// No frame.
|
||||||
_lock.acquire();
|
_lock.acquire();
|
||||||
do_recycle_frame(frame);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -750,12 +735,11 @@ do_poll() {
|
|||||||
if (_frame_ready) {
|
if (_frame_ready) {
|
||||||
export_frame(frame);
|
export_frame(frame);
|
||||||
_lock.acquire();
|
_lock.acquire();
|
||||||
do_recycle_all_frames();
|
do_clear_all_frames();
|
||||||
_readahead_frames.push_back(frame);
|
_readahead_frames.push_back(frame);
|
||||||
} else {
|
} else {
|
||||||
_lock.acquire();
|
_lock.acquire();
|
||||||
do_recycle_all_frames();
|
do_clear_all_frames();
|
||||||
do_recycle_frame(frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_thread_status == TS_seeking) {
|
if (_thread_status == TS_seeking) {
|
||||||
@ -776,50 +760,24 @@ do_poll() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::do_alloc_frame
|
// Function: FfmpegVideoCursor::do_alloc_frame
|
||||||
// Access: Private
|
// Access: Private
|
||||||
// Description: Allocates a new Buffer object, or returns a
|
// Description: Allocates a new Buffer object. Assumes the lock is
|
||||||
// previously-recycled object. Assumes the lock is
|
|
||||||
// held.
|
// held.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
PT(FfmpegVideoCursor::FfmpegBuffer) FfmpegVideoCursor::
|
PT(FfmpegVideoCursor::FfmpegBuffer) FfmpegVideoCursor::
|
||||||
do_alloc_frame() {
|
do_alloc_frame() {
|
||||||
if (!_recycled_frames.empty()) {
|
|
||||||
PT(FfmpegBuffer) frame = _recycled_frames.front();
|
|
||||||
_recycled_frames.pop_front();
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
PT(Buffer) buffer = make_new_buffer();
|
PT(Buffer) buffer = make_new_buffer();
|
||||||
return (FfmpegBuffer *)buffer.p();
|
return (FfmpegBuffer *)buffer.p();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: FfmpegVideoCursor::do_recycle_frame
|
// Function: FfmpegVideoCursor::do_clear_all_frames
|
||||||
// Access: Private
|
// Access: Private
|
||||||
// Description: Recycles a previously-allocated Buffer object for
|
// Description: Empties the entire readahead_frames queue.
|
||||||
// future reuse. Assumes the lock is held.
|
// Assumes the lock is held.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void FfmpegVideoCursor::
|
void FfmpegVideoCursor::
|
||||||
do_recycle_frame(FfmpegBuffer *frame) {
|
do_clear_all_frames() {
|
||||||
_recycled_frames.push_back(frame);
|
_readahead_frames.clear();
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: FfmpegVideoCursor::do_recycle_all_frames
|
|
||||||
// Access: Private
|
|
||||||
// Description: Empties the entire readahead_frames queue into the
|
|
||||||
// recycle bin. Assumes the lock is held.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void FfmpegVideoCursor::
|
|
||||||
do_recycle_all_frames() {
|
|
||||||
while (!_readahead_frames.empty()) {
|
|
||||||
PT(FfmpegBuffer) frame = _readahead_frames.front();
|
|
||||||
_readahead_frames.pop_front();
|
|
||||||
if (ffmpeg_cat.is_spam()) {
|
|
||||||
ffmpeg_cat.spam()
|
|
||||||
<< "ffmpeg for " << _filename.get_basename()
|
|
||||||
<< " recycling frame at " << frame->_begin_frame << "\n";
|
|
||||||
}
|
|
||||||
_recycled_frames.push_back(frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -1331,4 +1289,43 @@ fillin(DatagramIterator &scan, BamReader *manager) {
|
|||||||
manager->register_finalize(this);
|
manager->register_finalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: FfmpegVideoCursor::FfmpegBuffer::compare_timestamp
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description: 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.
|
||||||
|
//
|
||||||
|
// Returns 0 if the two buffers are of the same frame,
|
||||||
|
// <0 if this one comes earlier than the other one, and
|
||||||
|
// >0 if the other one comes earlier.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
int FfmpegVideoCursor::FfmpegBuffer::
|
||||||
|
compare_timestamp(const Buffer *other) const {
|
||||||
|
const FfmpegBuffer *fother;
|
||||||
|
DCAST_INTO_R(fother, other, 0);
|
||||||
|
if (_end_frame * _video_timebase <= fother->_begin_frame * fother->_video_timebase) {
|
||||||
|
return -1;
|
||||||
|
} else if (_begin_frame * _video_timebase >= fother->_end_frame * fother->_video_timebase) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: FfmpegVideoCursor::FfmpegBuffer::get_timestamp
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description: Returns the nearest timestamp value of this
|
||||||
|
// particular buffer. Ideally,
|
||||||
|
// MovieVideoCursor::set_time() for this timestamp would
|
||||||
|
// return this buffer again. This need be defined only
|
||||||
|
// if compare_timestamp() is also defined.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
double FfmpegVideoCursor::FfmpegBuffer::
|
||||||
|
get_timestamp() const {
|
||||||
|
int mid_frame = (_begin_frame + _end_frame - 1) / 2;
|
||||||
|
return mid_frame * _video_timebase;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_FFMPEG
|
#endif // HAVE_FFMPEG
|
||||||
|
@ -61,16 +61,37 @@ PUBLISHED:
|
|||||||
bool is_thread_started() const;
|
bool is_thread_started() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual bool set_time(double time, int loop_count);
|
virtual bool set_time(double timestamp, int loop_count);
|
||||||
virtual PT(Buffer) fetch_buffer();
|
virtual PT(Buffer) fetch_buffer();
|
||||||
virtual void release_buffer(Buffer *buffer);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class FfmpegBuffer : public Buffer {
|
class FfmpegBuffer : public Buffer {
|
||||||
public:
|
public:
|
||||||
INLINE FfmpegBuffer(size_t block_size);
|
ALLOC_DELETED_CHAIN(FfmpegBuffer);
|
||||||
|
INLINE FfmpegBuffer(size_t block_size, double video_timebase);
|
||||||
|
virtual int compare_timestamp(const Buffer *other) const;
|
||||||
|
virtual double get_timestamp() const;
|
||||||
|
|
||||||
int _begin_frame;
|
int _begin_frame;
|
||||||
int _end_frame;
|
int _end_frame;
|
||||||
|
double _video_timebase;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TypeHandle get_class_type() {
|
||||||
|
return _type_handle;
|
||||||
|
}
|
||||||
|
static void init_type() {
|
||||||
|
Buffer::init_type();
|
||||||
|
register_type(_type_handle, "FfmpegVideoCursor::FfmpegBuffer",
|
||||||
|
Buffer::get_class_type());
|
||||||
|
}
|
||||||
|
virtual TypeHandle get_type() const {
|
||||||
|
return get_class_type();
|
||||||
|
}
|
||||||
|
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TypeHandle _type_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual PT(Buffer) make_new_buffer();
|
virtual PT(Buffer) make_new_buffer();
|
||||||
@ -89,8 +110,8 @@ private:
|
|||||||
// This global Mutex protects calls to avcodec_open/close/etc.
|
// This global Mutex protects calls to avcodec_open/close/etc.
|
||||||
static ReMutex _av_lock;
|
static ReMutex _av_lock;
|
||||||
|
|
||||||
// Protects _readahead_frames, _recycled_buffers, and all the
|
// Protects _readahead_frames and all the immediately following
|
||||||
// immediately following members.
|
// members.
|
||||||
Mutex _lock;
|
Mutex _lock;
|
||||||
|
|
||||||
// Condition: the thread has something to do.
|
// Condition: the thread has something to do.
|
||||||
@ -98,7 +119,6 @@ private:
|
|||||||
|
|
||||||
typedef pdeque<PT(FfmpegBuffer) > Buffers;
|
typedef pdeque<PT(FfmpegBuffer) > Buffers;
|
||||||
Buffers _readahead_frames;
|
Buffers _readahead_frames;
|
||||||
Buffers _recycled_frames;
|
|
||||||
enum ThreadStatus {
|
enum ThreadStatus {
|
||||||
TS_stopped,
|
TS_stopped,
|
||||||
TS_wait,
|
TS_wait,
|
||||||
@ -120,8 +140,7 @@ private:
|
|||||||
bool do_poll();
|
bool do_poll();
|
||||||
|
|
||||||
PT(FfmpegBuffer) do_alloc_frame();
|
PT(FfmpegBuffer) do_alloc_frame();
|
||||||
void do_recycle_frame(FfmpegBuffer *frame);
|
void do_clear_all_frames();
|
||||||
void do_recycle_all_frames();
|
|
||||||
|
|
||||||
bool fetch_packet(int default_frame);
|
bool fetch_packet(int default_frame);
|
||||||
bool do_fetch_packet(int default_frame);
|
bool do_fetch_packet(int default_frame);
|
||||||
@ -179,6 +198,7 @@ public:
|
|||||||
MovieVideoCursor::init_type();
|
MovieVideoCursor::init_type();
|
||||||
register_type(_type_handle, "FfmpegVideoCursor",
|
register_type(_type_handle, "FfmpegVideoCursor",
|
||||||
MovieVideoCursor::get_class_type());
|
MovieVideoCursor::get_class_type());
|
||||||
|
FfmpegBuffer::init_type();
|
||||||
}
|
}
|
||||||
virtual TypeHandle get_type() const {
|
virtual TypeHandle get_type() const {
|
||||||
return get_class_type();
|
return get_class_type();
|
||||||
|
@ -163,25 +163,3 @@ ready() const {
|
|||||||
return _ready;
|
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);
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,12 @@
|
|||||||
#include "bamReader.h"
|
#include "bamReader.h"
|
||||||
#include "bamWriter.h"
|
#include "bamWriter.h"
|
||||||
|
|
||||||
|
PStatCollector MovieVideoCursor::_copy_pcollector("*:Copy Video into Texture");
|
||||||
|
PStatCollector MovieVideoCursor::_copy_pcollector_ram("*:Copy Video into Texture:modify_ram_image");
|
||||||
|
PStatCollector MovieVideoCursor::_copy_pcollector_copy("*:Copy Video into Texture:copy");
|
||||||
|
|
||||||
TypeHandle MovieVideoCursor::_type_handle;
|
TypeHandle MovieVideoCursor::_type_handle;
|
||||||
|
TypeHandle MovieVideoCursor::Buffer::_type_handle;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: MovieVideoCursor::Default Constructor
|
// Function: MovieVideoCursor::Default Constructor
|
||||||
@ -76,57 +81,58 @@ setup_texture(Texture *tex) const {
|
|||||||
// loop_count >= 1, the time is clamped to the movie's
|
// loop_count >= 1, the time is clamped to the movie's
|
||||||
// length * loop_count. If loop_count <= 0, the time is
|
// length * loop_count. If loop_count <= 0, the time is
|
||||||
// understood to be modulo the movie's length.
|
// understood to be modulo the movie's length.
|
||||||
|
//
|
||||||
// Returns true if a new frame is now available, false
|
// Returns true if a new frame is now available, false
|
||||||
// otherwise. If this returns true, you should
|
// otherwise. If this returns true, you should
|
||||||
// immediately follow this with exactly *one* call to
|
// immediately follow this with exactly *one* call to
|
||||||
// one of the fetch_*() methods.
|
// fetch_buffer().
|
||||||
|
//
|
||||||
|
// If the movie reports that it can_seek, you may also
|
||||||
|
// specify a time value less than the previous value you
|
||||||
|
// passed to set_time(). Otherwise, you may only
|
||||||
|
// specify a time value greater than or equal to
|
||||||
|
// the previous value.
|
||||||
|
//
|
||||||
|
// If the movie reports that it can_seek, it doesn't
|
||||||
|
// mean that it can do so quickly. It may have to
|
||||||
|
// rewind the movie and then fast forward to the
|
||||||
|
// desired location. Only if can_seek_fast returns
|
||||||
|
// true can it seek rapidly.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
bool MovieVideoCursor::
|
bool MovieVideoCursor::
|
||||||
set_time(double time, int loop_count) {
|
set_time(double timestamp, int loop_count) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: MovieVideoCursor::fetch_into_bitbucket
|
// Function: MovieVideoCursor::fetch_buffer
|
||||||
// Access: Published, Virtual
|
// Access: Published, Virtual
|
||||||
// Description: Discards the next video frame.
|
// Description: Gets the current video frame (as specified by
|
||||||
|
// set_time()) from the movie and returns it in a
|
||||||
|
// pre-allocated buffer. You may simply let the buffer
|
||||||
|
// dereference and delete itself when you are done with
|
||||||
|
// it.
|
||||||
//
|
//
|
||||||
// See fetch_buffer for more details.
|
// This may return NULL (even if set_time() returned
|
||||||
|
// true) if the frame is not available for some reason.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void MovieVideoCursor::
|
PT(MovieVideoCursor::Buffer) MovieVideoCursor::
|
||||||
fetch_into_bitbucket() {
|
fetch_buffer() {
|
||||||
|
return NULL;
|
||||||
// This generic implementation is layered on fetch_buffer.
|
|
||||||
// It will work for any derived class, so it is never necessary to
|
|
||||||
// redefine this. It is probably possible to make a faster
|
|
||||||
// implementation, but since this function is rarely used, it
|
|
||||||
// probably isn't worth the trouble.
|
|
||||||
|
|
||||||
PT(Buffer) buffer = fetch_buffer();
|
|
||||||
if (buffer != NULL) {
|
|
||||||
release_buffer(buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: MovieVideoCursor::fetch_into_texture
|
// Function: MovieVideoCursor::apply_to_texture
|
||||||
// Access: Published, Virtual
|
// Access: Published, Virtual
|
||||||
// Description: Reads the specified video frame into
|
// Description: Stores this buffer's contents in the indicated texture.
|
||||||
// the specified texture.
|
|
||||||
//
|
|
||||||
// See fetch_buffer for more details.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void MovieVideoCursor::
|
void MovieVideoCursor::
|
||||||
fetch_into_texture(Texture *t, int page) {
|
apply_to_texture(const Buffer *buffer, Texture *t, int page) {
|
||||||
static PStatCollector fetch_into_texture_collector("*:Decode Video into Texture");
|
if (buffer == NULL) {
|
||||||
PStatTimer timer(fetch_into_texture_collector);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// This generic implementation is layered on fetch_buffer.
|
PStatTimer timer(_copy_pcollector);
|
||||||
// It will work for any derived class, so it is never necessary to
|
|
||||||
// redefine this. However, it may be possible to make a faster
|
|
||||||
// implementation that uses fewer intermediate copies, depending
|
|
||||||
// on the capabilities of the underlying codec software.
|
|
||||||
|
|
||||||
nassertv(t->get_x_size() >= size_x());
|
nassertv(t->get_x_size() >= size_x());
|
||||||
nassertv(t->get_y_size() >= size_y());
|
nassertv(t->get_y_size() >= size_y());
|
||||||
@ -134,17 +140,16 @@ fetch_into_texture(Texture *t, int page) {
|
|||||||
nassertv(t->get_component_width() == 1);
|
nassertv(t->get_component_width() == 1);
|
||||||
nassertv(page < t->get_num_pages());
|
nassertv(page < t->get_num_pages());
|
||||||
|
|
||||||
|
PTA_uchar img;
|
||||||
|
{
|
||||||
|
PStatTimer timer2(_copy_pcollector_ram);
|
||||||
t->set_keep_ram_image(true);
|
t->set_keep_ram_image(true);
|
||||||
PTA_uchar img = t->modify_ram_image();
|
img = t->modify_ram_image();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
||||||
|
|
||||||
PT(Buffer) buffer = fetch_buffer();
|
PStatTimer timer2(_copy_pcollector_copy);
|
||||||
if (buffer == NULL) {
|
|
||||||
// No image available.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->get_x_size() == size_x() && t->get_num_components() == get_num_components()) {
|
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());
|
memcpy(data, buffer->_block, size_x() * size_y() * get_num_components());
|
||||||
|
|
||||||
@ -172,26 +177,22 @@ fetch_into_texture(Texture *t, int page) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
release_buffer(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: MovieVideoCursor::fetch_into_texture_alpha
|
// Function: MovieVideoCursor::apply_to_texture_alpha
|
||||||
// Access: Published, Virtual
|
// Access: Published, Virtual
|
||||||
// Description: Reads the specified video frame into
|
// Description: Copies this buffer's contents into the alpha channel
|
||||||
// the alpha channel of the supplied texture. The
|
// of the supplied texture. The RGB channels of the
|
||||||
// RGB channels of the texture are not touched.
|
// texture are not touched.
|
||||||
//
|
|
||||||
// See fetch_buffer for more details.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void MovieVideoCursor::
|
void MovieVideoCursor::
|
||||||
fetch_into_texture_alpha(Texture *t, int page, int alpha_src) {
|
apply_to_texture_alpha(const Buffer *buffer, Texture *t, int page, int alpha_src) {
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// This generic implementation is layered on fetch_buffer.
|
PStatTimer timer(_copy_pcollector);
|
||||||
// It will work for any derived class, so it is never necessary to
|
|
||||||
// redefine this. However, it may be possible to make a faster
|
|
||||||
// implementation that uses fewer intermediate copies, depending
|
|
||||||
// on the capabilities of the underlying codec software.
|
|
||||||
|
|
||||||
nassertv(t->get_x_size() >= size_x());
|
nassertv(t->get_x_size() >= size_x());
|
||||||
nassertv(t->get_y_size() >= size_y());
|
nassertv(t->get_y_size() >= size_y());
|
||||||
@ -200,17 +201,16 @@ fetch_into_texture_alpha(Texture *t, int page, int alpha_src) {
|
|||||||
nassertv(page < t->get_z_size());
|
nassertv(page < t->get_z_size());
|
||||||
nassertv((alpha_src >= 0) && (alpha_src <= get_num_components()));
|
nassertv((alpha_src >= 0) && (alpha_src <= get_num_components()));
|
||||||
|
|
||||||
PT(Buffer) buffer = fetch_buffer();
|
PTA_uchar img;
|
||||||
if (buffer == NULL) {
|
{
|
||||||
// No image available.
|
PStatTimer timer2(_copy_pcollector_ram);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
t->set_keep_ram_image(true);
|
t->set_keep_ram_image(true);
|
||||||
PTA_uchar img = t->modify_ram_image();
|
img = t->modify_ram_image();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
||||||
|
|
||||||
|
PStatTimer timer2(_copy_pcollector_copy);
|
||||||
int src_stride = size_x() * get_num_components();
|
int src_stride = size_x() * get_num_components();
|
||||||
int dst_stride = t->get_x_size() * 4;
|
int dst_stride = t->get_x_size() * 4;
|
||||||
if (alpha_src == 0) {
|
if (alpha_src == 0) {
|
||||||
@ -234,27 +234,22 @@ fetch_into_texture_alpha(Texture *t, int page, int alpha_src) {
|
|||||||
p += src_stride;
|
p += src_stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
release_buffer(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: MovieVideoCursor::fetch_into_texture_rgb
|
// Function: MovieVideoCursor::apply_to_texture_rgb
|
||||||
// Access: Published, Virtual
|
// Access: Published, Virtual
|
||||||
// Description: Reads the specified video frame into
|
// Description: Copies this buffer's contents into the RGB channels
|
||||||
// the RGB channels of the supplied texture. The alpha
|
// of the supplied texture. The alpha channel of the
|
||||||
// channel of the texture is not touched.
|
// texture is not touched.
|
||||||
//
|
|
||||||
// See fetch_buffer for more details.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void MovieVideoCursor::
|
void MovieVideoCursor::
|
||||||
fetch_into_texture_rgb(Texture *t, int page) {
|
apply_to_texture_rgb(const Buffer *buffer, Texture *t, int page) {
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// This generic implementation is layered on fetch_buffer.
|
PStatTimer timer(_copy_pcollector);
|
||||||
// It will work for any derived class, so it is never necessary to
|
|
||||||
// redefine this. However, it may be possible to make a faster
|
|
||||||
// implementation that uses fewer intermediate copies, depending
|
|
||||||
// on the capabilities of the underlying codec software.
|
|
||||||
|
|
||||||
nassertv(t->get_x_size() >= size_x());
|
nassertv(t->get_x_size() >= size_x());
|
||||||
nassertv(t->get_y_size() >= size_y());
|
nassertv(t->get_y_size() >= size_y());
|
||||||
@ -262,17 +257,16 @@ fetch_into_texture_rgb(Texture *t, int page) {
|
|||||||
nassertv(t->get_component_width() == 1);
|
nassertv(t->get_component_width() == 1);
|
||||||
nassertv(page < t->get_z_size());
|
nassertv(page < t->get_z_size());
|
||||||
|
|
||||||
PT(Buffer) buffer = fetch_buffer();
|
PTA_uchar img;
|
||||||
if (buffer == NULL) {
|
{
|
||||||
// No image available.
|
PStatTimer timer2(_copy_pcollector_ram);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
t->set_keep_ram_image(true);
|
t->set_keep_ram_image(true);
|
||||||
PTA_uchar img = t->modify_ram_image();
|
img = t->modify_ram_image();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
|
||||||
|
|
||||||
|
PStatTimer timer2(_copy_pcollector_copy);
|
||||||
int src_stride = size_x() * get_num_components();
|
int src_stride = size_x() * get_num_components();
|
||||||
int src_width = get_num_components();
|
int src_width = get_num_components();
|
||||||
int dst_stride = t->get_x_size() * 4;
|
int dst_stride = t->get_x_size() * 4;
|
||||||
@ -286,46 +280,6 @@ fetch_into_texture_rgb(Texture *t, int page) {
|
|||||||
data += dst_stride;
|
data += dst_stride;
|
||||||
p += src_stride;
|
p += src_stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
release_buffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: MovieVideoCursor::fetch_buffer
|
|
||||||
// Access: Published, Virtual
|
|
||||||
// Description: Reads the specified video frame and returns it in a
|
|
||||||
// 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.
|
|
||||||
// Otherwise, you may only specify a timestamp
|
|
||||||
// greater than or equal to next_start.
|
|
||||||
//
|
|
||||||
// If the movie reports that it can_seek, it doesn't
|
|
||||||
// mean that it can do so quickly. It may have to
|
|
||||||
// rewind the movie and then fast forward to the
|
|
||||||
// desired location. Only if can_seek_fast returns
|
|
||||||
// true can it seek rapidly.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
PT(MovieVideoCursor::Buffer) MovieVideoCursor::
|
|
||||||
fetch_buffer() {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: MovieVideoCursor::release_buffer
|
|
||||||
// Access: Public, Virtual
|
|
||||||
// Description: Should be called after processing the Buffer object
|
|
||||||
// returned by fetch_buffer(), this releases the Buffer
|
|
||||||
// for future use again.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void MovieVideoCursor::
|
|
||||||
release_buffer(Buffer *buffer) {
|
|
||||||
nassertv(buffer == _standard_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -333,8 +287,7 @@ release_buffer(Buffer *buffer) {
|
|||||||
// Access: Protected
|
// Access: Protected
|
||||||
// Description: May be called by a derived class to return a single
|
// Description: May be called by a derived class to return a single
|
||||||
// standard Buffer object to easily implement
|
// standard Buffer object to easily implement
|
||||||
// fetch_buffer(). The default release_buffer()
|
// fetch_buffer().
|
||||||
// implementation assumes this method is used.
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
MovieVideoCursor::Buffer *MovieVideoCursor::
|
MovieVideoCursor::Buffer *MovieVideoCursor::
|
||||||
get_standard_buffer() {
|
get_standard_buffer() {
|
||||||
@ -397,3 +350,57 @@ fillin(DatagramIterator &scan, BamReader *manager) {
|
|||||||
|
|
||||||
manager->read_pointer(scan); // _source
|
manager->read_pointer(scan); // _source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: MovieVideoCursor::Buffer::Constructor
|
||||||
|
// Access: Public
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
MovieVideoCursor::Buffer::
|
||||||
|
Buffer(size_t block_size) :
|
||||||
|
_block_size(block_size)
|
||||||
|
{
|
||||||
|
_deleted_chain = memory_hook->get_deleted_chain(_block_size);
|
||||||
|
_block = (unsigned char *)_deleted_chain->allocate(_block_size, get_class_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: MovieVideoCursor::Buffer::Destructor
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
MovieVideoCursor::Buffer::
|
||||||
|
~Buffer() {
|
||||||
|
_deleted_chain->deallocate(_block, get_class_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: MovieVideoCursor::Buffer::compare_timestamp
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description: 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.
|
||||||
|
//
|
||||||
|
// Returns 0 if the two buffers are of the same frame,
|
||||||
|
// <0 if this one comes earlier than the other one, and
|
||||||
|
// >0 if the other one comes earlier.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
int MovieVideoCursor::Buffer::
|
||||||
|
compare_timestamp(const Buffer *other) const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: MovieVideoCursor::Buffer::get_timestamp
|
||||||
|
// Access: Published, Virtual
|
||||||
|
// Description: Returns the nearest timestamp value of this
|
||||||
|
// particular buffer. Ideally,
|
||||||
|
// MovieVideoCursor::set_time() for this timestamp would
|
||||||
|
// return this buffer again. This need be defined only
|
||||||
|
// if compare_timestamp() is also defined.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
double MovieVideoCursor::Buffer::
|
||||||
|
get_timestamp() const {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
#include "pointerTo.h"
|
#include "pointerTo.h"
|
||||||
#include "memoryBase.h"
|
#include "memoryBase.h"
|
||||||
|
#include "pStatCollector.h"
|
||||||
|
#include "deletedChain.h"
|
||||||
|
#include "typedReferenceCount.h"
|
||||||
|
|
||||||
class MovieVideo;
|
class MovieVideo;
|
||||||
class FactoryParams;
|
class FactoryParams;
|
||||||
@ -58,22 +61,48 @@ PUBLISHED:
|
|||||||
INLINE bool streaming() const;
|
INLINE bool streaming() const;
|
||||||
void setup_texture(Texture *tex) const;
|
void setup_texture(Texture *tex) const;
|
||||||
|
|
||||||
virtual bool set_time(double time, int loop_count);
|
virtual bool set_time(double timestamp, int loop_count);
|
||||||
virtual void fetch_into_bitbucket();
|
|
||||||
virtual void fetch_into_texture(Texture *t, int page);
|
class Buffer : public TypedReferenceCount {
|
||||||
virtual void fetch_into_texture_rgb(Texture *t, int page);
|
public:
|
||||||
virtual void fetch_into_texture_alpha(Texture *t, int page, int alpha_src);
|
ALLOC_DELETED_CHAIN(Buffer);
|
||||||
|
Buffer(size_t block_size);
|
||||||
|
|
||||||
|
PUBLISHED:
|
||||||
|
virtual ~Buffer();
|
||||||
|
|
||||||
|
virtual int compare_timestamp(const Buffer *other) const;
|
||||||
|
virtual double get_timestamp() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Buffer : public ReferenceCount {
|
|
||||||
public:
|
|
||||||
INLINE Buffer(size_t block_size);
|
|
||||||
INLINE virtual ~Buffer();
|
|
||||||
unsigned char *_block;
|
unsigned char *_block;
|
||||||
size_t _block_size;
|
size_t _block_size;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeletedBufferChain *_deleted_chain;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TypeHandle get_class_type() {
|
||||||
|
return _type_handle;
|
||||||
|
}
|
||||||
|
static void init_type() {
|
||||||
|
TypedReferenceCount::init_type();
|
||||||
|
register_type(_type_handle, "MovieVideoCursor::Buffer",
|
||||||
|
TypedReferenceCount::get_class_type());
|
||||||
|
}
|
||||||
|
virtual TypeHandle get_type() const {
|
||||||
|
return get_class_type();
|
||||||
|
}
|
||||||
|
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TypeHandle _type_handle;
|
||||||
};
|
};
|
||||||
virtual PT(Buffer) fetch_buffer();
|
virtual PT(Buffer) fetch_buffer();
|
||||||
virtual void release_buffer(Buffer *buffer);
|
|
||||||
|
virtual void apply_to_texture(const Buffer *buffer, Texture *t, int page);
|
||||||
|
virtual void apply_to_texture_rgb(const Buffer *buffer, Texture *t, int page);
|
||||||
|
virtual void apply_to_texture_alpha(const Buffer *buffer, Texture *t, int page, int alpha_src);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Buffer *get_standard_buffer();
|
Buffer *get_standard_buffer();
|
||||||
@ -93,6 +122,10 @@ protected:
|
|||||||
|
|
||||||
PT(Buffer) _standard_buffer;
|
PT(Buffer) _standard_buffer;
|
||||||
|
|
||||||
|
static PStatCollector _copy_pcollector;
|
||||||
|
static PStatCollector _copy_pcollector_ram;
|
||||||
|
static PStatCollector _copy_pcollector_copy;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void write_datagram(BamWriter *manager, Datagram &dg);
|
virtual void write_datagram(BamWriter *manager, Datagram &dg);
|
||||||
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
|
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
|
||||||
@ -108,6 +141,7 @@ public:
|
|||||||
TypedWritableReferenceCount::init_type();
|
TypedWritableReferenceCount::init_type();
|
||||||
register_type(_type_handle, "MovieVideoCursor",
|
register_type(_type_handle, "MovieVideoCursor",
|
||||||
TypedWritableReferenceCount::get_class_type());
|
TypedWritableReferenceCount::get_class_type());
|
||||||
|
Buffer::init_type();
|
||||||
}
|
}
|
||||||
virtual TypeHandle get_type() const {
|
virtual TypeHandle get_type() const {
|
||||||
return get_class_type();
|
return get_class_type();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user