1 
2 #include "FFmpegClocks.hpp"
3 
4 #include <cmath>
5 #include <algorithm>
6 
7 // DEBUG
8 //#include <iostream>
9 
10 
11 namespace osgFFmpeg {
12 
13 
14 
15 namespace
16 {
17 
18     const double AV_SYNC_THRESHOLD = 0.01;
19     const double AV_NOSYNC_THRESHOLD = 10.0;
20 
clamp(const double value,const double min,const double max)21     inline double clamp(const double value, const double min, const double max)
22     {
23         return (std::min)((std::max)(value, min), max);
24     }
25 
26 }
27 
28 
29 
FFmpegClocks()30 FFmpegClocks::FFmpegClocks() :
31     m_video_clock(0),
32     m_start_time(0),
33     m_pause_time(0),
34     m_seek_time(0),
35     m_last_frame_delay(0.040),
36     m_last_frame_pts(0),
37     m_last_actual_delay(0),
38     m_frame_time(0),
39     m_audio_buffer_end_pts(0),
40     m_audio_delay(0.0),
41     m_audio_disabled(false),
42     m_paused(false),
43     m_last_current_time(0.0)
44 {
45 
46 }
47 
48 
49 
reset(const double start_time)50 void FFmpegClocks::reset(const double start_time)
51 {
52     ScopedLock lock(m_mutex);
53 
54     m_video_clock = start_time;
55 
56     m_start_time = start_time;
57     m_last_frame_delay = 0.040;
58     m_last_frame_pts = start_time - m_last_frame_delay;
59     m_frame_time = start_time;
60 
61     m_pause_time = 0;
62     m_seek_time = 0;
63 
64     m_audio_buffer_end_pts = start_time;
65     m_audio_timer.setStartTick();
66 }
67 
pause(bool pause)68 void FFmpegClocks::pause(bool pause)
69 {
70     if(pause)
71         m_paused = true;
72     else
73     {
74         m_paused = false;
75         if(!m_audio_disabled) m_audio_timer.setStartTick();
76     }
77 }
78 
79 
80 
rewind()81 void FFmpegClocks::rewind()
82 {
83     ScopedLock lock(m_mutex);
84 
85     m_pause_time = 0;
86     m_seek_time = 0;
87 
88     m_audio_buffer_end_pts = m_start_time;
89     m_audio_timer.setStartTick();
90 
91     m_last_frame_delay = 0.040;
92     m_frame_time = m_start_time;
93 
94     if (m_audio_disabled)
95         return;
96 
97     m_video_clock = m_start_time;
98 }
99 
seek(double seek_time)100 void FFmpegClocks::seek(double seek_time)
101 {
102     ScopedLock lock(m_mutex);
103 
104     m_video_clock = seek_time;
105     m_last_frame_delay = 0.040;
106     m_frame_time = seek_time;
107 }
108 
109 
audioSetBufferEndPts(const double pts)110 void FFmpegClocks::audioSetBufferEndPts(const double pts)
111 {
112     ScopedLock lock(m_mutex);
113 
114     m_audio_buffer_end_pts = pts;
115     m_audio_timer.setStartTick();
116 }
117 
118 
119 
audioAdjustBufferEndPts(double increment)120 void FFmpegClocks::audioAdjustBufferEndPts(double increment)
121 {
122     ScopedLock lock(m_mutex);
123 
124     m_audio_buffer_end_pts += increment;
125     m_audio_timer.setStartTick();
126 }
127 
128 
129 
audioSetDelay(const double delay)130 void FFmpegClocks::audioSetDelay(const double delay)
131 {
132     m_audio_delay = delay;
133 }
134 
135 
136 
audioDisable()137 void FFmpegClocks::audioDisable()
138 {
139     ScopedLock lock(m_mutex);
140 
141     m_audio_disabled = true;
142 }
143 
144 
145 
videoSynchClock(const AVFrame * const frame,const double time_base,double pts)146 double FFmpegClocks::videoSynchClock(const AVFrame * const frame, const double time_base, double pts)
147 {
148     if (pts != 0)
149     {
150         // If we have a PTS, set the video clock to it.
151         m_video_clock = pts;
152     }
153     else
154     {
155         // Else, if we don't, use the video clock value.
156         pts = m_video_clock;
157     }
158 
159     // Update the video clock to take into account the frame delay
160 
161     double frame_delay = time_base;
162     frame_delay += frame->repeat_pict * (frame_delay * 0.5);
163 
164     m_video_clock += frame_delay;
165 
166     return pts;
167 }
168 
169 
170 
videoRefreshSchedule(const double pts)171 double FFmpegClocks::videoRefreshSchedule(const double pts)
172 {
173     ScopedLock lock(m_mutex);
174 
175     // DEBUG
176     //std::cerr << "ftime / dpts / delay / audio_time / adelay:  ";
177 
178     double delay = pts - m_last_frame_pts;
179 
180 
181     //std::cerr << m_frame_time << "  /  ";
182     //std::cerr << delay << "  /  ";
183 
184 
185     // If incorrect delay, use previous one
186 
187     if (delay <= 0.0 || delay >= 1.0)
188     {
189         delay = m_last_frame_delay;
190         if(!m_audio_disabled) m_frame_time = pts - delay;
191     }
192 
193 
194     // Save for next time
195     m_last_frame_delay = delay;
196     m_last_frame_pts = pts;
197 
198     // Update the delay to synch to the audio stream
199 
200     // Ideally the frame time should be incremented after the actual delay is computed.
201     // But because of the sound latency, it seems better to keep some latency in the video too.
202     m_frame_time += delay;
203 
204     const double audio_time = getAudioTime();
205     const double actual_delay = clamp(m_frame_time - audio_time, -0.5*delay, 2.5*delay);
206 
207     //m_frame_time += delay;
208 
209 
210     // DEBUG
211     //std::cerr << delay << "  /  ";
212     //std::cerr << audio_time << "  /  ";
213     //std::cerr << actual_delay << std::endl;
214 
215     m_last_actual_delay = actual_delay;
216 
217     return actual_delay;
218 }
219 
220 
221 
getStartTime() const222 double FFmpegClocks::getStartTime() const
223 {
224     return m_start_time;
225 }
226 
setPauseTime(double pause_time)227 void FFmpegClocks::setPauseTime(double pause_time)
228 {
229     m_pause_time += pause_time;
230 }
231 
setSeekTime(double seek_time)232 void FFmpegClocks::setSeekTime(double seek_time)
233 {
234     m_seek_time += getAudioTime() - seek_time;
235 }
236 
237 
238 
getAudioTime() const239 double FFmpegClocks::getAudioTime() const
240 {
241     if(m_audio_disabled)
242         return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_pause_time - m_audio_delay - m_seek_time;
243     else
244         return m_audio_buffer_end_pts + m_audio_timer.time_s() - m_audio_delay;
245 }
246 
247 
getCurrentTime()248 double FFmpegClocks::getCurrentTime()
249 {
250     if(!m_paused)
251         m_last_current_time = getAudioTime();
252 
253     return m_last_current_time;
254 }
255 
256 } // namespace osgFFmpeg
257