diff --git a/include/vlc/libvlc_media_player.h b/include/vlc/libvlc_media_player.h index 2a0927e754..cdb8270110 100644 --- a/include/vlc/libvlc_media_player.h +++ b/include/vlc/libvlc_media_player.h @@ -2652,6 +2652,158 @@ LIBVLC_API int libvlc_media_player_set_role(libvlc_media_player_t *p_mi, /** @} audio */ +/** \defgroup libvlc_media_player_watch_time LibVLC media player time watch API + * @{ + */ + +/** + * Media Player timer point + * + * \note ts and system_date values should not be used directly by the user. + * libvlc_media_player_time_point_interpolate() will read these values and + * return an interpolated ts. + * + * @see libvlc_media_player_watch_time_on_update + */ +typedef struct libvlc_media_player_time_point_t +{ + /** Position in the range [0.0f;1.0] */ + double position; + /** Rate of the player */ + double rate; + /** Valid time >= 0 or -1 */ + libvlc_time_t ts; + /** Valid length >= 1 or 0 */ + libvlc_time_t length; + /** + * System date of this record (always valid). + * Based on libvlc_clock(). This date can be in the future or in the past. + * The special value of INT64_MAX mean that the clock was paused when this + * point was updated. In that case, + * libvlc_media_player_time_point_interpolate() will return the current + * ts/pos of this point (there is nothing to interpolate). + * */ + libvlc_time_t system_date; +} libvlc_media_player_time_point_t; + +/** + * Callback prototype that notify when the player state or time changed. + * + * Get notified when the time is updated by the input or output source. The + * input source is the 'demux' or the 'access_demux'. The output source are + * audio and video outputs: an update is received each time a video frame is + * displayed or an audio sample is written. The delay between each updates may + * depend on the input and source type (it can be every 5ms, 30ms, 1s or + * 10s...). Users of this timer may need to update the position at a higher + * frequency from their own mainloop via + * libvlc_media_player_time_point_interpolate(). + * + * \warning It is forbidden to call any Media Player functions from here. + * + * \param value always valid, the time corresponding to the state + * \param data opaque pointer set by libvlc_media_player_watch_time() + */ +typedef void (*libvlc_media_player_watch_time_on_update)( + const libvlc_media_player_time_point_t *value, void *data); + +/** + * Callback prototype that notify when the player is paused or a discontinuity + * occurred. + * + * Likely caused by seek from the user or because the playback is stopped. The + * player user should stop its "interpolate" timer. + * + * \warning It is forbidden to call any Media Player functions from here. + * + * \param system_date system date of this event, only valid (> 0) when paused. It + * can be used to interpolate the last updated point to this date in order + * to get the last paused ts/position. + * \param data opaque pointer set by libvlc_media_player_watch_time() + */ +typedef void (*libvlc_media_player_watch_time_on_discontinuity)( + libvlc_time_t system_date, void *data); + +/** + * Watch for times updates + * + * \warning Only one watcher can be registered at a time. Calling this function + * a second time (if libvlc_media_player_unwatch_time() was not called + * in-between) will fail. + * + * \param p_mi the media player + * \param min_period corresponds to the minimum period between each updates, + * use it to avoid flood from too many source updates, set it to 0 to receive + * all updates. + * \param on_update callback to listen to update events (must not be NULL) + * \param on_discontinuity callback to listen to discontinuity events (can be + * be NULL) + * \param cbs_data opaque pointer used by the callbacks + * \return 0 on success, -1 on error (allocation error, or if already watching) + * \version LibVLC 4.0.0 or later + */ +LIBVLC_API int +libvlc_media_player_watch_time(libvlc_media_player_t *p_mi, + libvlc_time_t min_period, + libvlc_media_player_watch_time_on_update on_update, + libvlc_media_player_watch_time_on_discontinuity on_discontinuity, + void *cbs_data); + +/** + * Unwatch time updates + * + * \param p_mi the media player + * \version LibVLC 4.0.0 or later + */ +LIBVLC_API void +libvlc_media_player_unwatch_time(libvlc_media_player_t *p_mi); + +/** + * Interpolate a timer value to now + + * \param point time update obtained via the + * libvlc_media_player_watch_time_on_update() callback + * \param system_now current system date, returned by libvlc_clock() + * \param out_ts pointer where to set the interpolated ts, subtract this time + * with VLC_TICK_0 to get the original value. + * \param out_pos pointer where to set the interpolated position + * \return 0 in case of success, -1 if the interpolated ts is negative (could + * happen during the buffering step) + * \version LibVLC 4.0.0 or later + */ +LIBVLC_API int +libvlc_media_player_time_point_interpolate(const libvlc_media_player_time_point_t *point, + libvlc_time_t system_now, + libvlc_time_t *out_ts, double *out_pos); + +/** + * Get the date of the next interval + * + * Can be used to setup an UI timer in order to update some widgets at specific + * interval. A next_interval of VLC_TICK_FROM_SEC(1) can be used to update a + * time widget when the media reaches a new second. + * + * \note The media time doesn't necessarily correspond to the system time, that + * is why this function is needed and uses the rate of the current point. + * + * \param point time update obtained via the + * libvlc_media_player_watch_time_on_update() + * \param system_now same system date used by + * libvlc_media_player_time_point_interpolate() + * \param interpolated_ts ts returned by + * libvlc_media_player_time_point_interpolate() + * \param next_interval next interval + * \return the absolute system date of the next interval, use libvlc_delay() to + * get a relative delay. + * \version LibVLC 4.0.0 or later + */ +LIBVLC_API libvlc_time_t +libvlc_media_player_time_point_get_next_date(const libvlc_media_player_time_point_t *point, + libvlc_time_t system_now, + libvlc_time_t interpolated_ts, + libvlc_time_t next_interval); + +/** @} libvlc_media_player_watch_time */ + /** @} media_player */ # ifdef __cplusplus diff --git a/lib/libvlc.sym b/lib/libvlc.sym index fb6df3dd10..de12fb79fe 100644 --- a/lib/libvlc.sym +++ b/lib/libvlc.sym @@ -193,6 +193,10 @@ libvlc_media_player_select_program_id libvlc_media_player_get_selected_program libvlc_media_player_get_program_from_id libvlc_media_player_get_programlist +libvlc_media_player_watch_time +libvlc_media_player_unwatch_time +libvlc_media_player_time_point_interpolate +libvlc_media_player_time_point_get_next_date libvlc_media_release libvlc_media_retain libvlc_media_save_meta diff --git a/lib/media_player.c b/lib/media_player.c index 333126f700..ddbeb2c4ad 100644 --- a/lib/media_player.c +++ b/lib/media_player.c @@ -717,6 +717,8 @@ libvlc_media_player_new( libvlc_instance_t *instance ) var_Create (mp, "equalizer-vlcfreqs", VLC_VAR_BOOL); var_Create (mp, "equalizer-bands", VLC_VAR_STRING); + mp->timer.id = NULL; + mp->p_md = NULL; mp->p_libvlc_instance = instance; /* use a reentrant lock to allow calling libvlc functions from callbacks */ @@ -2207,6 +2209,116 @@ int libvlc_media_player_get_role(libvlc_media_player_t *mp) return ret; } +#define PLAYER_TIME_CORE_TO_LIB(point) { \ + .position = point->position, \ + .rate = point->rate, \ + .ts = US_FROM_VLC_TICK(point->ts), \ + .length = US_FROM_VLC_TICK(point->length), \ + .system_date = US_FROM_VLC_TICK(point->system_date), \ +} + +#define PLAYER_TIME_LIB_TO_CORE(point) { \ + .position = point->position, \ + .rate = point->rate, \ + .ts = VLC_TICK_FROM_US(point->ts), \ + .length = VLC_TICK_FROM_US(point->length), \ + .system_date = VLC_TICK_FROM_US(point->system_date), \ +} + +static void player_timer_on_update(const struct vlc_player_timer_point *point, + void *data) +{ + libvlc_media_player_t *p_mi = data; + + const libvlc_media_player_time_point_t libpoint = PLAYER_TIME_CORE_TO_LIB(point); + + p_mi->timer.on_update(&libpoint, p_mi->timer.cbs_data); +} + +static void player_timer_on_discontinuity(vlc_tick_t system_date, void *data) +{ + libvlc_media_player_t *p_mi = data; + + if (p_mi->timer.on_discontinuity == NULL) + return; + + p_mi->timer.on_discontinuity(system_date, p_mi->timer.cbs_data); +} + +int +libvlc_media_player_watch_time(libvlc_media_player_t *p_mi, + libvlc_time_t min_period, + libvlc_media_player_watch_time_on_update on_update, + libvlc_media_player_watch_time_on_discontinuity on_discontinuity, + void *cbs_data) +{ + assert(on_update != NULL); + + static const struct vlc_player_timer_cbs player_timer_cbs = { + .on_update = player_timer_on_update, + .on_discontinuity = player_timer_on_discontinuity, + }; + + vlc_player_t *player = p_mi->player; + vlc_player_Lock(player); + + if (p_mi->timer.id != NULL) + { + libvlc_printerr("libvlc_media_player_watch_time error:" + "already watching for events"); + vlc_player_Unlock(player); + return -1; + } + + p_mi->timer.on_update = on_update; + p_mi->timer.on_discontinuity = on_discontinuity; + p_mi->timer.cbs_data = cbs_data; + + p_mi->timer.id = vlc_player_AddTimer(player, min_period, &player_timer_cbs, p_mi); + vlc_player_Unlock(player); + + if (unlikely(p_mi->timer.id == NULL)) + return -1; + + return 0; +} + +void +libvlc_media_player_unwatch_time(libvlc_media_player_t *p_mi) +{ + vlc_player_t *player = p_mi->player; + + vlc_player_Lock(player); + + assert(p_mi->timer.id != NULL); + vlc_player_RemoveTimer(player, p_mi->timer.id); + p_mi->timer.id = NULL; + + vlc_player_Unlock(player); +} + +int +libvlc_media_player_time_point_interpolate(const libvlc_media_player_time_point_t *libpoint, + libvlc_time_t system_now, + libvlc_time_t *out_ts, double *out_pos) +{ + const struct vlc_player_timer_point point = PLAYER_TIME_LIB_TO_CORE(libpoint); + + return vlc_player_timer_point_Interpolate(&point, system_now, out_ts, out_pos); +} + +libvlc_time_t +libvlc_media_player_time_point_get_next_date(const libvlc_media_player_time_point_t *libpoint, + libvlc_time_t system_now, + libvlc_time_t interpolated_ts, + libvlc_time_t next_interval) +{ + const struct vlc_player_timer_point point = PLAYER_TIME_LIB_TO_CORE(libpoint); + + return vlc_player_timer_point_GetNextIntervalDate(&point, system_now, + interpolated_ts, next_interval); +} + #include /* make sure surface structures from libvlc can be passed as such to vlc diff --git a/lib/media_player_internal.h b/lib/media_player_internal.h index 4a0010e29d..3453260a7e 100644 --- a/lib/media_player_internal.h +++ b/lib/media_player_internal.h @@ -49,6 +49,13 @@ struct libvlc_media_player_t struct libvlc_instance_t * p_libvlc_instance; /* Parent instance */ libvlc_media_t * p_md; /* current media descriptor */ libvlc_event_manager_t event_manager; + + struct { + vlc_player_timer_id *id; + libvlc_media_player_watch_time_on_update on_update; + libvlc_media_player_watch_time_on_discontinuity on_discontinuity; + void *cbs_data; + } timer; }; libvlc_track_description_t * libvlc_get_track_description(