diff --git a/src/osgPlugins/ffmpeg/FFmpegClocks.cpp b/src/osgPlugins/ffmpeg/FFmpegClocks.cpp index 5547461e5..8ec87ceff 100644 --- a/src/osgPlugins/ffmpeg/FFmpegClocks.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegClocks.cpp @@ -30,6 +30,8 @@ namespace FFmpegClocks::FFmpegClocks() : m_video_clock(0), m_start_time(0), + m_pause_time(0), + m_seek_time(0), m_last_frame_delay(0.040), m_last_frame_pts(0), m_last_actual_delay(0), @@ -37,7 +39,9 @@ FFmpegClocks::FFmpegClocks() : m_audio_buffer_end_pts(0), m_audio_delay(0.0), m_audio_disabled(false), - m_rewind(false) + m_rewind(false), + m_paused(false), + m_last_current_time(0.0) { } @@ -59,6 +63,14 @@ void FFmpegClocks::reset(const double start_time) m_audio_timer.setStartTick(); } +void FFmpegClocks::pause(bool pause) +{ + if(pause) + m_paused = true; + else + m_paused = false; +} + void FFmpegClocks::rewindAudio() @@ -205,13 +217,30 @@ double FFmpegClocks::getStartTime() const return m_start_time; } - - -double FFmpegClocks::getAudioTime() const +void FFmpegClocks::setPauseTime(double pause_time) { - return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_audio_delay; + m_pause_time += pause_time; +} + +void FFmpegClocks::setSeekTime(double seek_time) +{ + m_seek_time = getAudioTime() - seek_time; } +double FFmpegClocks::getAudioTime() const +{ + return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_pause_time - m_audio_delay; +} + + +double FFmpegClocks::getCurrentTime() +{ + if(!m_paused) + m_last_current_time = getAudioTime() - m_seek_time; // synced with audio + + return m_last_current_time; +} + } // namespace osgFFmpeg diff --git a/src/osgPlugins/ffmpeg/FFmpegClocks.hpp b/src/osgPlugins/ffmpeg/FFmpegClocks.hpp index 2f54a5f76..5fa165228 100644 --- a/src/osgPlugins/ffmpeg/FFmpegClocks.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegClocks.hpp @@ -22,6 +22,7 @@ public: FFmpegClocks(); void reset(double start_time); + void pause(bool pause); void rewindAudio(); void rewindVideo(); @@ -35,6 +36,9 @@ public: double videoRefreshSchedule(double pts); double getStartTime() const; + double getCurrentTime(); + void setPauseTime(double pause_time); + void setSeekTime(double seek_time); private: @@ -49,15 +53,20 @@ private: double m_video_clock; double m_start_time; + double m_pause_time; + double m_seek_time; double m_last_frame_delay; double m_last_frame_pts; double m_last_actual_delay; double m_frame_time; double m_audio_buffer_end_pts; double m_audio_delay; - Timer m_audio_timer; - bool m_audio_disabled; - bool m_rewind; + Timer m_audio_timer; + bool m_audio_disabled; + bool m_rewind; + bool m_paused; + double m_last_current_time; + }; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp index e6119c4b8..796d642d7 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp @@ -213,15 +213,6 @@ void FFmpegDecoder::pause() m_state = PAUSE; } -void FFmpegDecoder::resume() -{ - m_pending_packet.clear(); - - flushAudioQueue(); - flushVideoQueue(); - m_state = NORMAL; -} - void FFmpegDecoder::findAudioStream() { for (unsigned int i = 0; i < m_format_context->nb_streams; ++i) @@ -294,7 +285,10 @@ bool FFmpegDecoder::readNextPacketNormal() { // If we reach the end of the stream, change the decoder state if (loop()) + { + m_clocks.reset(m_start); rewindButDontFlushQueues(); + } else m_state = END_OF_STREAM; @@ -393,6 +387,8 @@ void FFmpegDecoder::seekButDontFlushQueues(double time) const int64_t pos = int64_t(m_clocks.getStartTime()+time * double(AV_TIME_BASE)); const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base); + m_clocks.setSeekTime(time); + if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0) throw std::runtime_error("av_seek_frame failed()"); diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp index b351d2b7a..4d078deb6 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp @@ -70,12 +70,12 @@ public: void rewind(); void seek(double time); void pause(); - void resume(); void loop(bool loop); bool loop() const; double duration() const; + double reference(); FFmpegDecoderAudio & audio_decoder(); FFmpegDecoderVideo & video_decoder(); @@ -108,24 +108,24 @@ protected: void seekButDontFlushQueues(double time); FormatContextPtr m_format_context; - AVStream * m_audio_stream; - AVStream * m_video_stream; + AVStream * m_audio_stream; + AVStream * m_video_stream; int m_audio_index; int m_video_index; FFmpegClocks m_clocks; FFmpegPacket m_pending_packet; - PacketQueue m_audio_queue; - PacketQueue m_video_queue; + PacketQueue m_audio_queue; + PacketQueue m_video_queue; - FFmpegDecoderAudio m_audio_decoder; - FFmpegDecoderVideo m_video_decoder; + FFmpegDecoderAudio m_audio_decoder; + FFmpegDecoderVideo m_video_decoder; - double m_duration; - double m_start; + double m_duration; + double m_start; - State m_state; + State m_state; bool m_loop; }; @@ -150,6 +150,11 @@ inline double FFmpegDecoder::duration() const return double(m_format_context->duration) / AV_TIME_BASE; } +inline double FFmpegDecoder::reference() +{ + return m_clocks.getCurrentTime(); +} + inline FFmpegDecoderAudio & FFmpegDecoder::audio_decoder() { diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp index 3fda745cd..86c1503d2 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp @@ -25,6 +25,7 @@ FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clo m_audio_buf_size(0), m_audio_buf_index(0), m_end_of_stream(false), + m_paused(true), m_exit(false) { @@ -88,6 +89,13 @@ void FFmpegDecoderAudio::open(AVStream * const stream) } } +void FFmpegDecoderAudio::pause(bool pause) +{ + if(pause) + m_paused = true; + else + m_paused = false; +} void FFmpegDecoderAudio::close(bool waitForThreadToExit) { @@ -184,6 +192,21 @@ void FFmpegDecoderAudio::decodeLoop() while (! m_exit) { + + if(m_paused) + { + m_clocks.pause(true); + m_pause_timer.setStartTick(); + + while(m_paused) + { + microSleep(10000); + } + + m_clocks.setPauseTime(m_pause_timer.time_s()); + m_clocks.pause(false); + } + // If skipping audio, make sure the audio stream is still consumed. if (skip_audio) { @@ -193,7 +216,6 @@ void FFmpegDecoderAudio::decodeLoop() if (packet.valid()) packet.clear(); } - // Else, just idle in this thread. // Note: If m_audio_sink has an audio callback, this thread will still be awaken // from time to time to refill the audio buffer. diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp index 8e0da0b7f..ecc260142 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp @@ -4,6 +4,8 @@ #include +#include + #include "FFmpegClocks.hpp" #include "FFmpegPacket.hpp" @@ -29,6 +31,7 @@ public: ~FFmpegDecoderAudio(); void open(AVStream * stream); + void pause(bool pause); void close(bool waitForThreadToExit); virtual void run(); @@ -51,26 +54,29 @@ private: size_t decodeFrame(void * buffer, size_t size); - PacketQueue & m_packets; - FFmpegClocks & m_clocks; - AVStream * m_stream; - AVCodecContext * m_context; - FFmpegPacket m_packet; - const uint8_t * m_packet_data; - int m_bytes_remaining; + PacketQueue & m_packets; + FFmpegClocks & m_clocks; + AVStream * m_stream; + AVCodecContext * m_context; + FFmpegPacket m_packet; + const uint8_t * m_packet_data; + int m_bytes_remaining; - Buffer m_audio_buffer; - size_t m_audio_buf_size; - size_t m_audio_buf_index; + Buffer m_audio_buffer; + size_t m_audio_buf_size; + size_t m_audio_buf_index; - int m_frequency; - int m_nb_channels; - osg::AudioStream::SampleFormat m_sample_format; + int m_frequency; + int m_nb_channels; + osg::AudioStream::SampleFormat m_sample_format; - SinkPtr m_audio_sink; + SinkPtr m_audio_sink; - bool m_end_of_stream; - volatile bool m_exit; + osg::Timer m_pause_timer; + + bool m_end_of_stream; + bool m_paused; + volatile bool m_exit; }; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp index 4052536b4..8b2e69d7d 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp @@ -20,6 +20,7 @@ FFmpegDecoderVideo::FFmpegDecoderVideo(PacketQueue & packets, FFmpegClocks & clo m_writeBuffer(0), m_user_data(0), m_publish_func(0), + m_paused(true), m_exit(false) #ifdef USE_SWSCALE ,m_swscale_ctx(0) @@ -117,6 +118,13 @@ void FFmpegDecoderVideo::close(bool waitForThreadToExit) } } +void FFmpegDecoderVideo::pause(bool pause) +{ + if(pause) + m_paused = true; + else + m_paused = false; +} void FFmpegDecoderVideo::run() { @@ -194,6 +202,11 @@ void FFmpegDecoderVideo::decodeLoop() } } + while(m_paused && !m_exit) + { + microSleep(10000); + } + // Get the next packet pts = 0; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp index 6ba9d2c45..7883b177c 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.hpp @@ -65,6 +65,7 @@ public: ~FFmpegDecoderVideo(); void open(AVStream * stream); + void pause(bool pause); void close(bool waitForThreadToExit); virtual void run(); @@ -120,6 +121,7 @@ private: size_t m_next_frame_index; bool m_alpha_channel; + bool m_paused; volatile bool m_exit; #ifdef USE_SWSCALE diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp index ab179a023..e25e1fb16 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp @@ -159,6 +159,12 @@ double FFmpegImageStream::getLength() const } +double FFmpegImageStream::getReferenceTime () const +{ + return m_decoder->reference(); +} + + double FFmpegImageStream::getFrameRate() const { @@ -264,7 +270,8 @@ void FFmpegImageStream::cmdPlay() if (! m_decoder->video_decoder().isRunning()) m_decoder->video_decoder().start(); - m_decoder->resume(); + m_decoder->video_decoder().pause(false); + m_decoder->audio_decoder().pause(false); } _status = PLAYING; @@ -276,7 +283,8 @@ void FFmpegImageStream::cmdPause() { if (_status == PLAYING) { - m_decoder->pause(); + m_decoder->video_decoder().pause(true); + m_decoder->audio_decoder().pause(true); } _status = PAUSED; diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp index dec9cea1d..2aaa0784f 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp @@ -34,6 +34,7 @@ namespace osgFFmpeg virtual void quit(bool waitForThreadToExit = true); virtual double getLength() const; + virtual double getReferenceTime () const; virtual double getFrameRate() const; virtual bool isImageTranslucent() const; @@ -71,7 +72,7 @@ namespace osgFFmpeg Mutex m_mutex; Condition m_frame_published_cond; - bool m_frame_published_flag; + bool m_frame_published_flag; double m_seek_time; };