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 *****************************************************************************/
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
);
45 vlc_player_SendTimerSourceUpdates(vlc_player_t
*player
,
46 struct vlc_player_timer_source
*source
,
48 const struct vlc_player_timer_point
*point
)
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
;
69 vlc_player_SendSmpteTimerSourceUpdates(vlc_player_t
*player
,
70 struct vlc_player_timer_source
*source
,
71 const struct vlc_player_timer_point
*point
)
74 vlc_player_timer_id
*timer
;
76 struct vlc_player_timer_smpte_timecode tc
;
77 unsigned long framenum
;
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. */
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
106 frame_rate
= source
->smpte
.df_fps
;
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
)
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
);
137 vlc_player_UpdateSmpteTimerFPS(vlc_player_t
*player
,
138 struct vlc_player_timer_source
*source
,
139 unsigned frame_rate
, unsigned frame_rate_base
)
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 */
165 source
->smpte
.df
= 0;
167 while (max_frames
!= 0)
170 source
->smpte
.frame_resolution
++;
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
185 struct vlc_player_timer_source
*bestsource
= &player
->timer
.best_source
;
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
)
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
;
203 case VLC_PLAYER_TIMER_STATE_PAUSED
:
205 assert(system_date
!= VLC_TICK_INVALID
);
209 case VLC_PLAYER_TIMER_STATE_PLAYING
:
213 player
->timer
.state
= state
;
217 vlc_mutex_unlock(&player
->timer
.lock
);
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
);
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
;
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
;
255 source
->point
.position
= player
->timer
.input_position
;
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
;
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
;
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
;
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
);
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
;
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
,
342 if (!vlc_list_is_empty(&source
->listeners
))
343 vlc_player_SendTimerSourceUpdates(player
, source
, force_update
,
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
,
366 if (point
->ts
!= player
->timer
.last_ts
&& source
->smpte
.frame_rate
!= 0)
368 vlc_player_UpdateTimerSource(player
, source
, point
->rate
, point
->ts
,
371 if (!vlc_list_is_empty(&source
->listeners
))
372 vlc_player_SendSmpteTimerSourceUpdates(player
, source
,
377 player
->timer
.last_ts
= point
->ts
;
379 vlc_mutex_unlock(&player
->timer
.lock
);
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
);
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
);
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
);
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
));
427 timer
->period
= min_period
;
428 timer
->last_update_date
= VLC_TICK_INVALID
;
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
);
439 vlc_player_timer_id
*
440 vlc_player_AddSmpteTimer(vlc_player_t
*player
,
441 const struct vlc_player_timer_smpte_cbs
*cbs
,
444 assert(cbs
&& cbs
->on_update
);
446 struct vlc_player_timer_id
*timer
= malloc(sizeof(*timer
));
449 timer
->period
= VLC_TICK_INVALID
;
450 timer
->last_update_date
= VLC_TICK_INVALID
;
451 timer
->smpte_cbs
= cbs
;
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
);
462 vlc_player_RemoveTimer(vlc_player_t
*player
, vlc_player_timer_id
*timer
)
466 vlc_mutex_lock(&player
->timer
.lock
);
467 vlc_list_remove(&timer
->node
);
468 vlc_mutex_unlock(&player
->timer
.lock
);
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
)
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
)
492 if (unlikely(ts
< VLC_TICK_0
))
495 if (point
->length
!= VLC_TICK_INVALID
)
497 pos
+= drift
/ (float) point
->length
;
498 if (unlikely(pos
< 0.f
))
502 if (ts
> point
->length
)
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
)
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
;
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
);
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
));