input: rename vlc_input_event_times variables
[vlc.git] / src / player / timer.c
blob48005571494e7f3a7121fdb884a3256a59d32b0f
1 /*****************************************************************************
2 * player.c: Player interface
3 *****************************************************************************
4 * Copyright © 2018 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <limits.h>
27 #include "player.h"
29 void
30 vlc_player_ResetTimer(vlc_player_t *player)
32 vlc_mutex_lock(&player->timer.lock);
34 player->timer.state = VLC_PLAYER_TIMER_STATE_DISCONTINUITY;
35 player->timer.input_length = VLC_TICK_INVALID;
36 player->timer.input_normal_time = VLC_TICK_0;
37 player->timer.last_ts = VLC_TICK_INVALID;
38 player->timer.input_position = 0.f;
39 player->timer.smpte_source.smpte.last_framenum = ULONG_MAX;
41 vlc_mutex_unlock(&player->timer.lock);
44 static void
45 vlc_player_SendTimerSourceUpdates(vlc_player_t *player,
46 struct vlc_player_timer_source *source,
47 bool force_update,
48 const struct vlc_player_timer_point *point)
50 (void) player;
51 vlc_player_timer_id *timer;
53 vlc_list_foreach(timer, &source->listeners, node)
55 /* Respect refresh delay of the timer */
56 if (force_update || timer->period == VLC_TICK_INVALID
57 || timer->last_update_date == VLC_TICK_INVALID
58 || point->system_date == INT64_MAX /* always update when paused */
59 || point->system_date - timer->last_update_date >= timer->period)
61 timer->cbs->on_update(point, timer->data);
62 timer->last_update_date = point->system_date == INT64_MAX ?
63 VLC_TICK_INVALID : point->system_date;
68 static void
69 vlc_player_SendSmpteTimerSourceUpdates(vlc_player_t *player,
70 struct vlc_player_timer_source *source,
71 const struct vlc_player_timer_point *point)
73 (void) player;
74 vlc_player_timer_id *timer;
76 struct vlc_player_timer_smpte_timecode tc;
77 unsigned long framenum;
78 unsigned frame_rate;
79 unsigned frame_rate_base;
81 if (source->smpte.df > 0)
83 /* Use the exact SMPTE framerate that can be different from the input
84 * source (at demuxer/decoder level) */
85 assert(source->smpte.df_fps == 30 || source->smpte.df_fps == 60);
86 frame_rate = source->smpte.df_fps * 1000;
87 frame_rate_base = 1001;
89 /* Convert the ts to a frame number */
90 framenum = round(point->ts * frame_rate
91 / (double) frame_rate_base / VLC_TICK_FROM_SEC(1));
93 /* Drop 2 or 4 frames every minutes except every 10 minutes in order to
94 * make one hour of timecode match one hour on the clock. */
95 ldiv_t res;
96 res = ldiv(framenum, source->smpte.frames_per_10mins);
98 framenum += (9 * source->smpte.df * res.quot)
99 + (source->smpte.df * ((res.rem - source->smpte.df)
100 / (source->smpte.frames_per_10mins / 10)));
102 tc.drop_frame = true;
104 /* Use 30 or 60 framerates for the next frames/seconds/minutes/hours
105 * calculaton */
106 frame_rate = source->smpte.df_fps;
107 frame_rate_base = 1;
109 else
111 frame_rate = source->smpte.frame_rate;
112 frame_rate_base = source->smpte.frame_rate_base;
114 /* Convert the ts to a frame number */
115 framenum = round(point->ts * frame_rate
116 / (double) frame_rate_base / VLC_TICK_FROM_SEC(1));
118 tc.drop_frame = false;
120 if (framenum == source->smpte.last_framenum)
121 return;
123 source->smpte.last_framenum = framenum;
125 tc.frames = framenum % (frame_rate / frame_rate_base);
126 tc.seconds = (framenum * frame_rate_base / frame_rate) % 60;
127 tc.minutes = (framenum * frame_rate_base / frame_rate / 60) % 60;
128 tc.hours = framenum * frame_rate_base / frame_rate / 3600;
130 tc.frame_resolution = source->smpte.frame_resolution;
132 vlc_list_foreach(timer, &source->listeners, node)
133 timer->smpte_cbs->on_update(&tc, timer->data);
136 static void
137 vlc_player_UpdateSmpteTimerFPS(vlc_player_t *player,
138 struct vlc_player_timer_source *source,
139 unsigned frame_rate, unsigned frame_rate_base)
141 (void) player;
142 source->smpte.frame_rate = frame_rate;
143 source->smpte.frame_rate_base = frame_rate_base;
145 /* Calculate everything that will be needed to create smpte timecodes */
146 source->smpte.frame_resolution = 0;
148 unsigned max_frames = frame_rate / frame_rate_base;
150 if (max_frames == 29 && (100 * frame_rate / frame_rate_base) == 2997)
152 /* SMPTE Timecode: 29.97 fps DF */
153 source->smpte.df = 2;
154 source->smpte.df_fps = 30;
155 source->smpte.frames_per_10mins = 17982; /* 29.97 * 60 * 10 */
157 else if (max_frames == 59 && (100 * frame_rate / frame_rate_base) == 5994)
159 /* SMPTE Timecode: 59.94 fps DF */
160 source->smpte.df = 4;
161 source->smpte.df_fps = 60;
162 source->smpte.frames_per_10mins = 35964; /* 59.94 * 60 * 10 */
164 else
165 source->smpte.df = 0;
167 while (max_frames != 0)
169 max_frames /= 10;
170 source->smpte.frame_resolution++;
174 void
175 vlc_player_UpdateTimerState(vlc_player_t *player, vlc_es_id_t *es_source,
176 enum vlc_player_timer_state state,
177 vlc_tick_t system_date)
179 vlc_mutex_lock(&player->timer.lock);
181 /* Discontinuity is signalled by all output clocks and the input.
182 * discard the event if it was already signalled or not on the good
183 * es_source. */
184 bool notify = false;
185 struct vlc_player_timer_source *bestsource = &player->timer.best_source;
187 switch(state)
189 case VLC_PLAYER_TIMER_STATE_DISCONTINUITY:
190 assert(system_date == VLC_TICK_INVALID);
191 for (size_t i = 0; i < VLC_PLAYER_TIMER_TYPE_COUNT; ++i)
193 struct vlc_player_timer_source *source = &player->timer.sources[i];
194 if (source->es != es_source)
195 continue;
196 /* signal discontinuity only on best source */
197 if (source->point.system_date != VLC_TICK_INVALID)
198 notify = bestsource->es == es_source;
199 source->point.system_date = VLC_TICK_INVALID;
201 break;
203 case VLC_PLAYER_TIMER_STATE_PAUSED:
204 notify = true;
205 assert(system_date != VLC_TICK_INVALID);
206 break;
208 default:
209 case VLC_PLAYER_TIMER_STATE_PLAYING:
210 break;
213 player->timer.state = state;
215 if (!notify)
217 vlc_mutex_unlock(&player->timer.lock);
218 return;
221 vlc_player_timer_id *timer;
222 vlc_list_foreach(timer, &bestsource->listeners, node)
224 timer->last_update_date = VLC_TICK_INVALID;
225 timer->cbs->on_discontinuity(system_date, timer->data);
228 vlc_mutex_unlock(&player->timer.lock);
231 static void
232 vlc_player_UpdateTimerSource(vlc_player_t *player,
233 struct vlc_player_timer_source *source,
234 double rate, vlc_tick_t ts, vlc_tick_t system_date)
236 assert(ts >= VLC_TICK_0);
237 assert(player->timer.input_normal_time >= VLC_TICK_0);
239 source->point.rate = rate;
240 source->point.ts = ts - player->timer.input_normal_time + VLC_TICK_0;
241 source->point.length = player->timer.input_length;
243 /* Put an invalid date for the first point in order to disable
244 * interpolation (behave as paused), indeed, we should wait for one more
245 * point before starting interpolation (ideally, it should be more) */
246 if (source->point.system_date == VLC_TICK_INVALID)
247 source->point.system_date = INT64_MAX;
248 else
249 source->point.system_date = system_date;
251 if (source->point.length != VLC_TICK_INVALID)
252 source->point.position = (ts - player->timer.input_normal_time)
253 / (double) source->point.length;
254 else
255 source->point.position = player->timer.input_position;
258 void
259 vlc_player_UpdateTimer(vlc_player_t *player, vlc_es_id_t *es_source,
260 bool es_source_is_master,
261 const struct vlc_player_timer_point *point,
262 vlc_tick_t normal_time,
263 unsigned frame_rate, unsigned frame_rate_base)
265 struct vlc_player_timer_source *source;
266 assert(point);
267 /* A null source can't be the master */
268 assert(es_source == NULL ? !es_source_is_master : true);
270 vlc_mutex_lock(&player->timer.lock);
272 bool force_update = false;
273 if (!es_source) /* input source */
275 /* Only valid for input sources */
276 if (player->timer.input_normal_time != normal_time)
278 player->timer.input_normal_time = normal_time;
279 player->timer.last_ts = VLC_TICK_INVALID;
280 force_update = true;
282 if (player->timer.input_length != point->length
283 && point->length >= VLC_TICK_0)
285 player->timer.input_length = point->length;
286 player->timer.last_ts = VLC_TICK_INVALID;
287 force_update = true;
289 /* Will likely be overridden by non input source */
290 player->timer.input_position = point->position;
292 if (point->ts == VLC_TICK_INVALID
293 || point->system_date == VLC_TICK_INVALID)
295 /* ts can only be invalid from the input source */
296 vlc_mutex_unlock(&player->timer.lock);
297 return;
301 assert(point->ts != VLC_TICK_INVALID);
303 vlc_tick_t system_date = point->system_date;
304 if (player->timer.state == VLC_PLAYER_TIMER_STATE_PAUSED)
305 system_date = INT64_MAX;
307 /* An update after a discontinuity means that the playback is resumed */
308 if (player->timer.state == VLC_PLAYER_TIMER_STATE_DISCONTINUITY)
309 player->timer.state = VLC_PLAYER_TIMER_STATE_PLAYING;
311 /* Best source priority:
312 * 1/ es_source != NULL when paused (any ES tracks when paused. Indeed,
313 * there is likely no audio update (master) when paused but only video
314 * ones, via vlc_player_NextVideoFrame() for example)
315 * 2/ es_source != NULL + master (from the master ES track)
316 * 3/ es_source != NULL (from the first ES track updated)
317 * 4/ es_source == NULL (from the input)
319 source = &player->timer.best_source;
320 if (!source->es || es_source_is_master
321 || (es_source && player->timer.state == VLC_PLAYER_TIMER_STATE_PAUSED))
322 source->es = es_source;
324 /* Notify the best source */
325 if (source->es == es_source)
327 if (source->point.rate != point->rate)
329 player->timer.last_ts = VLC_TICK_INVALID;
330 force_update = true;
333 /* When paused (INT64_MAX), the same ts can be send more than one time
334 * from the video source, only send it if different in that case. */
335 if (point->ts != player->timer.last_ts
336 || source->point.system_date != system_date
337 || system_date != INT64_MAX)
339 vlc_player_UpdateTimerSource(player, source, point->rate, point->ts,
340 system_date);
342 if (!vlc_list_is_empty(&source->listeners))
343 vlc_player_SendTimerSourceUpdates(player, source, force_update,
344 &source->point);
348 source = &player->timer.smpte_source;
349 /* SMPTE source: only the video source */
350 if (!source->es && es_source && vlc_es_id_GetCat(es_source) == VIDEO_ES)
351 source->es = es_source;
353 /* Notify the SMPTE source, also notify when the video output was rendered
354 * while the clock was paused */
355 if (source->es == es_source && source->es)
357 if (frame_rate != 0 && (frame_rate != source->smpte.frame_rate
358 || frame_rate_base != source->smpte.frame_rate_base))
360 assert(frame_rate_base != 0);
361 player->timer.last_ts = VLC_TICK_INVALID;
362 vlc_player_UpdateSmpteTimerFPS(player, source, frame_rate,
363 frame_rate_base);
366 if (point->ts != player->timer.last_ts && source->smpte.frame_rate != 0)
368 vlc_player_UpdateTimerSource(player, source, point->rate, point->ts,
369 system_date);
371 if (!vlc_list_is_empty(&source->listeners))
372 vlc_player_SendSmpteTimerSourceUpdates(player, source,
373 &source->point);
377 player->timer.last_ts = point->ts;
379 vlc_mutex_unlock(&player->timer.lock);
382 void
383 vlc_player_RemoveTimerSource(vlc_player_t *player, vlc_es_id_t *es_source)
385 vlc_mutex_lock(&player->timer.lock);
386 for (size_t i = 0; i < VLC_PLAYER_TIMER_TYPE_COUNT; ++i)
388 struct vlc_player_timer_source *source = &player->timer.sources[i];
389 if (source->es == es_source)
391 /* Discontinuity should have been already signaled */
392 assert(source->point.system_date == VLC_TICK_INVALID);
393 source->es = NULL;
396 vlc_mutex_unlock(&player->timer.lock);
400 vlc_player_GetTimerPoint(vlc_player_t *player, vlc_tick_t system_now,
401 vlc_tick_t *out_ts, float *out_pos)
403 vlc_mutex_lock(&player->timer.lock);
404 if (player->timer.best_source.point.system_date == VLC_TICK_INVALID)
406 vlc_mutex_unlock(&player->timer.lock);
407 return VLC_EGENERIC;
409 int ret =
410 vlc_player_timer_point_Interpolate(&player->timer.best_source.point,
411 system_now, out_ts, out_pos);
413 vlc_mutex_unlock(&player->timer.lock);
414 return ret;
417 vlc_player_timer_id *
418 vlc_player_AddTimer(vlc_player_t *player, vlc_tick_t min_period,
419 const struct vlc_player_timer_cbs *cbs, void *data)
421 assert(min_period >= VLC_TICK_0 || min_period == VLC_TICK_INVALID);
422 assert(cbs && cbs->on_update);
424 struct vlc_player_timer_id *timer = malloc(sizeof(*timer));
425 if (!timer)
426 return NULL;
427 timer->period = min_period;
428 timer->last_update_date = VLC_TICK_INVALID;
429 timer->cbs = cbs;
430 timer->data = data;
432 vlc_mutex_lock(&player->timer.lock);
433 vlc_list_append(&timer->node, &player->timer.best_source.listeners);
434 vlc_mutex_unlock(&player->timer.lock);
436 return timer;
439 vlc_player_timer_id *
440 vlc_player_AddSmpteTimer(vlc_player_t *player,
441 const struct vlc_player_timer_smpte_cbs *cbs,
442 void *data)
444 assert(cbs && cbs->on_update);
446 struct vlc_player_timer_id *timer = malloc(sizeof(*timer));
447 if (!timer)
448 return NULL;
449 timer->period = VLC_TICK_INVALID;
450 timer->last_update_date = VLC_TICK_INVALID;
451 timer->smpte_cbs = cbs;
452 timer->data = data;
454 vlc_mutex_lock(&player->timer.lock);
455 vlc_list_append(&timer->node, &player->timer.smpte_source.listeners);
456 vlc_mutex_unlock(&player->timer.lock);
458 return timer;
461 void
462 vlc_player_RemoveTimer(vlc_player_t *player, vlc_player_timer_id *timer)
464 assert(timer);
466 vlc_mutex_lock(&player->timer.lock);
467 vlc_list_remove(&timer->node);
468 vlc_mutex_unlock(&player->timer.lock);
470 free(timer);
474 vlc_player_timer_point_Interpolate(const struct vlc_player_timer_point *point,
475 vlc_tick_t system_now,
476 vlc_tick_t *out_ts, float *out_pos)
478 assert(point);
479 assert(system_now > 0);
480 assert(out_ts || out_pos);
482 /* A system_date == INT64_MAX means the clock was paused when it updated
483 * this point, so there is nothing to interpolate */
484 const vlc_tick_t drift = point->system_date == INT64_MAX ? 0
485 : (system_now - point->system_date) * point->rate;
486 vlc_tick_t ts = point->ts;
487 float pos = point->position;
489 if (ts != VLC_TICK_INVALID)
491 ts += drift;
492 if (unlikely(ts < VLC_TICK_0))
493 return VLC_EGENERIC;
495 if (point->length != VLC_TICK_INVALID)
497 pos += drift / (float) point->length;
498 if (unlikely(pos < 0.f))
499 return VLC_EGENERIC;
500 if (pos > 1.f)
501 pos = 1.f;
502 if (ts > point->length)
503 ts = point->length;
506 if (out_ts)
507 *out_ts = ts;
508 if (out_pos)
509 *out_pos = pos;
511 return VLC_SUCCESS;
514 vlc_tick_t
515 vlc_player_timer_point_GetNextIntervalDate(const struct vlc_player_timer_point *point,
516 vlc_tick_t system_now,
517 vlc_tick_t interpolated_ts,
518 vlc_tick_t next_interval)
520 assert(point);
521 assert(system_now > 0);
522 assert(next_interval != VLC_TICK_INVALID);
524 const unsigned ts_interval = interpolated_ts / next_interval;
525 const vlc_tick_t ts_next_interval =
526 ts_interval * next_interval + next_interval;
528 return (ts_next_interval - interpolated_ts) / point->rate + system_now;
531 void
532 vlc_player_InitTimer(vlc_player_t *player)
534 vlc_mutex_init(&player->timer.lock);
536 for (size_t i = 0; i < VLC_PLAYER_TIMER_TYPE_COUNT; ++i)
538 vlc_list_init(&player->timer.sources[i].listeners);
539 player->timer.sources[i].point.system_date = VLC_TICK_INVALID;
540 player->timer.sources[i].es = NULL;
542 vlc_player_ResetTimer(player);
545 void
546 vlc_player_DestroyTimer(vlc_player_t *player)
548 for (size_t i = 0; i < VLC_PLAYER_TIMER_TYPE_COUNT; ++i)
549 assert(vlc_list_is_empty(&player->timer.sources[i].listeners));