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