00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "ffmpegVideoCursor.h"
00016
00017 #ifdef HAVE_FFMPEG
00018
00019 #include "config_movies.h"
00020 #include "pStatCollector.h"
00021 #include "pStatTimer.h"
00022 #include "mutexHolder.h"
00023 #include "reMutexHolder.h"
00024 #include "ffmpegVideo.h"
00025 #include "bamReader.h"
00026 extern "C" {
00027 #include "libavcodec/avcodec.h"
00028 #include "libavformat/avformat.h"
00029 #ifdef HAVE_SWSCALE
00030 #include "libswscale/swscale.h"
00031 #endif
00032 }
00033
00034 ReMutex FfmpegVideoCursor::_av_lock;
00035 TypeHandle FfmpegVideoCursor::_type_handle;
00036 TypeHandle FfmpegVideoCursor::FfmpegBuffer::_type_handle;
00037
00038 PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decoding:Fetch");
00039 PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
00040 PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR");
00041
00042
00043 #if LIBAVFORMAT_VERSION_MAJOR < 53
00044 #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
00045 #endif
00046
00047
00048
00049
00050
00051
00052
00053 FfmpegVideoCursor::
00054 FfmpegVideoCursor() :
00055 _max_readahead_frames(0),
00056 _thread_priority(ffmpeg_thread_priority),
00057 _lock("FfmpegVideoCursor::_lock"),
00058 _action_cvar(_lock),
00059 _thread_status(TS_stopped),
00060 _seek_frame(0),
00061 _packet(NULL),
00062 _format_ctx(NULL),
00063 _video_ctx(NULL),
00064 _convert_ctx(NULL),
00065 _video_index(-1),
00066 _frame(NULL),
00067 _frame_out(NULL),
00068 _eof_known(false)
00069 {
00070 }
00071
00072
00073
00074
00075
00076
00077
00078
00079 void FfmpegVideoCursor::
00080 init_from(FfmpegVideo *source) {
00081 nassertv(_thread == NULL && _thread_status == TS_stopped);
00082 nassertv(source != NULL);
00083 _source = source;
00084 _filename = _source->get_filename();
00085
00086 if (!open_stream()) {
00087 cleanup();
00088 return;
00089 }
00090
00091 ReMutexHolder av_holder(_av_lock);
00092
00093 #ifdef HAVE_SWSCALE
00094 nassertv(_convert_ctx == NULL);
00095 _convert_ctx = sws_getContext(_size_x, _size_y,
00096 _video_ctx->pix_fmt, _size_x, _size_y,
00097 PIX_FMT_BGR24, SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
00098 #endif // HAVE_SWSCALE
00099
00100 _frame = avcodec_alloc_frame();
00101 _frame_out = avcodec_alloc_frame();
00102
00103 if ((_frame == 0)||(_frame_out == 0)) {
00104 cleanup();
00105 return;
00106 }
00107
00108 _packet = new AVPacket;
00109 memset(_packet, 0, sizeof(AVPacket));
00110
00111 fetch_packet(0);
00112 fetch_frame(-1);
00113 _initial_dts = _begin_frame;
00114
00115 _current_frame = -1;
00116 _eof_known = false;
00117 _eof_frame = 0;
00118
00119 #ifdef HAVE_THREADS
00120 set_max_readahead_frames(ffmpeg_max_readahead_frames);
00121 #endif // HAVE_THREADS
00122 }
00123
00124
00125
00126
00127
00128
00129 FfmpegVideoCursor::
00130 FfmpegVideoCursor(FfmpegVideo *src) :
00131 _max_readahead_frames(0),
00132 _thread_priority(ffmpeg_thread_priority),
00133 _lock("FfmpegVideoCursor::_lock"),
00134 _action_cvar(_lock),
00135 _thread_status(TS_stopped),
00136 _seek_frame(0),
00137 _packet(NULL),
00138 _format_ctx(NULL),
00139 _video_ctx(NULL),
00140 _convert_ctx(NULL),
00141 _video_index(-1),
00142 _frame(NULL),
00143 _frame_out(NULL),
00144 _eof_known(false)
00145 {
00146 init_from(src);
00147 }
00148
00149
00150
00151
00152
00153
00154 FfmpegVideoCursor::
00155 ~FfmpegVideoCursor() {
00156 cleanup();
00157 }
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 void FfmpegVideoCursor::
00177 set_max_readahead_frames(int max_readahead_frames) {
00178 #ifndef HAVE_THREADS
00179 if (max_readahead_frames > 0) {
00180 ffmpeg_cat.warning()
00181 << "Couldn't set max_readahead_frames to " << max_readahead_frames
00182 << ": threading not available.\n";
00183 max_readahead_frames = 0;
00184 }
00185 #endif // HAVE_THREADS
00186
00187 _max_readahead_frames = max_readahead_frames;
00188 if (_max_readahead_frames > 0) {
00189 if (_thread_status == TS_stopped) {
00190 start_thread();
00191 }
00192 } else {
00193 if (_thread_status != TS_stopped) {
00194 stop_thread();
00195 }
00196 }
00197 }
00198
00199
00200
00201
00202
00203
00204
00205
00206 int FfmpegVideoCursor::
00207 get_max_readahead_frames() const {
00208 return _max_readahead_frames;
00209 }
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222 void FfmpegVideoCursor::
00223 set_thread_priority(ThreadPriority thread_priority) {
00224 if (_thread_priority != thread_priority) {
00225 _thread_priority = thread_priority;
00226 if (is_thread_started()) {
00227 stop_thread();
00228 start_thread();
00229 }
00230 }
00231 }
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241 ThreadPriority FfmpegVideoCursor::
00242 get_thread_priority() const {
00243 return _thread_priority;
00244 }
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 void FfmpegVideoCursor::
00256 start_thread() {
00257 MutexHolder holder(_lock);
00258
00259 if (_thread_status == TS_stopped && _max_readahead_frames > 0) {
00260
00261 ostringstream strm;
00262 strm << (void *)this;
00263 _sync_name = strm.str();
00264
00265
00266 _thread_status = TS_wait;
00267 _thread = new GenericThread(_filename.get_basename(), _sync_name, st_thread_main, this);
00268 if (!_thread->start(_thread_priority, true)) {
00269
00270 _thread = NULL;
00271 _thread_status = TS_stopped;
00272 }
00273 }
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 void FfmpegVideoCursor::
00288 stop_thread() {
00289 if (_thread_status != TS_stopped) {
00290 PT(GenericThread) thread = _thread;
00291 {
00292 MutexHolder holder(_lock);
00293 if (_thread_status != TS_stopped) {
00294 _thread_status = TS_shutdown;
00295 }
00296 _action_cvar.notify();
00297 _thread = NULL;
00298 }
00299
00300
00301 thread->join();
00302 }
00303
00304
00305
00306
00307
00308 MutexHolder holder(_lock);
00309
00310 _readahead_frames.clear();
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320 bool FfmpegVideoCursor::
00321 is_thread_started() const {
00322 return (_thread_status != TS_stopped);
00323 }
00324
00325
00326
00327
00328
00329
00330 bool FfmpegVideoCursor::
00331 set_time(double timestamp, int loop_count) {
00332 int frame = (int)(timestamp / _video_timebase + 0.5);
00333
00334 if (_eof_known) {
00335 if (loop_count == 0) {
00336 frame = frame % _eof_frame;
00337 } else {
00338 int last_frame = _eof_frame * loop_count;
00339 if (frame < last_frame) {
00340 frame = frame % _eof_frame;
00341 } else {
00342 frame = _eof_frame - 1;
00343 }
00344 }
00345 }
00346
00347
00348 frame = max(frame, _initial_dts);
00349
00350 if (ffmpeg_cat.is_spam() && frame != _current_frame) {
00351 ffmpeg_cat.spam()
00352 << "set_time(" << timestamp << "): " << frame
00353 << ", loop_count = " << loop_count << "\n";
00354 }
00355
00356 _current_frame = frame;
00357 if (_current_frame_buffer != NULL) {
00358
00359
00360 return (_current_frame >= _current_frame_buffer->_end_frame ||
00361 _current_frame < _current_frame_buffer->_begin_frame);
00362 }
00363
00364
00365 return true;
00366 }
00367
00368
00369
00370
00371
00372
00373 PT(MovieVideoCursor::Buffer) FfmpegVideoCursor::
00374 fetch_buffer() {
00375 MutexHolder holder(_lock);
00376
00377
00378 if (_format_ctx == (AVFormatContext *)NULL) {
00379 return NULL;
00380 }
00381
00382 PT(FfmpegBuffer) frame;
00383 if (_thread_status == TS_stopped) {
00384
00385 advance_to_frame(_current_frame);
00386 if (_frame_ready) {
00387 frame = do_alloc_frame();
00388 export_frame(frame);
00389 }
00390
00391 } else {
00392
00393
00394 if (!_readahead_frames.empty()) {
00395 frame = _readahead_frames.front();
00396 _readahead_frames.pop_front();
00397 _action_cvar.notify();
00398 while (frame->_end_frame < _current_frame && !_readahead_frames.empty()) {
00399
00400 if (ffmpeg_cat.is_debug()) {
00401 ffmpeg_cat.debug()
00402 << "ffmpeg for " << _filename.get_basename()
00403 << " at frame " << _current_frame << ", discarding frame at "
00404 << frame->_begin_frame << "\n";
00405 }
00406 frame = _readahead_frames.front();
00407 _readahead_frames.pop_front();
00408 }
00409 if (frame->_begin_frame > _current_frame) {
00410
00411
00412 if (ffmpeg_cat.is_debug()) {
00413 ffmpeg_cat.debug()
00414 << "ffmpeg for " << _filename.get_basename()
00415 << " at frame " << _current_frame << ", encountered too-new frame at "
00416 << frame->_begin_frame << "\n";
00417 }
00418 do_clear_all_frames();
00419 if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) {
00420 _thread_status = TS_seek;
00421 _seek_frame = _current_frame;
00422 _action_cvar.notify();
00423 }
00424 }
00425 }
00426 if (frame == NULL || frame->_end_frame < _current_frame) {
00427
00428 if (_thread_status == TS_wait || _thread_status == TS_seek || _thread_status == TS_readahead) {
00429 _thread_status = TS_seek;
00430 _seek_frame = _current_frame;
00431 _action_cvar.notify();
00432 }
00433 }
00434 }
00435
00436 if (frame != NULL) {
00437 bool too_old = (frame->_end_frame < _current_frame && !ffmpeg_show_seek_frames);
00438 bool too_new = frame->_begin_frame > _current_frame;
00439 if (too_old || too_new) {
00440
00441 frame = NULL;
00442 }
00443 }
00444
00445 if (frame != NULL) {
00446 _current_frame_buffer = frame;
00447 if (ffmpeg_cat.is_debug()) {
00448 ffmpeg_cat.debug()
00449 << "ffmpeg for " << _filename.get_basename()
00450 << " at frame " << _current_frame << ", returning frame at "
00451 << frame->_begin_frame << "\n";
00452 }
00453 } else {
00454 if (ffmpeg_cat.is_debug()) {
00455 ffmpeg_cat.debug()
00456 << "ffmpeg for " << _filename.get_basename()
00457 << " at frame " << _current_frame << ", returning NULL\n";
00458 }
00459 }
00460 return frame.p();
00461 }
00462
00463
00464
00465
00466
00467
00468
00469 PT(MovieVideoCursor::Buffer) FfmpegVideoCursor::
00470 make_new_buffer() {
00471 PT(FfmpegBuffer) frame = new FfmpegBuffer(size_x() * size_y() * get_num_components(), _video_timebase);
00472 return frame.p();
00473 }
00474
00475
00476
00477
00478
00479
00480
00481 bool FfmpegVideoCursor::
00482 open_stream() {
00483 nassertr(!_ffvfile.is_open(), false);
00484
00485
00486
00487 ReMutexHolder av_holder(_av_lock);
00488
00489 if (!_source->get_subfile_info().is_empty()) {
00490
00491 if (!_ffvfile.open_subfile(_source->get_subfile_info())) {
00492 ffmpeg_cat.info()
00493 << "Couldn't open " << _source->get_subfile_info() << "\n";
00494 close_stream();
00495 return false;
00496 }
00497
00498 } else {
00499
00500 if (!_ffvfile.open_vfs(_filename)) {
00501 ffmpeg_cat.info()
00502 << "Couldn't open " << _filename << "\n";
00503 close_stream();
00504 return false;
00505 }
00506 }
00507
00508 nassertr(_format_ctx == NULL, false);
00509 _format_ctx = _ffvfile.get_format_context();
00510 nassertr(_format_ctx != NULL, false);
00511
00512 if (av_find_stream_info(_format_ctx) < 0) {
00513 ffmpeg_cat.info()
00514 << "Couldn't find stream info\n";
00515 close_stream();
00516 return false;
00517 }
00518
00519
00520 nassertr(_video_ctx == NULL, false);
00521 for (int i = 0; i < (int)_format_ctx->nb_streams; ++i) {
00522 if (_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
00523 _video_index = i;
00524 _video_ctx = _format_ctx->streams[i]->codec;
00525 _video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
00526 _min_fseek = (int)(3.0 / _video_timebase);
00527 }
00528 }
00529
00530 if (_video_ctx == NULL) {
00531 ffmpeg_cat.info()
00532 << "Couldn't find video_ctx\n";
00533 close_stream();
00534 return false;
00535 }
00536
00537 AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
00538 if (pVideoCodec == NULL) {
00539 ffmpeg_cat.info()
00540 << "Couldn't find codec\n";
00541 close_stream();
00542 return false;
00543 }
00544 if (avcodec_open(_video_ctx, pVideoCodec) < 0) {
00545 ffmpeg_cat.info()
00546 << "Couldn't open codec\n";
00547 close_stream();
00548 return false;
00549 }
00550
00551 _size_x = _video_ctx->width;
00552 _size_y = _video_ctx->height;
00553 _num_components = 3;
00554 _length = (double)_format_ctx->duration / (double)AV_TIME_BASE;
00555 _can_seek = true;
00556 _can_seek_fast = true;
00557
00558 return true;
00559 }
00560
00561
00562
00563
00564
00565
00566
00567 void FfmpegVideoCursor::
00568 close_stream() {
00569
00570 ReMutexHolder av_holder(_av_lock);
00571
00572 if ((_video_ctx)&&(_video_ctx->codec)) {
00573 avcodec_close(_video_ctx);
00574 }
00575 _video_ctx = NULL;
00576
00577 _ffvfile.close();
00578 _format_ctx = NULL;
00579
00580 _video_index = -1;
00581 }
00582
00583
00584
00585
00586
00587
00588 void FfmpegVideoCursor::
00589 cleanup() {
00590 stop_thread();
00591 close_stream();
00592
00593 ReMutexHolder av_holder(_av_lock);
00594
00595 #ifdef HAVE_SWSCALE
00596 if (_convert_ctx != NULL) {
00597 sws_freeContext(_convert_ctx);
00598 }
00599 _convert_ctx = NULL;
00600 #endif // HAVE_SWSCALE
00601
00602 if (_frame) {
00603 av_free(_frame);
00604 _frame = NULL;
00605 }
00606
00607 if (_frame_out) {
00608 _frame_out->data[0] = 0;
00609 av_free(_frame_out);
00610 _frame_out = NULL;
00611 }
00612
00613 if (_packet) {
00614 if (_packet->data) {
00615 av_free_packet(_packet);
00616 }
00617 delete _packet;
00618 _packet = NULL;
00619 }
00620 }
00621
00622
00623
00624
00625
00626
00627
00628 void FfmpegVideoCursor::
00629 st_thread_main(void *self) {
00630 ((FfmpegVideoCursor *)self)->thread_main();
00631 }
00632
00633
00634
00635
00636
00637
00638 void FfmpegVideoCursor::
00639 thread_main() {
00640 if (ffmpeg_cat.is_spam()) {
00641 ffmpeg_cat.spam()
00642 << "ffmpeg thread for " << _filename.get_basename() << " starting.\n";
00643 }
00644
00645
00646 if (_frame_ready) {
00647 PT(FfmpegBuffer) frame = do_alloc_frame();
00648 export_frame(frame);
00649 MutexHolder holder(_lock);
00650 _readahead_frames.push_back(frame);
00651 }
00652
00653
00654
00655 MutexHolder holder(_lock);
00656 while (_thread_status != TS_shutdown) {
00657 nassertv(_thread_status != TS_stopped);
00658 _action_cvar.wait();
00659
00660 while (do_poll()) {
00661
00662 _lock.release();
00663 PStatClient::thread_tick(_sync_name);
00664 Thread::consider_yield();
00665 _lock.acquire();
00666 }
00667 }
00668
00669 _thread_status = TS_stopped;
00670 if (ffmpeg_cat.is_spam()) {
00671 ffmpeg_cat.spam()
00672 << "ffmpeg thread for " << _filename.get_basename() << " stopped.\n";
00673 }
00674 }
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684 bool FfmpegVideoCursor::
00685 do_poll() {
00686 switch (_thread_status) {
00687 case TS_stopped:
00688 case TS_seeking:
00689
00690 nassertr(false, false);
00691 return false;
00692
00693 case TS_wait:
00694
00695 return false;
00696
00697 case TS_readahead:
00698 if ((int)_readahead_frames.size() < _max_readahead_frames) {
00699
00700 PT(FfmpegBuffer) frame = do_alloc_frame();
00701 nassertr(frame != NULL, false);
00702 _lock.release();
00703 fetch_frame(-1);
00704 if (_frame_ready) {
00705 export_frame(frame);
00706 _lock.acquire();
00707 _readahead_frames.push_back(frame);
00708 } else {
00709
00710 _lock.acquire();
00711 }
00712 return true;
00713 }
00714
00715
00716 return false;
00717
00718 case TS_seek:
00719
00720 {
00721 int seek_frame = _seek_frame;
00722 _thread_status = TS_seeking;
00723 PT(FfmpegBuffer) frame = do_alloc_frame();
00724 nassertr(frame != NULL, false);
00725 _lock.release();
00726 advance_to_frame(seek_frame);
00727 if (_frame_ready) {
00728 export_frame(frame);
00729 _lock.acquire();
00730 do_clear_all_frames();
00731 _readahead_frames.push_back(frame);
00732 } else {
00733 _lock.acquire();
00734 do_clear_all_frames();
00735 }
00736
00737 if (_thread_status == TS_seeking) {
00738
00739 _thread_status = TS_readahead;
00740 }
00741 }
00742 return true;
00743
00744 case TS_shutdown:
00745
00746 return false;
00747 }
00748
00749 return false;
00750 }
00751
00752
00753
00754
00755
00756
00757
00758 PT(FfmpegVideoCursor::FfmpegBuffer) FfmpegVideoCursor::
00759 do_alloc_frame() {
00760 PT(Buffer) buffer = make_new_buffer();
00761 return (FfmpegBuffer *)buffer.p();
00762 }
00763
00764
00765
00766
00767
00768
00769
00770 void FfmpegVideoCursor::
00771 do_clear_all_frames() {
00772 _readahead_frames.clear();
00773 }
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786 bool FfmpegVideoCursor::
00787 fetch_packet(int default_frame) {
00788 if (ffmpeg_global_lock) {
00789 ReMutexHolder av_holder(_av_lock);
00790 return do_fetch_packet(default_frame);
00791 } else {
00792 return do_fetch_packet(default_frame);
00793 }
00794 }
00795
00796
00797
00798
00799
00800
00801
00802 bool FfmpegVideoCursor::
00803 do_fetch_packet(int default_frame) {
00804 if (_packet->data) {
00805 av_free_packet(_packet);
00806 }
00807 while (av_read_frame(_format_ctx, _packet) >= 0) {
00808 if (_packet->stream_index == _video_index) {
00809 _packet_frame = _packet->dts;
00810 return false;
00811 }
00812 av_free_packet(_packet);
00813 }
00814 _packet->data = 0;
00815
00816 if (!_eof_known && default_frame != 0) {
00817 _eof_frame = _packet_frame;
00818 _eof_known = true;
00819 }
00820
00821 if (ffmpeg_cat.is_spam()) {
00822 if (_eof_known) {
00823 ffmpeg_cat.spam()
00824 << "end of video at frame " << _eof_frame << "\n";
00825 } else {
00826 ffmpeg_cat.spam()
00827 << "end of video\n";
00828 }
00829 }
00830 _packet_frame = default_frame;
00831 return true;
00832 }
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845 void FfmpegVideoCursor::
00846 fetch_frame(int frame) {
00847 PStatTimer timer(_fetch_buffer_pcollector);
00848
00849 int finished = 0;
00850
00851 if (_packet_frame <= frame) {
00852 finished = 0;
00853
00854
00855
00856 while (_packet_frame <= frame) {
00857 PStatTimer timer(_seek_pcollector);
00858
00859
00860 decode_frame(finished);
00861 _begin_frame = _packet_frame;
00862 if (fetch_packet(frame)) {
00863 _end_frame = _packet_frame;
00864 _frame_ready = false;
00865 return;
00866 }
00867 }
00868
00869 } else {
00870
00871 finished = 0;
00872 while (!finished && _packet->data) {
00873 decode_frame(finished);
00874 _begin_frame = _packet_frame;
00875 fetch_packet(_begin_frame + 1);
00876 }
00877 }
00878
00879 _end_frame = _packet_frame;
00880 _frame_ready = true;
00881 }
00882
00883
00884
00885
00886
00887
00888
00889 void FfmpegVideoCursor::
00890 decode_frame(int &finished) {
00891 if (ffmpeg_global_lock) {
00892 ReMutexHolder av_holder(_av_lock);
00893 do_decode_frame(finished);
00894 } else {
00895 do_decode_frame(finished);
00896 }
00897 }
00898
00899
00900
00901
00902
00903
00904
00905 void FfmpegVideoCursor::
00906 do_decode_frame(int &finished) {
00907 #if LIBAVCODEC_VERSION_INT < 3414272
00908 avcodec_decode_video(_video_ctx, _frame,
00909 &finished, _packet->data, _packet->size);
00910 #else
00911 avcodec_decode_video2(_video_ctx, _frame, &finished, _packet);
00912 #endif
00913 }
00914
00915
00916
00917
00918
00919
00920
00921
00922 void FfmpegVideoCursor::
00923 seek(int frame, bool backward) {
00924 PStatTimer timer(_seek_pcollector);
00925
00926 if (ffmpeg_support_seek) {
00927 if (ffmpeg_global_lock) {
00928 ReMutexHolder av_holder(_av_lock);
00929 do_seek(frame, backward);
00930 } else {
00931 do_seek(frame, backward);
00932 }
00933
00934 } else {
00935
00936 if (backward) {
00937 reset_stream();
00938 }
00939 }
00940 }
00941
00942
00943
00944
00945
00946
00947
00948
00949 void FfmpegVideoCursor::
00950 do_seek(int frame, bool backward) {
00951 PN_int64 target_ts = (PN_int64)frame;
00952 if (target_ts < (PN_int64)(_initial_dts)) {
00953
00954 target_ts = _initial_dts;
00955 }
00956 int flags = 0;
00957 if (backward) {
00958 flags = AVSEEK_FLAG_BACKWARD;
00959 }
00960
00961 if (av_seek_frame(_format_ctx, _video_index, target_ts, flags) < 0) {
00962 if (ffmpeg_cat.is_spam()) {
00963 ffmpeg_cat.spam()
00964 << "Seek failure.\n";
00965 }
00966
00967 if (backward) {
00968
00969 reset_stream();
00970 seek(frame, false);
00971 return;
00972 }
00973
00974
00975 if (binary_seek(_initial_dts, frame, frame, 1) < 0) {
00976 if (ffmpeg_cat.is_spam()) {
00977 ffmpeg_cat.spam()
00978 << "Seek double failure.\n";
00979 }
00980 reset_stream();
00981 return;
00982 }
00983 }
00984
00985 fetch_packet(0);
00986 fetch_frame(-1);
00987 }
00988
00989
00990
00991
00992
00993
00994
00995
00996 int FfmpegVideoCursor::
00997 binary_seek(int min_frame, int max_frame, int target_frame, int num_iterations) {
00998 int try_frame = (min_frame + max_frame) / 2;
00999 if (num_iterations > 5 || try_frame >= max_frame) {
01000
01001 return 0;
01002 }
01003
01004 if (av_seek_frame(_format_ctx, _video_index, try_frame, AVSEEK_FLAG_BACKWARD) < 0) {
01005
01006 if (binary_seek(min_frame, try_frame - 1, target_frame, num_iterations + 1) < 0) {
01007 return -1;
01008 }
01009 } else {
01010
01011 if (binary_seek(try_frame + 1, max_frame, target_frame, num_iterations + 1) < 0) {
01012 return -1;
01013 }
01014 }
01015 return 0;
01016 }
01017
01018
01019
01020
01021
01022
01023
01024 void FfmpegVideoCursor::
01025 reset_stream() {
01026 if (ffmpeg_cat.is_spam()) {
01027 ffmpeg_cat.spam()
01028 << "Resetting ffmpeg stream.\n";
01029 }
01030
01031 close_stream();
01032 if (!open_stream()) {
01033 ffmpeg_cat.error()
01034 << "Stream error, invalidating movie.\n";
01035 cleanup();
01036 return;
01037 }
01038
01039 fetch_packet(0);
01040 fetch_frame(-1);
01041 }
01042
01043
01044
01045
01046
01047
01048
01049 void FfmpegVideoCursor::
01050 advance_to_frame(int frame) {
01051 PStatTimer timer(_fetch_buffer_pcollector);
01052
01053 if (frame < _begin_frame) {
01054
01055 if (ffmpeg_cat.is_spam()) {
01056 ffmpeg_cat.spam()
01057 << "Seeking backward to " << frame << " from " << _begin_frame << "\n";
01058 }
01059 seek(frame, true);
01060 if (_begin_frame > frame) {
01061 if (ffmpeg_cat.is_spam()) {
01062 ffmpeg_cat.spam()
01063 << "Ended up at " << _begin_frame << ", not far enough back!\n";
01064 }
01065 reset_stream();
01066 if (ffmpeg_cat.is_spam()) {
01067 ffmpeg_cat.spam()
01068 << "Reseek to 0, got " << _begin_frame << "\n";
01069 }
01070 }
01071 if (frame > _end_frame) {
01072 if (ffmpeg_cat.is_spam()) {
01073 ffmpeg_cat.spam()
01074 << "Now sliding forward to " << frame << " from " << _begin_frame << "\n";
01075 }
01076 fetch_frame(frame);
01077 }
01078
01079 } else if (frame < _end_frame) {
01080
01081 if (ffmpeg_cat.is_spam()) {
01082 ffmpeg_cat.spam()
01083 << "Currently have " << frame << " within " << _begin_frame << " .. " << _end_frame << "\n";
01084 }
01085
01086 } else if (frame < _end_frame + _min_fseek) {
01087
01088 if (ffmpeg_cat.is_spam()) {
01089 ffmpeg_cat.spam()
01090 << "Sliding forward to " << frame << " from " << _begin_frame << "\n";
01091 }
01092 fetch_frame(frame);
01093
01094 } else {
01095
01096
01097
01098
01099
01100
01101
01102
01103 if (ffmpeg_cat.is_spam()) {
01104 ffmpeg_cat.spam()
01105 << "Jumping forward to " << frame << " from " << _begin_frame << "\n";
01106 }
01107 int base = _begin_frame;
01108 seek(frame, false);
01109 if (_begin_frame < base) {
01110 _min_fseek += (base - _begin_frame);
01111 if (ffmpeg_cat.is_spam()) {
01112 ffmpeg_cat.spam()
01113 << "Wrong way! Increasing _min_fseek to " << _min_fseek << "\n";
01114 }
01115 }
01116 if (frame > _end_frame) {
01117 if (ffmpeg_cat.is_spam()) {
01118 ffmpeg_cat.spam()
01119 << "Correcting, sliding forward to " << frame << " from " << _begin_frame << "\n";
01120 }
01121 fetch_frame(frame);
01122 }
01123 }
01124
01125 if (ffmpeg_cat.is_spam()) {
01126 ffmpeg_cat.spam()
01127 << "Wanted " << frame << ", got " << _begin_frame << "\n";
01128 }
01129 }
01130
01131
01132
01133
01134
01135
01136
01137 void FfmpegVideoCursor::
01138 export_frame(FfmpegBuffer *buffer) {
01139 PStatTimer timer(_export_frame_pcollector);
01140
01141 if (!_frame_ready) {
01142
01143 if (ffmpeg_cat.is_spam()) {
01144 ffmpeg_cat.spam()
01145 << "ffmpeg for " << _filename.get_basename()
01146 << ", no frame available.\n";
01147 }
01148 memset(buffer->_block, 0, buffer->_block_size);
01149 return;
01150 }
01151
01152 _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3);
01153 _frame_out->linesize[0] = _size_x * -3;
01154 buffer->_begin_frame = _begin_frame;
01155 buffer->_end_frame = _end_frame;
01156
01157 if (ffmpeg_global_lock) {
01158 ReMutexHolder av_holder(_av_lock);
01159 #ifdef HAVE_SWSCALE
01160 nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
01161 sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
01162 #else
01163 img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
01164 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
01165 #endif
01166 } else {
01167 #ifdef HAVE_SWSCALE
01168 nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
01169 sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
01170 #else
01171 img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
01172 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
01173 #endif
01174 }
01175 }
01176
01177
01178
01179
01180
01181
01182
01183 void FfmpegVideoCursor::
01184 register_with_read_factory() {
01185 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
01186 }
01187
01188
01189
01190
01191
01192
01193
01194 void FfmpegVideoCursor::
01195 write_datagram(BamWriter *manager, Datagram &dg) {
01196 MovieVideoCursor::write_datagram(manager, dg);
01197
01198
01199
01200
01201 }
01202
01203
01204
01205
01206
01207
01208
01209
01210 void FfmpegVideoCursor::
01211 finalize(BamReader *) {
01212 if (_source != (MovieVideo *)NULL) {
01213 FfmpegVideo *video;
01214 DCAST_INTO_V(video, _source);
01215 init_from(video);
01216 }
01217 }
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227 TypedWritable *FfmpegVideoCursor::
01228 make_from_bam(const FactoryParams ¶ms) {
01229 FfmpegVideoCursor *video = new FfmpegVideoCursor;
01230 DatagramIterator scan;
01231 BamReader *manager;
01232
01233 parse_params(params, scan, manager);
01234 video->fillin(scan, manager);
01235
01236 return video;
01237 }
01238
01239
01240
01241
01242
01243
01244
01245
01246 void FfmpegVideoCursor::
01247 fillin(DatagramIterator &scan, BamReader *manager) {
01248 MovieVideoCursor::fillin(scan, manager);
01249
01250
01251
01252
01253 manager->register_finalize(this);
01254 }
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268 int FfmpegVideoCursor::FfmpegBuffer::
01269 compare_timestamp(const Buffer *other) const {
01270 const FfmpegBuffer *fother;
01271 DCAST_INTO_R(fother, other, 0);
01272 if (_end_frame * _video_timebase <= fother->_begin_frame * fother->_video_timebase) {
01273 return -1;
01274 } else if (_begin_frame * _video_timebase >= fother->_end_frame * fother->_video_timebase) {
01275 return 1;
01276 }
01277 return 0;
01278 }
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289 double FfmpegVideoCursor::FfmpegBuffer::
01290 get_timestamp() const {
01291 int mid_frame = (_begin_frame + _end_frame - 1) / 2;
01292 return mid_frame * _video_timebase;
01293 }
01294
01295 #endif // HAVE_FFMPEG