Panda3D
movieTexture.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file movieTexture.cxx
10  * @author jyelon
11  * @date 2007-08-01
12  */
13 
14 #include "pandabase.h"
15 
16 #ifdef HAVE_AUDIO
17 
18 #include "movieVideo.h"
19 #include "movieVideoCursor.h"
20 #include "movieTypeRegistry.h"
21 #include "movieTexture.h"
22 #include "clockObject.h"
23 #include "config_gobj.h"
24 #include "config_grutil.h"
25 #include "bamCacheRecord.h"
26 #include "bamReader.h"
27 #include "bamWriter.h"
28 #include "audioSound.h"
29 
30 #include <math.h>
31 
32 TypeHandle MovieTexture::_type_handle;
33 
34 /**
35  * Creates a blank movie texture. Movies must be added using do_read_one or
36  * do_load_one.
37  */
38 MovieTexture::
39 MovieTexture(const std::string &name) :
40  Texture(name)
41 {
42 }
43 
44 /**
45  * Creates a texture playing the specified movie.
46  */
47 MovieTexture::
48 MovieTexture(MovieVideo *video) :
49  Texture(video->get_name())
50 {
51  Texture::CDWriter cdata_tex(Texture::_cycler, true);
52  do_load_one(cdata_tex, video->open(), nullptr, 0, LoaderOptions());
53 }
54 
55 /**
56  * xxx
57  */
58 MovieTexture::CData::
59 CData() :
60  _video_width(1),
61  _video_height(1),
62  _video_length(1.0),
63  _clock(0.0),
64  _playing(false),
65  _loop_count(1),
66  _play_rate(1.0),
67  _has_offset(false)
68 {
69 }
70 
71 /**
72  * xxx
73  */
74 MovieTexture::CData::
75 CData(const CData &copy) :
76  _pages(copy._pages),
77  _video_width(copy._video_width),
78  _video_height(copy._video_height),
79  _video_length(copy._video_length),
80  _clock(0.0),
81  _playing(false),
82  _loop_count(1),
83  _play_rate(1.0),
84  _has_offset(false)
85 {
86 }
87 
88 /**
89  * xxx
90  */
91 CycleData *MovieTexture::CData::
92 make_copy() const {
93  return new CData(*this);
94 }
95 
96 /**
97  * xxx
98  */
99 MovieTexture::
100 ~MovieTexture() {
101  clear();
102 }
103 
104 /**
105  * May be called prior to calling read_txo() or any bam-related Texture-
106  * creating callback, to ensure that the proper dynamic libraries for a
107  * Texture of the current class type, and the indicated filename, have been
108  * already loaded.
109  *
110  * This is a low-level function that should not normally need to be called
111  * directly by the user.
112  *
113  * Note that for best results you must first create a Texture object of the
114  * appropriate class type for your filename, for instance with
115  * TexturePool::make_texture().
116  */
117 void MovieTexture::
118 ensure_loader_type(const Filename &filename) {
119  // Creating a MovieVideo of the appropriate type is a slightly hacky way to
120  // ensure the appropriate libraries are loaded. We can let the MovieVideo
121  // we create immediately destruct.
123  PT(MovieVideo) video = reg->make_video(filename);
124 }
125 
126 /**
127  * A factory function to make a new MovieTexture, used to pass to the
128  * TexturePool.
129  */
130 PT(Texture) MovieTexture::
131 make_texture() {
132  return new MovieTexture("");
133 }
134 
135 /**
136  * Resizes the texture, and adjusts the format, based on the source movies.
137  * The resulting texture will be large enough to hold all the videos.
138  *
139  * Assumes the lock is already held.
140  */
141 void MovieTexture::
142 do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex, const LoaderOptions &options) {
143  int x_max = 1;
144  int y_max = 1;
145  bool rgb = false;
146  bool alpha = false;
147  double len = 0.0;
148 
149  for (size_t i = 0; i < cdata->_pages.size(); ++i) {
150  MovieVideoCursor *t = cdata->_pages[i]._color;
151  if (t) {
152  if (t->size_x() > x_max) x_max = t->size_x();
153  if (t->size_y() > y_max) y_max = t->size_y();
154  if (t->length() > len) len = t->length();
155  if (t->get_num_components() >= 3) rgb=true;
156  if (t->get_num_components() == 4 || t->get_num_components() == 2) alpha=true;
157  }
158  t = cdata->_pages[i]._alpha;
159  if (t) {
160  if (t->size_x() > x_max) x_max = t->size_x();
161  if (t->size_y() > y_max) y_max = t->size_y();
162  if (t->length() > len) len = t->length();
163  alpha = true;
164  }
165  }
166 
167  cdata->_video_width = x_max;
168  cdata->_video_height = y_max;
169  cdata->_video_length = len;
170 
171  do_adjust_this_size(cdata_tex, x_max, y_max, get_name(), true);
172 
173  int num_components = (rgb ? 3 : 1) + alpha;
174  do_reconsider_image_properties(cdata_tex, x_max, y_max, num_components,
175  T_unsigned_byte, cdata->_pages.size(),
176  options);
177  cdata_tex->_orig_file_x_size = cdata->_video_width;
178  cdata_tex->_orig_file_y_size = cdata->_video_height;
179 
180  do_set_pad_size(cdata_tex,
181  std::max(cdata_tex->_x_size - cdata_tex->_orig_file_x_size, 0),
182  std::max(cdata_tex->_y_size - cdata_tex->_orig_file_y_size, 0),
183  0);
184 }
185 
186 /**
187  * Works like adjust_size, but also considers the texture class. Movie
188  * textures, for instance, always pad outwards, never scale down.
189  */
190 bool MovieTexture::
191 do_adjust_this_size(const Texture::CData *cdata_tex,
192  int &x_size, int &y_size, const std::string &name,
193  bool for_padding) const {
194  AutoTextureScale ats = do_get_auto_texture_scale(cdata_tex);
195  if (ats != ATS_none) {
196  ats = ATS_pad;
197  }
198 
199  return adjust_size(x_size, y_size, name, for_padding, ats);
200 }
201 
202 /**
203  * Combines a color and alpha video image from the two indicated filenames.
204  * Both must be the same kind of video with similar properties.
205  */
206 bool MovieTexture::
207 do_read_one(Texture::CData *cdata_tex,
208  const Filename &fullpath, const Filename &alpha_fullpath,
209  int z, int n, int primary_file_num_channels, int alpha_file_channel,
210  const LoaderOptions &options,
211  bool header_only, BamCacheRecord *record) {
212  nassertr(n == 0, false);
213  if (!do_reconsider_z_size(cdata_tex, z, options)) {
214  return false;
215  }
216  nassertr(z >= 0 && z < cdata_tex->_z_size * cdata_tex->_num_views, false);
217 
218  if (record != nullptr) {
219  record->add_dependent_file(fullpath);
220  }
221 
222  PT(MovieVideoCursor) color;
223  PT(MovieVideoCursor) alpha;
224 
225  color = MovieVideo::get(fullpath)->open();
226  if (color == nullptr) {
227  return false;
228  }
229  if (!alpha_fullpath.empty()) {
230  alpha = MovieVideo::get(alpha_fullpath)->open();
231  if (alpha == nullptr) {
232  return false;
233  }
234  }
235 
236  if (z == 0) {
237  if (!has_name()) {
238  set_name(fullpath.get_basename_wo_extension());
239  }
240  // Don't use has_filename() here, it will cause a deadlock
241  if (cdata_tex->_filename.empty()) {
242  cdata_tex->_filename = fullpath;
243  cdata_tex->_alpha_filename = alpha_fullpath;
244  }
245 
246  cdata_tex->_fullpath = fullpath;
247  cdata_tex->_alpha_fullpath = alpha_fullpath;
248  }
249 
250  cdata_tex->_primary_file_num_channels = primary_file_num_channels;
251  cdata_tex->_alpha_file_channel = alpha_file_channel;
252 
253  if (!do_load_one(cdata_tex, color, alpha, z, options)) {
254  return false;
255  }
256 
257  cdata_tex->_loaded_from_image = true;
258  set_loop(true);
259  play();
260  return true;
261 }
262 
263 /**
264  * Loads movie objects into the texture.
265  */
266 bool MovieTexture::
267 do_load_one(Texture::CData *cdata_tex,
268  PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z,
269  const LoaderOptions &options) {
270  CDWriter cdata(_cycler);
271  cdata->_pages.resize(z + 1);
272  cdata->_pages[z]._color = color;
273  cdata->_pages[z]._alpha = alpha;
274  do_recalculate_image_properties(cdata, cdata_tex, options);
275 
276  // Make sure the image data is initially black, which is nice for padded
277  // textures.
278  PTA_uchar image = make_ram_image();
279  memset(image.p(), 0, image.size());
280 
281  return true;
282 }
283 
284 /**
285  * Loading a static image into a MovieTexture is an error.
286  */
287 bool MovieTexture::
288 do_load_one(Texture::CData *cdata_tex,
289  const PNMImage &pnmimage, const std::string &name, int z, int n,
290  const LoaderOptions &options) {
291  grutil_cat.error() << "You cannot load a static image into a MovieTexture\n";
292  return false;
293 }
294 
295 /**
296  * Loading a static image into a MovieTexture is an error.
297  */
298 bool MovieTexture::
299 do_load_one(Texture::CData *cdata_tex,
300  const PfmFile &pfm, const std::string &name, int z, int n,
301  const LoaderOptions &options) {
302  grutil_cat.error() << "You cannot load a static image into a MovieTexture\n";
303  return false;
304 }
305 
306 /**
307  * Called internally by do_reconsider_z_size() to allocate new memory in
308  * _ram_images[0] for the new number of pages.
309  *
310  * Assumes the lock is already held.
311  */
312 void MovieTexture::
313 do_allocate_pages(Texture::CData *cdata_tex) {
314  // We don't actually do anything here; the allocation is made in
315  // do_load_one(), above.
316 }
317 
318 /**
319  * Should be overridden by derived classes to return true if cull_callback()
320  * has been defined. Otherwise, returns false to indicate cull_callback()
321  * does not need to be called for this node during the cull traversal.
322  */
323 bool MovieTexture::
324 has_cull_callback() const {
325  return true;
326 }
327 
328 /**
329  * This function will be called during the cull traversal to update the
330  * MovieTexture. This update consists of fetching the next video frame from
331  * the underlying MovieVideo sources. The MovieVideo object belongs to the
332  * cull thread.
333  */
334 bool MovieTexture::
335 cull_callback(CullTraverser *, const CullTraverserData &) const {
336  Texture::CDReader cdata_tex(Texture::_cycler);
337  CDReader cdata(_cycler);
338 
339  if (!cdata->_has_offset) {
340  // If we don't have a previously-computed timestamp (offset) cached, then
341  // compute a new one.
342  double offset;
343  int true_loop_count = 1;
344  if (cdata->_synchronize != nullptr) {
345  offset = cdata->_synchronize->get_time();
346  } else {
347  // Calculate the cursor position modulo the length of the movie.
349  offset = cdata->_clock;
350  if (cdata->_playing) {
351  offset += now * cdata->_play_rate;
352  }
353  true_loop_count = cdata->_loop_count;
354  }
355  ((CData *)cdata.p())->_offset = offset;
356  ((CData *)cdata.p())->_true_loop_count = true_loop_count;
357  ((CData *)cdata.p())->_has_offset = true;
358  }
359 
360  bool in_sync = do_update_frames(cdata);
361  if (!in_sync) {
362  // If it didn't successfully sync, try again--once. The second time it
363  // might be able to fill in some more recent frames.
364  in_sync = do_update_frames(cdata);
365  }
366 
367  if (in_sync) {
368  // Now go back through and apply all the frames to the texture.
369  Pages::const_iterator pi;
370  for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
371  const VideoPage &page = (*pi);
372  MovieVideoCursor *color = page._color;
373  MovieVideoCursor *alpha = page._alpha;
374  size_t i = pi - cdata->_pages.begin();
375 
376  if (color != nullptr && alpha != nullptr) {
377  color->apply_to_texture_rgb(page._cbuffer, (MovieTexture*)this, i);
378  alpha->apply_to_texture_alpha(page._abuffer, (MovieTexture*)this, i, cdata_tex->_alpha_file_channel);
379 
380  } else if (color != nullptr) {
381  color->apply_to_texture(page._cbuffer, (MovieTexture*)this, i);
382  }
383 
384  ((VideoPage &)page)._cbuffer.clear();
385  ((VideoPage &)page)._abuffer.clear();
386  }
387 
388  // Clear the cached offset so we can update the frame next time.
389  ((CData *)cdata.p())->_has_offset = false;
390  }
391 
392  return true;
393 }
394 
395 /**
396  * Returns a new copy of the same Texture. This copy, if applied to geometry,
397  * will be copied into texture as a separate texture from the original, so it
398  * will be duplicated in texture memory (and may be independently modified if
399  * desired).
400  *
401  * If the Texture is a MovieTexture, the resulting duplicate may be animated
402  * independently of the original.
403  */
404 PT(Texture) MovieTexture::
405 make_copy_impl() const {
406  Texture::CDReader cdata_tex(Texture::_cycler);
407  CDReader cdata(_cycler);
408  PT(MovieTexture) copy = new MovieTexture(get_name());
409  Texture::CDWriter cdata_copy_tex(copy->Texture::_cycler, true);
410  CDWriter cdata_copy(copy->_cycler, true);
411  copy->do_assign(cdata_copy, cdata_copy_tex, this, cdata, cdata_tex);
412 
413  return copy;
414 }
415 
416 /**
417  * Implements make_copy().
418  */
419 void MovieTexture::
420 do_assign(CData *cdata, Texture::CData *cdata_tex, const MovieTexture *copy,
421  const CData *cdata_copy, const Texture::CData *cdata_copy_tex) {
422  Texture::do_assign(cdata_tex, copy, cdata_copy_tex);
423 
426  color.resize(cdata_copy->_pages.size());
427  alpha.resize(cdata_copy->_pages.size());
428  for (int i=0; i<(int)(color.size()); i++) {
429  color[i] = cdata_copy->_pages[i]._color;
430  alpha[i] = cdata_copy->_pages[i]._alpha;
431  }
432 
433  cdata->_pages.resize(color.size());
434  for (int i=0; i<(int)(color.size()); i++) {
435  if (color[i]) {
436  cdata->_pages[i]._color = color[i]->get_source()->open();
437  }
438  if (alpha[i]) {
439  cdata->_pages[i]._alpha = alpha[i]->get_source()->open();
440  }
441  }
442  do_recalculate_image_properties(cdata, cdata_tex, LoaderOptions());
443 }
444 
445 /**
446  * A MovieTexture must always keep its ram image, since there is no way to
447  * reload it from the source MovieVideo.
448  */
449 void MovieTexture::
450 do_reload_ram_image(Texture::CData *cdata, bool allow_compression) {
451  // A MovieTexture should never dump its RAM image. Therefore, this is not
452  // needed.
453 }
454 
455 /**
456  * A MovieTexture must always keep its ram image, since there is no way to
457  * reload it from the source MovieVideo.
458  */
459 bool MovieTexture::
460 get_keep_ram_image() const {
461  // A MovieTexture should never dump its RAM image.
462  return true;
463 }
464 
465 /**
466  * Returns true if there is a rawdata image that we have available to write to
467  * the bam stream. For a normal Texture, this is the same thing as
468  * do_has_ram_image(), but a movie texture might define it differently.
469  */
470 bool MovieTexture::
471 do_has_bam_rawdata(const Texture::CData *cdata) const {
472  return true;
473 }
474 
475 /**
476  * If do_has_bam_rawdata() returned false, this attempts to reload the rawdata
477  * image if possible.
478  */
479 void MovieTexture::
480 do_get_bam_rawdata(Texture::CData *cdata) {
481 }
482 
483 /**
484  * Returns true if we can safely call do_unlock_and_reload_ram_image() in
485  * order to make the image available, or false if we shouldn't do this
486  * (because we know from a priori knowledge that it wouldn't work anyway).
487  */
488 bool MovieTexture::
489 do_can_reload(const Texture::CData *cdata) const {
490  return false;
491 }
492 
493 /**
494  * Start playing the movie from where it was last paused. Has no effect if
495  * the movie is not paused, or if the movie's cursor is already at the end.
496  */
497 void MovieTexture::
498 restart() {
499  CDWriter cdata(_cycler);
500  if (!cdata->_playing) {
502  cdata->_clock = cdata->_clock - (now * cdata->_play_rate);
503  cdata->_playing = true;
504  }
505 }
506 
507 /**
508  * Stops a currently playing or looping movie right where it is. The movie's
509  * cursor remains frozen at the point where it was stopped.
510  */
511 void MovieTexture::
512 stop() {
513  CDWriter cdata(_cycler);
514  if (cdata->_playing) {
516  cdata->_clock = cdata->_clock + (now * cdata->_play_rate);
517  cdata->_playing = false;
518  }
519 }
520 
521 /**
522  * Plays the movie from the beginning.
523  */
524 void MovieTexture::
525 play() {
526  CDWriter cdata(_cycler);
528  cdata->_clock = 0.0 - (now * cdata->_play_rate);
529  cdata->_playing = true;
530 }
531 
532 /**
533  * Sets the movie's cursor.
534  */
535 void MovieTexture::
536 set_time(double t) {
537  CDWriter cdata(_cycler);
538  t = std::min(cdata->_video_length, std::max(0.0, t));
539  if (cdata->_playing) {
541  cdata->_clock = t - (now * cdata->_play_rate);
542  } else {
543  cdata->_clock = t;
544  }
545 }
546 
547 /**
548  * Returns the current value of the movie's cursor. If the movie's loop count
549  * is greater than one, then its length is effectively multiplied for the
550  * purposes of this function. In other words, the return value will be in the
551  * range 0.0 to (length * loopcount).
552  */
553 double MovieTexture::
554 get_time() const {
555  CDReader cdata(_cycler);
556  double clock = cdata->_clock;
557  if (cdata->_playing) {
559  clock += (now * cdata->_play_rate);
560  }
561  return clock;
562 }
563 
564 /**
565  * If true, sets the movie's loop count to 1 billion. If false, sets the
566  * movie's loop count to one.
567  */
568 void MovieTexture::
569 set_loop(bool loop) {
570  set_loop_count(loop ? 0:1);
571 }
572 
573 /**
574  * Returns true if the movie's loop count is not equal to one.
575  */
576 bool MovieTexture::
577 get_loop() const {
578  CDReader cdata(_cycler);
579  return (cdata->_loop_count == 0);
580 }
581 
582 /**
583  * Sets the movie's loop count to the desired value.
584  */
585 void MovieTexture::
586 set_loop_count(int n) {
587  CDWriter cdata(_cycler);
588  cdata->_loop_count = n;
589 }
590 
591 /**
592  * Returns the movie's loop count.
593  */
594 int MovieTexture::
595 get_loop_count() const {
596  CDReader cdata(_cycler);
597  return cdata->_loop_count;
598 }
599 
600 /**
601  * Sets the movie's play-rate. This is the speed at which the movie's cursor
602  * advances. The default is to advance 1.0 movie-seconds per real-time
603  * second.
604  */
605 void MovieTexture::
606 set_play_rate(double rate) {
607  CDWriter cdata(_cycler);
608  if (cdata->_playing) {
610  cdata->_clock += (now * cdata->_play_rate);
611  cdata->_play_rate = rate;
612  cdata->_clock -= (now * cdata->_play_rate);
613  } else {
614  cdata->_play_rate = rate;
615  }
616 }
617 
618 /**
619  * Gets the movie's play-rate.
620  */
621 double MovieTexture::
622 get_play_rate() const {
623  CDReader cdata(_cycler);
624  return cdata->_play_rate;
625 }
626 
627 /**
628  * Returns true if the movie's cursor is advancing.
629  */
630 bool MovieTexture::
631 is_playing() const {
632  CDReader cdata(_cycler);
633  return cdata->_playing;
634 }
635 
636 /**
637  * Synchronize this texture to a sound. Typically, you would load the texture
638  * and the sound from the same AVI file.
639  */
640 void MovieTexture::
641 synchronize_to(AudioSound *s) {
642  CDWriter cdata(_cycler);
643  cdata->_synchronize = s;
644 }
645 
646 /**
647  * Stop synchronizing with a sound.
648  */
649 void MovieTexture::
650 unsynchronize() {
651  CDWriter cdata(_cycler);
652  cdata->_synchronize = nullptr;
653 }
654 
655 
656 /**
657  * Called internally to sync all of the frames to the current time. Returns
658  * true if successful, or false of some of the frames are out-of-date with
659  * each other.
660  */
661 bool MovieTexture::
662 do_update_frames(const CData *cdata) const {
663  // Throughout this method, we cast the VideoPage to non-const to update the
664  // _cbuffer or _abuffer member. We can do this safely because this is only
665  // a transparent cache value.
666  nassertr(cdata->_has_offset, false);
667 
668  // First, go through and get all of the current frames.
669  Pages::const_iterator pi;
670  for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
671  const VideoPage &page = (*pi);
672  MovieVideoCursor *color = page._color;
673  MovieVideoCursor *alpha = page._alpha;
674 
675  if (color != nullptr && page._cbuffer == nullptr) {
676  if (color->set_time(cdata->_offset, cdata->_true_loop_count)) {
677  ((VideoPage &)page)._cbuffer = color->fetch_buffer();
678  }
679  }
680  if (alpha != nullptr && page._abuffer == nullptr) {
681  if (alpha->set_time(cdata->_offset, cdata->_true_loop_count)) {
682  ((VideoPage &)page)._abuffer = alpha->fetch_buffer();
683  }
684  }
685  }
686 
687  if (!movies_sync_pages) {
688  // If movies-sync-pages is configured off, we don't care about syncing the
689  // pages, and we always return true here to render the pages we've got.
690  return true;
691  }
692 
693  // Now make sure all of the frames are in sync with each other.
694  bool in_sync = true;
695  bool any_frames = false;
696  bool any_dropped = false;
697  PT(MovieVideoCursor::Buffer) newest;
698  for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
699  const VideoPage &page = (*pi);
700  if (page._cbuffer == nullptr) {
701  if (page._color != nullptr) {
702  // This page isn't ready at all.
703  in_sync = false;
704  }
705  } else {
706  nassertr(page._color != nullptr, true);
707  any_frames = true;
708  if (newest == nullptr) {
709  newest = page._cbuffer;
710  } else {
711  int ref = newest->compare_timestamp(page._cbuffer);
712  if (ref != 0) {
713  // This page is ready, but out-of-date.
714  in_sync = false;
715  any_dropped = true;
716  if (ref < 0) {
717  newest = page._cbuffer;
718  }
719  }
720  }
721  }
722  if (page._abuffer == nullptr) {
723  if (page._alpha != nullptr) {
724  in_sync = false;
725  }
726  } else {
727  nassertr(page._alpha != nullptr, true);
728  any_frames = true;
729  if (newest == nullptr) {
730  newest = page._abuffer;
731  } else {
732  int ref = newest->compare_timestamp(page._abuffer);
733  if (ref != 0) {
734  in_sync = false;
735  any_dropped = true;
736  if (ref < 0) {
737  newest = page._abuffer;
738  }
739  }
740  }
741  }
742  }
743 
744  if (!any_frames) {
745  // If no frames at all are ready yet, just carry on.
746  return true;
747  }
748 
749  if (!in_sync) {
750  // If we're not in sync, throw away pages that are older than the newest
751  // available frame.
752  if (newest != nullptr) {
753  Pages::const_iterator pi;
754  for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
755  const VideoPage &page = (*pi);
756  if (page._cbuffer != nullptr && newest->compare_timestamp(page._cbuffer) > 0) {
757  ((VideoPage &)page)._cbuffer.clear();
758  any_dropped = true;
759  }
760  if (page._abuffer != nullptr && newest->compare_timestamp(page._abuffer) > 0) {
761  ((VideoPage &)page)._abuffer.clear();
762  any_dropped = true;
763  }
764  }
765 
766  if (any_dropped) {
767  // If we dropped one or more frames for being out-of-sync, implying
768  // that compare_timestamp() is implemented, then we also want to
769  // update our internal offset value so that future frames will get the
770  // same value.
771  ((CData *)cdata)->_offset = newest->get_timestamp();
772  }
773  }
774  }
775 
776  return in_sync;
777 }
778 
779 /**
780  * Factory method to generate a Texture object
781  */
782 void MovieTexture::
783 register_with_read_factory() {
784  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
785 }
786 
787 /**
788  * Factory method to generate a MovieTexture object
789  */
790 TypedWritable *MovieTexture::
791 make_from_bam(const FactoryParams &params) {
792  PT(MovieTexture) dummy = new MovieTexture("");
793  return dummy->make_this_from_bam(params);
794 }
795 
796 /**
797  * Receives an array of pointers, one for each time manager->read_pointer()
798  * was called in fillin(). Returns the number of pointers processed.
799  */
800 int MovieTexture::
801 complete_pointers(TypedWritable **p_list, BamReader *manager) {
802  int pi = Texture::complete_pointers(p_list, manager);
803 
804  CDWriter cdata(_cycler);
805  size_t num_pages = cdata->_pages.size();
806  for (size_t n = 0; n < num_pages; ++n) {
807  VideoPage &page = cdata->_pages[n];
808  page._color = DCAST(MovieVideoCursor, p_list[pi++]);
809  page._alpha = DCAST(MovieVideoCursor, p_list[pi++]);
810  }
811 
812  return pi;
813 }
814 
815 /**
816  * Writes the rawdata part of the texture to the Datagram.
817  */
818 void MovieTexture::
819 do_write_datagram_rawdata(Texture::CData *cdata_tex, BamWriter *manager, Datagram &dg) {
820  CDReader cdata(_cycler);
821 
822  dg.add_uint16(cdata_tex->_z_size);
823  if (manager->get_file_minor_ver() >= 26) {
824  dg.add_uint16(cdata_tex->_num_views);
825  }
826 
827  nassertv(cdata->_pages.size() == (size_t)(cdata_tex->_z_size * cdata_tex->_num_views));
828  for (size_t n = 0; n < cdata->_pages.size(); ++n) {
829  const VideoPage &page = cdata->_pages[n];
830  manager->write_pointer(dg, page._color);
831  manager->write_pointer(dg, page._alpha);
832  }
833 }
834 
835 /**
836  * Reads in the part of the Texture that was written with
837  * do_write_datagram_rawdata().
838  */
839 void MovieTexture::
840 do_fillin_rawdata(Texture::CData *cdata_tex, DatagramIterator &scan, BamReader *manager) {
841  CDWriter cdata(_cycler);
842 
843  cdata_tex->_z_size = scan.get_uint16();
844  cdata_tex->_num_views = 1;
845  if (manager->get_file_minor_ver() >= 26) {
846  cdata_tex->_num_views = scan.get_uint16();
847  }
848 
849  size_t num_pages = (size_t)(cdata_tex->_z_size * cdata_tex->_num_views);
850  cdata->_pages.reserve(num_pages);
851  for (size_t n = 0; n < num_pages; ++n) {
852  cdata->_pages.push_back(VideoPage());
853  manager->read_pointer(scan); // page._color
854  manager->read_pointer(scan); // page._alpha
855  }
856 
857  // We load one or more MovieVideoCursors during the above loop. We need a
858  // finalize callback so we can initialize ourselves once those cursors have
859  // been read completely.
860  manager->register_finalize(this);
861 }
862 
863 /**
864  * Called by the BamReader to perform any final actions needed for setting up
865  * the object after all objects have been read and all pointers have been
866  * completed.
867  */
868 void MovieTexture::
869 finalize(BamReader *manager) {
870  Texture::CDWriter cdata_tex(Texture::_cycler);
871  CDWriter cdata(_cycler);
872 
873  // Insist that each of our video pages gets finalized before we do.
874  size_t num_pages = cdata->_pages.size();
875  for (size_t n = 0; n < num_pages; ++n) {
876  VideoPage &page = cdata->_pages[n];
877  manager->finalize_now(page._color);
878  manager->finalize_now(page._alpha);
879  }
880 
881  do_recalculate_image_properties(cdata, cdata_tex, LoaderOptions());
882 
883  set_loaded_from_image();
884  set_loop(true);
885  play();
886 }
887 
888 #endif // HAVE_AUDIO
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
This class records the different types of MovieAudio and MovieVideo that are available for loading.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
virtual void apply_to_texture_rgb(const Buffer *buffer, Texture *t, int page)
Copies this buffer's contents into the RGB channels of the supplied texture.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
virtual bool set_time(double timestamp, int loop_count)
Updates the cursor to the indicated time.
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition: pfmFile.h:31
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
A MovieVideo is actually any source that provides a sequence of video frames.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
virtual void apply_to_texture(const Buffer *buffer, Texture *t, int page)
Stores this buffer's contents in the indicated texture.
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:808
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void finalize_now(TypedWritable *whom)
Forces the finalization of a particular object.
Definition: bamReader.cxx:897
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
static MovieTypeRegistry * get_global_ptr()
Returns a pointer to the global MovieTypeRegistry instance.
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
A MovieVideo is actually any source that provides a sequence of video frames.
Definition: movieVideo.h:38
virtual void apply_to_texture_alpha(const Buffer *buffer, Texture *t, int page, int alpha_src)
Copies this buffer's contents into the alpha channel of the supplied texture.
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record.
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.