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