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