From Julen Garcia,"I've been lately working also with the ffmpeg plugin and I implemented pause(), seek() and getReferenceTime(). I think that I have solved the internal clock issues (maybe not in the most elegant way :?"
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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()");
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <OpenThreads/Thread>
|
||||
|
||||
#include <osg/Timer>
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user