00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "pandabase.h"
00016
00017 #ifdef HAVE_AUDIO
00018
00019 #include "movieVideo.h"
00020 #include "movieVideoCursor.h"
00021 #include "movieTexture.h"
00022 #include "clockObject.h"
00023 #include "config_gobj.h"
00024 #include "config_grutil.h"
00025 #include "bamCacheRecord.h"
00026 #include "bamReader.h"
00027 #include "bamWriter.h"
00028 #include "math.h"
00029 #include "audioSound.h"
00030
00031 TypeHandle MovieTexture::_type_handle;
00032
00033
00034
00035
00036
00037
00038
00039 MovieTexture::
00040 MovieTexture(const string &name) :
00041 Texture(name)
00042 {
00043 }
00044
00045
00046
00047
00048
00049
00050 MovieTexture::
00051 MovieTexture(MovieVideo *video) :
00052 Texture(video->get_name())
00053 {
00054 Texture::CDWriter cdata_tex(Texture::_cycler, true);
00055 do_load_one(cdata_tex, video->open(), NULL, 0, LoaderOptions());
00056 }
00057
00058
00059
00060
00061
00062
00063 MovieTexture::CData::
00064 CData() :
00065 _video_width(1),
00066 _video_height(1),
00067 _video_length(1.0),
00068 _clock(0.0),
00069 _playing(false),
00070 _loop_count(1),
00071 _play_rate(1.0),
00072 _has_offset(false)
00073 {
00074 }
00075
00076
00077
00078
00079
00080
00081 MovieTexture::CData::
00082 CData(const CData ©) :
00083 _pages(copy._pages),
00084 _video_width(copy._video_width),
00085 _video_height(copy._video_height),
00086 _video_length(copy._video_length),
00087 _clock(0.0),
00088 _playing(false),
00089 _loop_count(1),
00090 _play_rate(1.0),
00091 _has_offset(false)
00092 {
00093 }
00094
00095
00096
00097
00098
00099
00100 CycleData *MovieTexture::CData::
00101 make_copy() const {
00102 return new CData(*this);
00103 }
00104
00105
00106
00107
00108
00109
00110
00111 MovieTexture::
00112 MovieTexture(const MovieTexture ©) :
00113 Texture(copy)
00114 {
00115 nassertv(false);
00116 }
00117
00118
00119
00120
00121
00122
00123 MovieTexture::
00124 ~MovieTexture() {
00125 clear();
00126 }
00127
00128
00129
00130
00131
00132
00133
00134 PT(Texture) MovieTexture::
00135 make_texture() {
00136 return new MovieTexture("");
00137 }
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148 void MovieTexture::
00149 do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex, const LoaderOptions &options) {
00150 int x_max = 1;
00151 int y_max = 1;
00152 bool alpha = false;
00153 double len = 0.0;
00154
00155 for (size_t i = 0; i < cdata->_pages.size(); ++i) {
00156 MovieVideoCursor *t = cdata->_pages[i]._color;
00157 if (t) {
00158 if (t->size_x() > x_max) x_max = t->size_x();
00159 if (t->size_y() > y_max) y_max = t->size_y();
00160 if (t->length() > len) len = t->length();
00161 if (t->get_num_components() == 4) alpha=true;
00162 }
00163 t = cdata->_pages[i]._alpha;
00164 if (t) {
00165 if (t->size_x() > x_max) x_max = t->size_x();
00166 if (t->size_y() > y_max) y_max = t->size_y();
00167 if (t->length() > len) len = t->length();
00168 alpha = true;
00169 }
00170 }
00171
00172 cdata->_video_width = x_max;
00173 cdata->_video_height = y_max;
00174 cdata->_video_length = len;
00175
00176 do_adjust_this_size(cdata_tex, x_max, y_max, get_name(), true);
00177
00178 do_reconsider_image_properties(cdata_tex, x_max, y_max, alpha?4:3,
00179 T_unsigned_byte, cdata->_pages.size(),
00180 options);
00181 cdata_tex->_orig_file_x_size = cdata->_video_width;
00182 cdata_tex->_orig_file_y_size = cdata->_video_height;
00183
00184 do_set_pad_size(cdata_tex,
00185 max(cdata_tex->_x_size - cdata_tex->_orig_file_x_size, 0),
00186 max(cdata_tex->_y_size - cdata_tex->_orig_file_y_size, 0),
00187 0);
00188 }
00189
00190
00191
00192
00193
00194
00195
00196
00197 bool MovieTexture::
00198 do_adjust_this_size(const Texture::CData *cdata_tex,
00199 int &x_size, int &y_size, const string &name,
00200 bool for_padding) const {
00201 AutoTextureScale ats = do_get_auto_texture_scale(cdata_tex);
00202 if (ats != ATS_none) {
00203 ats = ATS_pad;
00204 }
00205
00206 return adjust_size(x_size, y_size, name, for_padding, ats);
00207 }
00208
00209
00210
00211
00212
00213
00214
00215
00216 bool MovieTexture::
00217 do_read_one(Texture::CData *cdata_tex,
00218 const Filename &fullpath, const Filename &alpha_fullpath,
00219 int z, int n, int primary_file_num_channels, int alpha_file_channel,
00220 const LoaderOptions &options,
00221 bool header_only, BamCacheRecord *record) {
00222 nassertr(n == 0, false);
00223 if (!do_reconsider_z_size(cdata_tex, z, options)) {
00224 return false;
00225 }
00226 nassertr(z >= 0 && z < cdata_tex->_z_size * cdata_tex->_num_views, false);
00227
00228 if (record != (BamCacheRecord *)NULL) {
00229 record->add_dependent_file(fullpath);
00230 }
00231
00232 PT(MovieVideoCursor) color;
00233 PT(MovieVideoCursor) alpha;
00234
00235 color = MovieVideo::get(fullpath)->open();
00236 if (color == 0) {
00237 return false;
00238 }
00239 if (!alpha_fullpath.empty()) {
00240 alpha = MovieVideo::get(alpha_fullpath)->open();
00241 if (alpha == 0) {
00242 return false;
00243 }
00244 }
00245
00246 if (z == 0) {
00247 if (!has_name()) {
00248 set_name(fullpath.get_basename_wo_extension());
00249 }
00250
00251 if (cdata_tex->_filename.empty()) {
00252 cdata_tex->_filename = fullpath;
00253 cdata_tex->_alpha_filename = alpha_fullpath;
00254 }
00255
00256 cdata_tex->_fullpath = fullpath;
00257 cdata_tex->_alpha_fullpath = alpha_fullpath;
00258 }
00259
00260 cdata_tex->_primary_file_num_channels = primary_file_num_channels;
00261 cdata_tex->_alpha_file_channel = alpha_file_channel;
00262
00263 if (!do_load_one(cdata_tex, color, alpha, z, options)) {
00264 return false;
00265 }
00266
00267 cdata_tex->_loaded_from_image = true;
00268 set_loop(true);
00269 play();
00270 return true;
00271 }
00272
00273
00274
00275
00276
00277
00278 bool MovieTexture::
00279 do_load_one(Texture::CData *cdata_tex,
00280 PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z,
00281 const LoaderOptions &options) {
00282 CDWriter cdata(_cycler);
00283 cdata->_pages.resize(z + 1);
00284 cdata->_pages[z]._color = color;
00285 cdata->_pages[z]._alpha = alpha;
00286 do_recalculate_image_properties(cdata, cdata_tex, options);
00287
00288
00289
00290 PTA_uchar image = make_ram_image();
00291 memset(image.p(), 0, image.size());
00292
00293 return true;
00294 }
00295
00296
00297
00298
00299
00300
00301
00302 bool MovieTexture::
00303 do_load_one(Texture::CData *cdata_tex,
00304 const PNMImage &pnmimage, const string &name, int z, int n,
00305 const LoaderOptions &options) {
00306 grutil_cat.error() << "You cannot load a static image into a MovieTexture\n";
00307 return false;
00308 }
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 void MovieTexture::
00320 do_allocate_pages(Texture::CData *cdata_tex) {
00321
00322
00323 }
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334 bool MovieTexture::
00335 has_cull_callback() const {
00336 return true;
00337 }
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348 bool MovieTexture::
00349 cull_callback(CullTraverser *, const CullTraverserData &) const {
00350 Texture::CDReader cdata_tex(Texture::_cycler);
00351 CDReader cdata(_cycler);
00352
00353 if (!cdata->_has_offset) {
00354
00355
00356 double offset;
00357 int true_loop_count = 1;
00358 if (cdata->_synchronize != 0) {
00359 offset = cdata->_synchronize->get_time();
00360 } else {
00361
00362 double now = ClockObject::get_global_clock()->get_frame_time();
00363 offset = cdata->_clock;
00364 if (cdata->_playing) {
00365 offset += now * cdata->_play_rate;
00366 }
00367 true_loop_count = cdata->_loop_count;
00368 }
00369 ((CData *)cdata.p())->_offset = offset;
00370 ((CData *)cdata.p())->_true_loop_count = true_loop_count;
00371 ((CData *)cdata.p())->_has_offset = true;
00372 }
00373
00374 bool in_sync = do_update_frames(cdata);
00375 if (!in_sync) {
00376
00377
00378 in_sync = do_update_frames(cdata);
00379 }
00380
00381 if (in_sync) {
00382
00383 Pages::const_iterator pi;
00384 for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
00385 const VideoPage &page = (*pi);
00386 MovieVideoCursor *color = page._color;
00387 MovieVideoCursor *alpha = page._alpha;
00388 size_t i = pi - cdata->_pages.begin();
00389
00390 if (color != NULL && alpha != NULL) {
00391 color->apply_to_texture_rgb(page._cbuffer, (MovieTexture*)this, i);
00392 alpha->apply_to_texture_alpha(page._abuffer, (MovieTexture*)this, i, cdata_tex->_alpha_file_channel);
00393
00394 } else if (color != NULL) {
00395 color->apply_to_texture(page._cbuffer, (MovieTexture*)this, i);
00396 }
00397
00398 ((VideoPage &)page)._cbuffer.clear();
00399 ((VideoPage &)page)._abuffer.clear();
00400 }
00401
00402
00403 ((CData *)cdata.p())->_has_offset = false;
00404 }
00405
00406 return true;
00407 }
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422 PT(Texture) MovieTexture::
00423 make_copy_impl() {
00424 Texture::CDReader cdata_tex(Texture::_cycler);
00425 CDReader cdata(_cycler);
00426 PT(MovieTexture) copy = new MovieTexture(get_name());
00427 Texture::CDWriter cdata_copy_tex(copy->Texture::_cycler, true);
00428 CDWriter cdata_copy(copy->_cycler, true);
00429 copy->do_assign(cdata_copy, cdata_copy_tex, this, cdata, cdata_tex);
00430
00431 return copy.p();
00432 }
00433
00434
00435
00436
00437
00438
00439 void MovieTexture::
00440 do_assign(CData *cdata, Texture::CData *cdata_tex, const MovieTexture *copy,
00441 const CData *cdata_copy, const Texture::CData *cdata_copy_tex) {
00442 Texture::do_assign(cdata_tex, copy, cdata_copy_tex);
00443
00444 pvector<MovieVideoCursor *> color;
00445 pvector<MovieVideoCursor *> alpha;
00446 color.resize(cdata_copy->_pages.size());
00447 alpha.resize(cdata_copy->_pages.size());
00448 for (int i=0; i<(int)(color.size()); i++) {
00449 color[i] = cdata_copy->_pages[i]._color;
00450 alpha[i] = cdata_copy->_pages[i]._alpha;
00451 }
00452
00453 cdata->_pages.resize(color.size());
00454 for (int i=0; i<(int)(color.size()); i++) {
00455 if (color[i]) {
00456 cdata->_pages[i]._color = color[i]->get_source()->open();
00457 }
00458 if (alpha[i]) {
00459 cdata->_pages[i]._alpha = alpha[i]->get_source()->open();
00460 }
00461 }
00462 do_recalculate_image_properties(cdata, cdata_tex, LoaderOptions());
00463 }
00464
00465
00466
00467
00468
00469
00470
00471
00472 void MovieTexture::
00473 do_reload_ram_image(Texture::CData *cdata, bool allow_compression) {
00474
00475
00476 }
00477
00478
00479
00480
00481
00482
00483
00484
00485 bool MovieTexture::
00486 get_keep_ram_image() const {
00487
00488 return true;
00489 }
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500 bool MovieTexture::
00501 do_has_bam_rawdata(const Texture::CData *cdata) const {
00502 return true;
00503 }
00504
00505
00506
00507
00508
00509
00510
00511 void MovieTexture::
00512 do_get_bam_rawdata(Texture::CData *cdata) {
00513 }
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524 bool MovieTexture::
00525 do_can_reload(const Texture::CData *cdata) const {
00526 return false;
00527 }
00528
00529
00530
00531
00532
00533
00534
00535
00536 void MovieTexture::
00537 restart() {
00538 CDWriter cdata(_cycler);
00539 if (!cdata->_playing) {
00540 double now = ClockObject::get_global_clock()->get_frame_time();
00541 cdata->_clock = cdata->_clock - (now * cdata->_play_rate);
00542 cdata->_playing = true;
00543 }
00544 }
00545
00546
00547
00548
00549
00550
00551
00552
00553 void MovieTexture::
00554 stop() {
00555 CDWriter cdata(_cycler);
00556 if (cdata->_playing) {
00557 double now = ClockObject::get_global_clock()->get_frame_time();
00558 cdata->_clock = cdata->_clock + (now * cdata->_play_rate);
00559 cdata->_playing = false;
00560 }
00561 }
00562
00563
00564
00565
00566
00567
00568 void MovieTexture::
00569 play() {
00570 CDWriter cdata(_cycler);
00571 double now = ClockObject::get_global_clock()->get_frame_time();
00572 cdata->_clock = 0.0 - (now * cdata->_play_rate);
00573 cdata->_playing = true;
00574 }
00575
00576
00577
00578
00579
00580
00581 void MovieTexture::
00582 set_time(double t) {
00583 CDWriter cdata(_cycler);
00584 t = min(cdata->_video_length, max(0.0, t));
00585 if (cdata->_playing) {
00586 double now = ClockObject::get_global_clock()->get_frame_time();
00587 cdata->_clock = t - (now * cdata->_play_rate);
00588 } else {
00589 cdata->_clock = t;
00590 }
00591 }
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603 double MovieTexture::
00604 get_time() const {
00605 CDReader cdata(_cycler);
00606 double clock = cdata->_clock;
00607 if (cdata->_playing) {
00608 double now = ClockObject::get_global_clock()->get_frame_time();
00609 clock += (now * cdata->_play_rate);
00610 }
00611 return clock;
00612 }
00613
00614
00615
00616
00617
00618
00619
00620 void MovieTexture::
00621 set_loop(bool loop) {
00622 set_loop_count(loop ? 0:1);
00623 }
00624
00625
00626
00627
00628
00629
00630
00631 bool MovieTexture::
00632 get_loop() const {
00633 CDReader cdata(_cycler);
00634 return (cdata->_loop_count == 0);
00635 }
00636
00637
00638
00639
00640
00641
00642 void MovieTexture::
00643 set_loop_count(int n) {
00644 CDWriter cdata(_cycler);
00645 cdata->_loop_count = n;
00646 }
00647
00648
00649
00650
00651
00652
00653 int MovieTexture::
00654 get_loop_count() const {
00655 CDReader cdata(_cycler);
00656 return cdata->_loop_count;
00657 }
00658
00659
00660
00661
00662
00663
00664
00665
00666 void MovieTexture::
00667 set_play_rate(double rate) {
00668 CDWriter cdata(_cycler);
00669 if (cdata->_playing) {
00670 double now = ClockObject::get_global_clock()->get_frame_time();
00671 cdata->_clock += (now * cdata->_play_rate);
00672 cdata->_play_rate = rate;
00673 cdata->_clock -= (now * cdata->_play_rate);
00674 } else {
00675 cdata->_play_rate = rate;
00676 }
00677 }
00678
00679
00680
00681
00682
00683
00684 double MovieTexture::
00685 get_play_rate() const {
00686 CDReader cdata(_cycler);
00687 return cdata->_play_rate;
00688 }
00689
00690
00691
00692
00693
00694
00695 bool MovieTexture::
00696 is_playing() const {
00697 CDReader cdata(_cycler);
00698 return cdata->_playing;
00699 }
00700
00701
00702
00703
00704
00705
00706
00707
00708 void MovieTexture::
00709 synchronize_to(AudioSound *s) {
00710 CDWriter cdata(_cycler);
00711 cdata->_synchronize = s;
00712 }
00713
00714
00715
00716
00717
00718
00719 void MovieTexture::
00720 unsynchronize() {
00721 CDWriter cdata(_cycler);
00722 cdata->_synchronize = 0;
00723 }
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734 bool MovieTexture::
00735 do_update_frames(const CData *cdata) const {
00736
00737
00738
00739 nassertr(cdata->_has_offset, false);
00740
00741
00742 Pages::const_iterator pi;
00743 for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
00744 const VideoPage &page = (*pi);
00745 MovieVideoCursor *color = page._color;
00746 MovieVideoCursor *alpha = page._alpha;
00747
00748 if (color != NULL && page._cbuffer == NULL) {
00749 if (color->set_time(cdata->_offset, cdata->_true_loop_count)) {
00750 ((VideoPage &)page)._cbuffer = color->fetch_buffer();
00751 }
00752 }
00753 if (alpha != NULL && page._abuffer == NULL) {
00754 if (alpha->set_time(cdata->_offset, cdata->_true_loop_count)) {
00755 ((VideoPage &)page)._abuffer = alpha->fetch_buffer();
00756 }
00757 }
00758 }
00759
00760 if (!movies_sync_pages) {
00761
00762
00763
00764 return true;
00765 }
00766
00767
00768 bool in_sync = true;
00769 bool any_frames = false;
00770 bool any_dropped = false;
00771 PT(MovieVideoCursor::Buffer) newest;
00772 for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
00773 const VideoPage &page = (*pi);
00774 if (page._cbuffer == NULL) {
00775 if (page._color != NULL) {
00776
00777 in_sync = false;
00778 }
00779 } else {
00780 nassertr(page._color != NULL, true);
00781 any_frames = true;
00782 if (newest == NULL) {
00783 newest = page._cbuffer;
00784 } else {
00785 int ref = newest->compare_timestamp(page._cbuffer);
00786 if (ref != 0) {
00787
00788 in_sync = false;
00789 any_dropped = true;
00790 if (ref < 0) {
00791 newest = page._cbuffer;
00792 }
00793 }
00794 }
00795 }
00796 if (page._abuffer == NULL) {
00797 if (page._alpha != NULL) {
00798 in_sync = false;
00799 }
00800 } else {
00801 nassertr(page._alpha != NULL, true);
00802 any_frames = true;
00803 if (newest == NULL) {
00804 newest = page._abuffer;
00805 } else {
00806 int ref = newest->compare_timestamp(page._abuffer);
00807 if (ref != 0) {
00808 in_sync = false;
00809 any_dropped = true;
00810 if (ref < 0) {
00811 newest = page._abuffer;
00812 }
00813 }
00814 }
00815 }
00816 }
00817
00818 if (!any_frames) {
00819
00820 return true;
00821 }
00822
00823 if (!in_sync) {
00824
00825
00826 if (newest != NULL) {
00827 Pages::const_iterator pi;
00828 for (pi = cdata->_pages.begin(); pi != cdata->_pages.end(); ++pi) {
00829 const VideoPage &page = (*pi);
00830 if (page._cbuffer != NULL && newest->compare_timestamp(page._cbuffer) > 0) {
00831 ((VideoPage &)page)._cbuffer.clear();
00832 any_dropped = true;
00833 }
00834 if (page._abuffer != NULL && newest->compare_timestamp(page._abuffer) > 0) {
00835 ((VideoPage &)page)._abuffer.clear();
00836 any_dropped = true;
00837 }
00838 }
00839
00840 if (any_dropped) {
00841
00842
00843
00844
00845 ((CData *)cdata)->_offset = newest->get_timestamp();
00846 }
00847 }
00848 }
00849
00850 return in_sync;
00851 }
00852
00853
00854
00855
00856
00857
00858 void MovieTexture::
00859 register_with_read_factory() {
00860 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
00861 }
00862
00863
00864
00865
00866
00867
00868 TypedWritable *MovieTexture::
00869 make_from_bam(const FactoryParams ¶ms) {
00870 PT(MovieTexture) dummy = new MovieTexture("");
00871 return dummy->make_this_from_bam(params);
00872 }
00873
00874
00875
00876
00877
00878
00879
00880
00881 int MovieTexture::
00882 complete_pointers(TypedWritable **p_list, BamReader *manager) {
00883 int pi = Texture::complete_pointers(p_list, manager);
00884
00885 CDWriter cdata(_cycler);
00886 size_t num_pages = cdata->_pages.size();
00887 for (size_t n = 0; n < num_pages; ++n) {
00888 VideoPage &page = cdata->_pages[n];
00889 page._color = DCAST(MovieVideoCursor, p_list[pi++]);
00890 page._alpha = DCAST(MovieVideoCursor, p_list[pi++]);
00891 }
00892
00893 return pi;
00894 }
00895
00896
00897
00898
00899
00900
00901
00902 void MovieTexture::
00903 do_write_datagram_rawdata(Texture::CData *cdata_tex, BamWriter *manager, Datagram &dg) {
00904 CDReader cdata(_cycler);
00905
00906 dg.add_uint16(cdata_tex->_z_size);
00907 dg.add_uint16(cdata_tex->_num_views);
00908 nassertv(cdata->_pages.size() == (size_t)(cdata_tex->_z_size * cdata_tex->_num_views));
00909 for (size_t n = 0; n < cdata->_pages.size(); ++n) {
00910 const VideoPage &page = cdata->_pages[n];
00911 manager->write_pointer(dg, page._color);
00912 manager->write_pointer(dg, page._alpha);
00913 }
00914 }
00915
00916
00917
00918
00919
00920
00921
00922 void MovieTexture::
00923 do_fillin_rawdata(Texture::CData *cdata_tex, DatagramIterator &scan, BamReader *manager) {
00924 CDWriter cdata(_cycler);
00925
00926 cdata_tex->_z_size = scan.get_uint16();
00927 cdata_tex->_num_views = 1;
00928 if (manager->get_file_minor_ver() >= 26) {
00929 cdata_tex->_num_views = scan.get_uint16();
00930 }
00931
00932 size_t num_pages = (size_t)(cdata_tex->_z_size * cdata_tex->_num_views);
00933 cdata->_pages.reserve(num_pages);
00934 for (size_t n = 0; n < num_pages; ++n) {
00935 cdata->_pages.push_back(VideoPage());
00936 manager->read_pointer(scan);
00937 manager->read_pointer(scan);
00938 }
00939
00940
00941
00942
00943 manager->register_finalize(this);
00944 }
00945
00946
00947
00948
00949
00950
00951
00952
00953 void MovieTexture::
00954 finalize(BamReader *manager) {
00955 Texture::CDWriter cdata_tex(Texture::_cycler);
00956 CDWriter cdata(_cycler);
00957
00958
00959 size_t num_pages = cdata->_pages.size();
00960 for (size_t n = 0; n < num_pages; ++n) {
00961 VideoPage &page = cdata->_pages[n];
00962 manager->finalize_now(page._color);
00963 manager->finalize_now(page._alpha);
00964 }
00965
00966 do_recalculate_image_properties(cdata, cdata_tex, LoaderOptions());
00967
00968 set_loaded_from_image();
00969 set_loop(true);
00970 play();
00971 }
00972
00973 #endif // HAVE_AUDIO