[vlc-devel] [RFC PATCH 1/2] core: add vlc_player_t (input manager)
Thomas Guillem
thomas at gllm.fr
Mon Sep 17 08:41:39 CEST 2018
On Fri, Sep 14, 2018, at 15:32, Rémi Denis-Courmont wrote:
> Le jeudi 6 septembre 2018, 14:11:31 EEST Thomas Guillem a écrit :
> > ---
> > PLAYER_TODO.txt | 4 +
> > include/vlc_player.h | 759 +++++++++++++++++++++++++
> > src/Makefile.am | 2 +
> > src/input/player.c | 1276 ++++++++++++++++++++++++++++++++++++++++++
> > src/libvlccore.sym | 28 +
> > 5 files changed, 2069 insertions(+)
> > create mode 100644 PLAYER_TODO.txt
> > create mode 100644 include/vlc_player.h
> > create mode 100644 src/input/player.c
> >
> > diff --git a/PLAYER_TODO.txt b/PLAYER_TODO.txt
> > new file mode 100644
> > index 0000000000..832fdc62d8
> > --- /dev/null
> > +++ b/PLAYER_TODO.txt
> > @@ -0,0 +1,4 @@
> > + - Remove input/control.c
> > + - Move bookmark handling from input to player
> > + - Add Seekpoint/title/chapter
> > + - Handle vout/aout
> > diff --git a/include/vlc_player.h b/include/vlc_player.h
> > new file mode 100644
> > index 0000000000..06476cfc65
> > --- /dev/null
> > +++ b/include/vlc_player.h
> > @@ -0,0 +1,759 @@
> > +/**************************************************************************
> > *** + * vlc_player.h: player interface
> > +
> > ***************************************************************************
> > ** + * Copyright (C) 2018 VLC authors and VideoLAN
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU Lesser General Public License as published by
> > + * the Free Software Foundation; either version 2.1 of the License, or + *
> > (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public License
> > + * along with this program; if not, write to the Free Software Foundation,
> > + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +
> > ***************************************************************************
> > **/ +
> > +#ifndef VLC_PLAYER_H
> > +#define VLC_PLAYER_H 1
> > +
> > +#include <vlc_input.h>
> > +
> > +/**
> > + * \defgroup Player
> > + * \ingroup input
> > + * @{
> > + */
> > +
> > +/**
> > + * Player opaque vlc_object structure.
> > + */
> > +typedef struct vlc_player_t vlc_player_t;
> > +
> > +/**
> > + * Player listener opaque structure.
> > + *
> > + * This opaque structure is returned by vlc_player_AddListener() and can be
> > + * used to remove the listener via vlc_player_RemoveListener().
> > + */
> > +struct vlc_player_listener_id;
> > +
> > +/**
> > + * Player program structure.
> > + */
> > +struct vlc_player_program
> > +{
> > + int id;
> > + const char *title;
> > + bool selected;
> > + bool scrambled;
> > +};
> > +
> > +/**
> > + * Player ES track structure.
> > + */
> > +struct vlc_player_track
> > +{
> > + vlc_es_id_t *id;
> > + const char *title;
> > + es_format_t fmt;
> > + bool selected;
> > +};
> > +
> > +struct vlc_player_track *
> > +vlc_player_track_Dup(const struct vlc_player_track *track);
> > +
> > +void
> > +vlc_player_track_Release(struct vlc_player_track *track);
>
> We normally use release for reference counted objects, and this looks like it
> is copy-on-write. In that later case, we usually use destroy or delete.
Yes, I generally do that too.
>
> > +
> > +struct vlc_player_seek_arg
> > +{
> > + enum {
> > + VLC_PLAYER_SEEK_BY_POS,
> > + VLC_PLAYER_SEEK_BY_TIME,
> > + } type;
> > + union {
> > + float position;
> > + vlc_tick_t time;
> > + };
> > + bool fast;
> > + bool absolute;
> > +};
> > +
> > +/** Menu (VCD/DVD/BD) Navigation */
> > +enum vlc_player_nav
> > +{
> > + /** Activate the navigation item selected. */
> > + VLC_PLAYER_NAV_ACTIVATE,
> > + /** Use the up arrow to select a navigation item above. */
> > + VLC_PLAYER_NAV_UP,
> > + /** Use the down arrow to select a navigation item under. */
> > + VLC_PLAYER_NAV_DOWN,
> > + /** Use the left arrow to select a navigation item on the left */
> > + VLC_PLAYER_NAV_LEFT,
> > + /** Use the right arrow to select a navigation item on the right. */
> > + VLC_PLAYER_NAV_RIGHT,
> > + /** Activate the popup Menu (for BD). */
> > + VLC_PLAYER_NAV_POPUP,
> > + /** Activate disc Root Menu. */
> > + VLC_PLAYER_NAV_MENU,
> > +};
> > +
> > +/**
> > + * Callbacks for the owner of the player.
> > + *
> > + * These callbacks are needed to control the player flow (via the
> > + * vlc_playlist_t as a owner for example). It can only be set when creating
> > the + * player via vlc_player_New().
> > + *
> > + * All callbacks are called with the player locked (cf. vlc_player_Lock()).
> > + */
> > +struct vlc_player_owner_cbs
> > +{
> > + /**
> > + * Called when a new media is played.
> > + *
> > + * @param player locked player instance
> > + * @param new_media new media currently played
> > + * @param data opaque pointer set from vlc_player_New()
> > + */
> > + void (*on_current_media_changed)(vlc_player_t *player,
> > + input_item_t *new_media, void *data);
> > + /**
> > + * Called when the player requires a new media.
> > + *
> > + * @param player locked player instance
> > + * @param data opaque pointer set from vlc_player_New()
> > + * @return the next media to play, held by the callee with
> > input_item_Hold()
> > + */
> > + input_item_t *(*get_next_media)(vlc_player_t *player, void *data);
> > +};
> > +
> > +enum vlc_player_list_action
> > +{
> > + VLC_PLAYER_LIST_ADDED,
> > + VLC_PLAYER_LIST_REMOVED,
> > + VLC_PLAYER_LIST_UPDATED,
> > +};
> > +
> > +enum vlc_player_state
> > +{
> > + VLC_PLAYER_STATE_STARTED,
> > + VLC_PLAYER_STATE_STOPPED,
> > + VLC_PLAYER_STATE_PLAYING,
> > + VLC_PLAYER_STATE_PAUSED,
> > + VLC_PLAYER_STATE_ERROR,
>
> Not entirely obvious what ERROR state is...
I still don't know know if we should distinguish:
- Stopped (from the user most likely)
- EOF
- Various error ? (access / demux / decoder)
>
> > +};
> > +
> > +/**
> > + * Callbacks to get the state of the input.
> > + *
> > + * Can be registered with vlc_player_AddListener().
> > + *
> > + * All callbacks are called with the player locked (cf. vlc_player_Lock()).
> > + */
> > +struct vlc_player_cbs
> > +{
> > + void (*on_state_changed)(vlc_player_t *player, enum vlc_player_state
> > state, + void *data);
> > +
> > + void (*on_buffering)(vlc_player_t *player, float new_buffering, void
> > *data); +
> > + void (*on_rate_changed)(vlc_player_t *player, float new_rate, void
> > *data); +
> > + void (*on_capabilities_changed)(vlc_player_t *player, int new_caps,
> > + void *data);
> > +
> > + void (*on_length_changed)(vlc_player_t *player, vlc_tick_t new_length,
> > + void *data);
> > +
> > + void (*on_track_list_changed)(vlc_player_t *player,
> > + enum vlc_player_list_action action,
> > + const struct vlc_player_track *track,
> > + void *data);
> > +
> > + void (*on_track_selection_changed)(vlc_player_t *player,
> > + vlc_es_id_t *unselected_id,
> > + vlc_es_id_t *selected_id, void
> > *data); +
> > + void (*on_track_delay_changed)(vlc_player_t *player,
> > + vlc_es_id_t *id, vlc_tick_t new_delay,
> > + void *data);
> > +
> > + void (*on_program_list_changed)(vlc_player_t *player,
> > + enum vlc_player_list_action action,
> > + struct vlc_player_program *prgm,
> > + void *data);
> > +
> > + void (*on_program_selection_changed)(vlc_player_t *player,
> > + int unselected_id, int
> > selected_id, + void *data);
> > +
> > + void (*on_chapter_list_changed)(vlc_player_t *player, void *data);
> > +
> > + void (*on_chapter_selection_changed)(vlc_player_t *player, /* XXX*/
> > void *data); +
> > + void (*on_title_list_changed)(vlc_player_t *player, void *data);
> > +
> > + void (*on_title_selection_changed)(vlc_player_t *player, /* XXX*/ void
> > *data); +
> > + void (*on_record_changed)(vlc_player_t *player, bool recording, void
> > *data); +
> > + void (*on_signal_changed)(vlc_player_t *player,
> > + float quality, float strength, void *data);
> > +
> > + void (*on_stats_changed)(vlc_player_t *player,
> > + const struct input_stats_t *stats, void
> > *data); +};
> > +
> > +/**
> > + * Create a new player instance.
> > + *
> > + * @param parent parent VLC object
> > + * @parent owner_cbs callbacks for the owner
> > + * @parent owner_cbs_data opaque data for owner callbacks
> > + * @return a pointer to a valid player instance or NULL in case of error
> > + */
> > +VLC_API vlc_player_t *
> > +vlc_player_New(vlc_object_t *parent,
> > + const struct vlc_player_owner_cbs *owner_cbs,
> > + void *owner_cbs_data);
> > +
> > +/**
> > + * Delete the player.
> > + *
> > + * @param player unlocked player instance created by vlc_player_New()
> > + */
> > +VLC_API void
> > +vlc_player_Delete(vlc_player_t *player);
> > +
> > +/**
> > + * Lock the player.
> > + *
> > + * All player functions (except vlc_player_Delete()) need to be called
> > while + * the player lock is held.
> > + *
> > + * @param player unlocked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Lock(vlc_player_t *player);
> > +
> > +/**
> > + * Unlock the player
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Unlock(vlc_player_t *player);
> > +
> > +/**
> > + * Add a listener callback
> > + *
> > + * Every registered callbacks need to be removed by the caller with
> > + * vlc_player_RemoveListener().
> > + *
> > + * @param player locked player instance
> > + * @param cbs pointer to a static vlc_player_cbs structure
> > + * @param cbs_data opaque pointer used by the callbacks
> > + * @return a valid listener id, or NULL in case of error
> > + */
> > +VLC_API struct vlc_player_listener_id *
> > +vlc_player_AddListener(vlc_player_t *player,
> > + const struct vlc_player_cbs *cbs, void *cbs_data);
> > +
> > +/**
> > + * Remove a listener callback
> > + *
> > + * @param player locked player instance
> > + * @param id listener id returned by vlc_player_AddListener()
> > + */
> > +VLC_API void
> > +vlc_player_RemoveListener(vlc_player_t *player,
> > + struct vlc_player_listener_id *id);
> > +
> > +/**
> > + * Set the current media for playback.
> > + *
> > + * This function replaces the current and next medias (and stop the
> > playback of
> > + * these medias if needed). The playback need to be started
> > with
> > + * vlc_player_Start().
> > + *
> > + * @param player locked player instance
> > + * @param media new media to player
> > + * @return VLC_SUCCESS or a VLC error code
> > + */
> > +VLC_API int
> > +vlc_player_SetCurrentMedia(vlc_player_t *player, input_item_t *media);
> > +
> > +/**
> > + * Get the current played media.
> > + *
> > + * @param player locked player instance
> > + * @return a valid media or NULL (if not media are set)
> > + */
> > +VLC_API input_item_t *
> > +vlc_player_GetCurrentMedia(vlc_player_t *player);
> > +
> > +/**
> > + * Invalidate the next media.
> > + *
> > + * This function can be used to invalidate the media returned by the
> > + * vlc_player_owner_cbs.get_next_media callback. This can be used when the
> > next
> > + * item from a playlist was changed by the user.
> > + *
> > + * Calling this function will trigger the
> > vlc_player_owner_cbs.get_next_media + * callback from the playback thread.
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_InvalidateNextMedia(vlc_player_t *player);
> > +
> > +/**
> > + * Start the playback of the current media.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @return VLC_SUCCESS or a VLC error code
> > + */
> > +VLC_API int
> > +vlc_player_Start(vlc_player_t *player);
> > +
> > +/**
> > + * Stop the playback of the current media.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @return VLC_SUCCESS or a VLC error code
> > + */
> > +VLC_API void
> > +vlc_player_Stop(vlc_player_t *player);
> > +
> > +/**
> > + * Pause the playback.
> > + *
> > + * @warning The behaviour is undefined if the player is not started.
> > + * @warning The behaviour is undefined if vlc_player_CanPause() is false.
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Pause(vlc_player_t *player);
> > +
> > +/**
> > + * Resume the playback from a pause.
> > + *
> > + * @warning The behaviour is undefined if the player is not started.
> > + * @warning The behaviour is undefined if vlc_player_CanPause() is false.
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Resume(vlc_player_t *player);
>
> OK with me, but I am not sure everybody else will like to distinguish resume
> and start, instead of just play.
>
> > +
> > +/**
> > + * Get the started state.
> > + *
> > + * @param player locked player instance
> > + * @return true if the player is started (vlc_player_Start() succeeded and
> > + * vlc_player_cbs.on_playback_event didn't send a stopped/dead event).
> > + */
> > +VLC_API bool
> > +vlc_player_IsStarted(vlc_player_t *player);
>
> const? Ditto following functions.
Yes.
>
> > +
> > +/**
> > + * Get the paused state.
> > + *
> > + * Since the vlc_player_Pause() / vlc_player_Resume() are asynchronous,
> > this + * function won't reflect the paused state immediately. Wait for the
> > + * INPUT_EVENT_STATE event to be notified.
> > + *
> > + * @param player locked player instance
> > + * @return true if the player is paused
> > + */
> > +VLC_API bool
> > +vlc_player_IsPaused(vlc_player_t *player);
> > +
> > +/**
> > + * Get the player capabilities
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @return the player capabilities, a bitwise mask of
> > + * VLC_INPUT_CAPABILITIES_SEEKABLE, VLC_INPUT_CAPABILITIES_PAUSEABLE,
> > + * VLC_INPUT_CAPABILITIES_CHANGE_RATE, VLC_INPUT_CAPABILITIES_REWINDABLE,
> > + * VLC_INPUT_CAPABILITIES_RECORDABLE,
> > + */
> > +VLC_API int
> > +vlc_player_GetCapabilities(vlc_player_t *player);
> > +
> > +/**
> > + * Get the seek capability (Helper).
> > + */
> > +static inline bool
> > +vlc_player_CanSeek(vlc_player_t *player)
> > +{
> > + return vlc_player_GetCapabilities(player) &
> > VLC_INPUT_CAPABILITIES_SEEKABLE; +}
> > +
> > +/**
> > + * Get the pause capability (Helper).
> > + */
> > +static inline bool
> > +vlc_player_CanPause(vlc_player_t *player)
> > +{
> > + return vlc_player_GetCapabilities(player) &
> > VLC_INPUT_CAPABILITIES_PAUSEABLE; +}
> > +
> > +/**
> > + * Get the change-rate capability (Helper).
> > + */
> > +static inline bool
> > +vlc_player_CanChangeRate(vlc_player_t *player)
> > +{
> > + return vlc_player_GetCapabilities(player) &
> > VLC_INPUT_CAPABILITIES_CHANGE_RATE; +}
> > +
> > +/**
> > + * Get the rewindable capability (Helper).
> > + */
> > +static inline bool
> > +vlc_player_IsRewindable(vlc_player_t *player)
> > +{
> > + return vlc_player_GetCapabilities(player) &
> > VLC_INPUT_CAPABILITIES_REWINDABLE; +}
> > +
> > +/**
> > + * Get the recordable capability (Helper).
> > + */
> > +static inline bool
> > +vlc_player_IsRecordable(vlc_player_t *player)
> > +{
> > + return vlc_player_GetCapabilities(player) &
> > VLC_INPUT_CAPABILITIES_RECORDABLE; +}
> > +
> > +VLC_API vlc_tick_t
> > +vlc_player_GetLength(vlc_player_t *player);
> > +
> > +VLC_API vlc_tick_t
> > +vlc_player_GetTime(vlc_player_t *player);
> > +
> > +VLC_API float
> > +vlc_player_GetPosition(vlc_player_t *player);
> > +
> > +VLC_API void
> > +vlc_player_Seek(vlc_player_t *player, const struct vlc_player_seek_arg
> > *arg);
>
> It's rather weird to pack everything in a struct when you could have multiple
> parameters.
OK
>
> > +
> > +static inline void
> > +vlc_player_SetPosition(vlc_player_t *player, float position)
> > +{
> > + vlc_player_Seek(player, &(struct vlc_player_seek_arg) {
> > + VLC_PLAYER_SEEK_BY_POS, { position }, false, true,
> > + });
> > +}
> > +
> > +static inline void
> > +vlc_player_SetPositionFast(vlc_player_t *player, float position)
> > +{
> > + vlc_player_Seek(player, &(struct vlc_player_seek_arg) {
> > + VLC_PLAYER_SEEK_BY_POS, { position }, true, true,
> > + });
> > +}
> > +
> > +static inline void
> > +vlc_player_JumpPos(vlc_player_t *player, vlc_tick_t jumppos)
> > +{
> > + /* No fask seek for jumps. Indeed, jumps can seek to the current
> > position + * if not precise enough or if the jump value is too small.
> > */ + vlc_player_Seek(player, &(struct vlc_player_seek_arg) {
> > + VLC_PLAYER_SEEK_BY_POS, { jumppos }, false, false,
> > + });
> > +}
> > +
> > +static inline void
> > +vlc_player_SetTime(vlc_player_t *player, vlc_tick_t time)
> > +{
> > + vlc_player_Seek(player, &(struct vlc_player_seek_arg) {
> > + VLC_PLAYER_SEEK_BY_TIME, { time }, false, true,
> > + });
> > +}
> > +
> > +static inline void
> > +vlc_player_SetTimeFast(vlc_player_t *player, vlc_tick_t time)
> > +{
> > + vlc_player_Seek(player, &(struct vlc_player_seek_arg) {
> > + VLC_PLAYER_SEEK_BY_TIME, { time }, true, true,
> > + });
> > +}
> > +
> > +static inline void
> > +vlc_player_JumpTime(vlc_player_t *player, vlc_tick_t jumptime)
> > +{
> > + /* No fask seek for jumps. Indeed, jumps can seek to the current
> > position + * if not precise enough or if the jump value is too small.
> > */ + vlc_player_Seek(player, &(struct vlc_player_seek_arg) {
> > + VLC_PLAYER_SEEK_BY_TIME, { jumptime }, false, false,
> > + });
> > +}
> > +
> > +/**
> > + * Get the number of tracks for an ES category.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
> > + * @return number of tracks (or 0)
> > + */
> > +VLC_API size_t
> > +vlc_player_GetTrackCount(vlc_player_t *player, enum es_format_category_e
> > cat); +
> > +/**
> > + * Get the track for an ES caterogy at a specific index.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + * @warning The behaviour is undefined if the index is not valid.
> > + *
> > + * @param player locked player instance
> > + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
> > + * @param index valid index in the range [0; count[
> > + * @return a valid player track (can't be NULL)
> > + */
> > +VLC_API const struct vlc_player_track *
> > +vlc_player_GetTrackAt(vlc_player_t *player, enum es_format_category_e cat,
> > + size_t index);
> > +
> > +/**
> > + * Get the video track count (Helper).
> > + */
> > +static inline size_t
> > +vlc_player_GetVideoTrackCount(vlc_player_t *player)
> > +{
> > + return vlc_player_GetTrackCount(player, VIDEO_ES);
> > +}
> > +
> > +/**
> > + * Get the video track at a specific index (Helper).
> > + */
> > +static inline const struct vlc_player_track *
> > +vlc_player_GetVideoTrackAt(vlc_player_t *player, size_t index)
> > +{
> > + return vlc_player_GetTrackAt(player, VIDEO_ES, index);
> > +}
> > +
> > +/**
> > + * Get the audio track count (Helper).
> > + */
> > +static inline size_t
> > +vlc_player_GetAudioTrackCount(vlc_player_t *player)
> > +{
> > + return vlc_player_GetTrackCount(player, AUDIO_ES);
> > +}
> > +
> > +/**
> > + * Get the audio track at a specific index (Helper).
> > + */
> > +static inline const struct vlc_player_track *
> > +vlc_player_GetAudioTrackAt(vlc_player_t *player, size_t index)
> > +{
> > + return vlc_player_GetTrackAt(player, AUDIO_ES, index);
> > +}
> > +
> > +/**
> > + * Get the spu track count (Helper).
> > + */
> > +static inline size_t
> > +vlc_player_GetSpuTrackCount(vlc_player_t *player)
> > +{
> > + return vlc_player_GetTrackCount(player, SPU_ES);
> > +}
> > +
> > +/**
> > + * Get the spu track at a specific index (Helper).
> > + */
> > +static inline const struct vlc_player_track *
> > +vlc_player_GetSpuTrackAt(vlc_player_t *player, size_t index)
> > +{
> > + return vlc_player_GetTrackAt(player, SPU_ES, index);
> > +}
> > +
> > +/**
> > + * Get a track from an ES identifier.
> > + *
> > + * The only way to save a player track when the player is not locked
> > anymore + * (from the event thread to the UI main thread for example) is to
> > hold the ES + * ID with vlc_es_id_Hold()). Then, the user can call this
> > function in order to + * retrieve the up to date track information from the
> > previously held ES ID. + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from the INPUT_EVENT_ES event or from
> > + * vlc_player_GetTrackAt())
> > + * @return a valid player track or NULL (if the track was terminated by the
> > + * playback thread)
> > + */
> > +VLC_API const struct vlc_player_track *
> > +vlc_player_GetTrack(vlc_player_t *player, vlc_es_id_t *id);
> > +
> > +/**
> > + * Select a track from an ES identifier.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from the INPUT_EVENT_ES event or from
> > + * vlc_player_GetTrackAt())
> > + */
> > +VLC_API void
> > +vlc_player_SelectTrack(vlc_player_t *player, vlc_es_id_t *id);
> > +
> > +/**
> > + * Unselect a track from an ES identifier.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from the INPUT_EVENT_ES event or from
> > + * vlc_player_GetTrackAt())
> > + */
> > +VLC_API void
> > +vlc_player_UnselectTrack(vlc_player_t *player, vlc_es_id_t *id);
> > +
> > +/**
> > + * Restart a track from an ES identifier.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from the INPUT_EVENT_ES event or from
> > + * vlc_player_GetTrackAt())
> > + */
> > +VLC_API void
> > +vlc_player_RestartTrack(vlc_player_t *player, vlc_es_id_t *id);
> > +
> > +/**
> > + * Select the default track for an ES category.
> > + *
> > + * Tracks for this category will be automatically chosen according to the
> > + * language for all future played media.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
> > + * @param lang language (TODO: define it) or NULL to reset the default
> > state + */
> > +VLC_API void
> > +vlc_player_SelectDefaultTrack(vlc_player_t *player,
> > + enum es_format_category_e cat, const char
> > *lang); +
> > +/**
> > + * Select the default video track (Helper).
> > + */
> > +static inline void
> > +vlc_player_SelectDefaultVideoTrack(vlc_player_t *player, const char *lang)
> > +{
> > + vlc_player_SelectDefaultTrack(player, VIDEO_ES, lang);
> > +}
> > +
> > +/**
> > + * Select the default audio track (Helper).
> > + */
> > +static inline void
> > +vlc_player_SelectDefaultAudioTrack(vlc_player_t *player, const char *lang)
> > +{
> > + vlc_player_SelectDefaultTrack(player, AUDIO_ES, lang);
> > +}
> > +
> > +/**
> > + * Select the default spu track (Helper).
> > + */
> > +static inline void
> > +vlc_player_SelectDefaultSpuTrack(vlc_player_t *player, const char *lang)
> > +{
> > + vlc_player_SelectDefaultTrack(player, SPU_ES, lang);
> > +}
> > +
> > +/**
> > + * Get the number of programs
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @return number of programs (or 0)
> > + */
> > +VLC_API size_t
> > +vlc_player_GetProgramCount(vlc_player_t *player);
> > +
> > +/**
> > + * Get the program at a specific index.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + * @warning The behaviour is undefined if the index is not valid.
> > + *
> > + * @param player locked player instance
> > + * @param index valid index in the range [0; count[
> > + * @return a valid player program (can't be NULL)
> > + */
> > +VLC_API const struct vlc_player_program *
> > +vlc_player_GetProgramAt(vlc_player_t *player, size_t index);
> > +
> > +/**
> > + * Get a program from an ES identifier.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES program ID (retrieved from the INPUT_EVENT_PROGRAM event
> > or + * from vlc_player_GetProgramAt())
> > + * @return a valid program or NULL (if the program was terminated by the
> > + * playback thread)
> > + */
> > +VLC_API const struct vlc_player_program *
> > +vlc_player_GetProgram(vlc_player_t *player, int id);
> > +
> > +/**
> > + * Select a program from an ES program identifier.
> > + *
> > + * @warning The behaviour is undefined if there is no current media.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES program ID (retrieved from the INPUT_EVENT_PROGRAM event
> > or + * from vlc_player_GetProgramAt())
> > + */
> > +VLC_API void
> > +vlc_player_SelectProgram(vlc_player_t *player, int id);
> > +
> > +/**
> > + * Set the renderer
> > + *
> > + * Valid for the current media and all future ones.
> > + *
> > + * @param player locked player instance
> > + * @param renderer a valid renderer item or NULL (to disable it)
> > + */
> > +VLC_API void
> > +vlc_player_SetRenderer(vlc_player_t *player, vlc_renderer_item_t
> > *renderer); +
> > +VLC_API void
> > +vlc_player_Navigate(vlc_player_t *player, enum vlc_player_nav nav);
> > +
> > +VLC_API bool
> > +vlc_player_IsRecording(vlc_player_t *player);
> > +
> > +VLC_API int
> > +vlc_player_GetSignal(vlc_player_t *player, float *quality, float
> > *strength); +
> > +VLC_API void
> > +vlc_player_GetStats(vlc_player_t *player, struct input_stats_t *stats);
> > +
> > +/** @} */
> > +#endif
> > diff --git a/src/Makefile.am b/src/Makefile.am
> > index d1fceade27..1f5af53b5e 100644
> > --- a/src/Makefile.am
> > +++ b/src/Makefile.am
> > @@ -76,6 +76,7 @@ pluginsinclude_HEADERS = \
> > ../include/vlc_picture.h \
> > ../include/vlc_picture_fifo.h \
> > ../include/vlc_picture_pool.h \
> > + ../include/vlc_player.h \
> > ../include/vlc_playlist.h \
> > ../include/vlc_plugin.h \
> > ../include/vlc_probe.h \
> > @@ -242,6 +243,7 @@ libvlccore_la_SOURCES = \
> > input/es_out.c \
> > input/es_out_timeshift.c \
> > input/input.c \
> > + input/player.c \
> > input/info.h \
> > input/meta.c \
> > clock/input_clock.h \
> > diff --git a/src/input/player.c b/src/input/player.c
> > new file mode 100644
> > index 0000000000..7fe80a08e7
> > --- /dev/null
> > +++ b/src/input/player.c
> > @@ -0,0 +1,1276 @@
> > +/**************************************************************************
> > *** + * player.c: Player interface
> > +
> > ***************************************************************************
> > ** + * Copyright © 2018 VLC authors and VideoLAN
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU Lesser General Public License as published by
> > + * the Free Software Foundation; either version 2.1 of the License, or + *
> > (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public License
> > + * along with this program; if not, write to the Free Software Foundation,
> > + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +
> > ***************************************************************************
> > **/ +
> > +#ifdef HAVE_CONFIG_H
> > +# include "config.h"
> > +#endif
> > +
> > +#include <vlc_common.h>
> > +#include <vlc_player.h>
> > +#include <vlc_player.h>
> > +#include <vlc_aout.h>
> > +#include <vlc_renderer_discovery.h>
> > +#include <vlc_list.h>
> > +#include <vlc_vector.h>
> > +
> > +#include "libvlc.h"
> > +#include "input_internal.h"
> > +
> > +#define GAPLESS 0 /* TODO */
> > +
> > +struct vlc_player_listener_id
> > +{
> > + const struct vlc_player_cbs * cbs;
> > + void * cbs_data;
> > + struct vlc_list node;
> > +};
> > +
> > +struct vlc_player_program_priv
> > +{
> > + struct vlc_player_program p;
> > + char *title;
> > +};
> > +typedef struct VLC_VECTOR(struct vlc_player_program_priv *)
> > + vlc_player_program_vector;
> > +
> > +typedef struct VLC_VECTOR(struct vlc_player_track *)
> > + vlc_player_track_vector;
> > +
> > +struct vlc_player_input
> > +{
> > + input_thread_t *thread;
> > + vlc_player_t *player;
> > + bool started;
> > +
> > + input_state_e state;
> > + float rate;
> > + int capabilities;
> > + vlc_tick_t length;
> > +
> > + vlc_tick_t position_ms;
> > + float position_percent;
> > + vlc_tick_t position_date;
> > +
> > + bool recording;
> > +
> > + float signal_quality;
> > + float signal_strength;
> > + float cache;
> > +
> > + struct input_stats_t stats;
> > +
> > + vlc_player_program_vector program_vector;
> > + vlc_player_track_vector video_track_vector;
> > + vlc_player_track_vector audio_track_vector;
> > + vlc_player_track_vector spu_track_vector;
> > +
> > + struct vlc_list node;
> > +};
> > +
> > +struct vlc_player_t
> > +{
> > + struct vlc_common_members obj;
> > + vlc_mutex_t lock;
> > +
> > + const struct vlc_player_owner_cbs *owner_cbs;
> > + void *owner_cbs_data;
> > +
> > + struct vlc_list listeners;
> > +
> > + input_resource_t *resource;
> > + vlc_renderer_item_t *renderer;
> > +
> > + input_item_t *media;
> > + struct vlc_player_input *input;
> > +
> > +#if GAPLESS
> > + input_item_t *next_media;
> > + struct vlc_player_input *next_input;
> > +#endif
> > +
> > + struct
> > + {
> > + bool running;
> > + vlc_thread_t thread;
> > + vlc_cond_t cond;
> > + struct vlc_list inputs;
> > + size_t wait_count;
> > + } destructor;
> > +};
> > +
> > +#define vlc_player_SendEvent(player, event, ...) do { \
> > + struct vlc_player_listener_id *listener; \
> > + vlc_list_foreach(listener, &player->listeners, node) \
> > + { \
> > + if (listener->cbs->event) \
> > + listener->cbs->event(player, ##__VA_ARGS__,
> > listener->cbs_data); \ + } \
> > +} while(0)
> > +
> > +#if GAPLESS
> > +#define vlc_player_foreach_inputs(it) \
> > + for (struct vlc_player_input *it = player->input; \
> > + it != NULL; \
> > + it = (it == player->input ? player->next_input : NULL))
> > +#else
> > +#define vlc_player_foreach_inputs(it) \
> > + for (struct vlc_player_input *it = player->input; it != NULL; it =
> > NULL) +#endif
> > +
> > +static void
> > +input_thread_events(input_thread_t *, const struct vlc_input_event *, void
> > *); +
> > +static inline void
> > +vlc_player_assert_locked(vlc_player_t *player)
> > +{
> > + assert(player);
> > + vlc_assert_locked(&player->lock);
> > +}
> > +
> > +static struct vlc_player_input *
> > +vlc_player_input_New(vlc_player_t *player, input_item_t *item)
> > +{
> > + struct vlc_player_input *input = malloc(sizeof(*input));
> > + if (!input)
> > + return NULL;
> > +
> > + input->player = player;
> > + input->started = false;
> > +
> > + input->state = INIT_S;
> > + input->rate = 1.f;
> > + input->capabilities = 0;
> > + input->length = input->position_ms =
> > + input->position_date = VLC_TICK_INVALID;
> > + input->position_percent = 0.f;
> > +
> > + input->recording = false;
> > +
> > + input->cache = 0.f;
> > + input->signal_quality = input->signal_strength = -1.f;
> > +
> > + memset(&input->stats, 0, sizeof(input->stats));
> > +
> > + vlc_vector_init(&input->program_vector);
> > + vlc_vector_init(&input->video_track_vector);
> > + vlc_vector_init(&input->audio_track_vector);
> > + vlc_vector_init(&input->spu_track_vector);
> > +
> > + input->thread = input_Create(player, input_thread_events, input, item,
> > + NULL, player->resource, player->renderer);
> > + if (!input->thread)
> > + {
> > + free(input);
> > + return NULL;
> > + }
> > + return input;
> > +}
> > +
> > +static void
> > +vlc_player_input_Delete(struct vlc_player_input *input)
> > +{
> > + assert(input->program_vector.size == 0);
> > + assert(input->video_track_vector.size == 0);
> > + assert(input->audio_track_vector.size == 0);
> > + assert(input->spu_track_vector.size == 0);
> > +
> > + vlc_vector_clear(&input->program_vector);
> > + vlc_vector_clear(&input->video_track_vector);
> > + vlc_vector_clear(&input->audio_track_vector);
> > + vlc_vector_clear(&input->spu_track_vector);
> > +
> > + input_Close(input->thread);
> > + free(input);
> > +}
> > +
> > +static int
> > +vlc_player_input_Start(struct vlc_player_input *input)
> > +{
> > + int ret = input_Start(input->thread);
> > + if (ret != VLC_SUCCESS)
> > + return ret;
> > + input->started = true;
> > + return ret;
> > +}
> > +
> > +static void
> > +vlc_player_input_StopAndClose(struct vlc_player_input *input)
> > +{
> > + if (input->started)
> > + {
> > + input->started = false;
> > + input_Stop(input->thread);
> > +
> > + input->player->destructor.wait_count++;
> > + /* This input will be cleaned when we receive the INPUT_EVENT_DEAD
> > + * event */
> > + }
> > + else
> > + vlc_player_input_Delete(input);
> > +}
> > +
> > +static void *
> > +destructor_thread(void *data)
> > +{
> > + vlc_player_t *player = data;
> > + struct vlc_player_input *input;
> > +
> > + vlc_mutex_lock(&player->lock);
> > + while (player->destructor.running || player->destructor.wait_count > 0)
> > + {
> > + while (player->destructor.running
> > + && vlc_list_is_empty(&player->destructor.inputs))
> > + vlc_cond_wait(&player->destructor.cond, &player->lock);
> > +
> > + vlc_list_foreach(input, &player->destructor.inputs, node)
> > + {
> > + vlc_player_input_Delete(input);
> > + vlc_list_remove(&input->node);
> > + player->destructor.wait_count--;
> > + }
> > + }
> > + vlc_mutex_unlock(&player->lock);
> > + return NULL;
> > +}
> > +
> > +static void
> > +vlc_player_SendNewPlaybackEvent(vlc_player_t *player, input_item_t
> > *new_item) +{
> > + vlc_player_assert_locked(player);
> > +
> > + if (player->owner_cbs->on_current_media_changed)
> > + player->owner_cbs->on_current_media_changed(player, new_item,
> > +
> > player->owner_cbs_data); +}
> > +
> > +static input_item_t *
> > +vlc_player_GetNextItem(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > +
> > + return player->owner_cbs->get_next_media ?
> > + player->owner_cbs->get_next_media(player,
> > player->owner_cbs_data) : + NULL;
> > +}
> > +
> > +static int
> > +vlc_player_OpenNextItem(vlc_player_t *player)
> > +{
> > + assert(player->input == NULL);
> > +
> > + input_item_t *next_media = vlc_player_GetNextItem(player);
> > + if (!next_media)
> > + return VLC_EGENERIC;
> > +
> > + if (player->media)
> > + input_item_Release(player->media);
> > + player->media = next_media;
> > +
> > + player->input = vlc_player_input_New(player, player->media);
> > +
> > + return player->input ? VLC_SUCCESS : VLC_EGENERIC;
> > +}
> > +
> > +static struct vlc_player_program_priv *
> > +vlc_player_program_vector_FindById(vlc_player_program_vector *vec, int id,
> > + size_t *idx)
> > +{
> > + for (size_t i = 0; i < vec->size; ++i)
> > + {
> > + struct vlc_player_program_priv *prgm = vec->data[i];
> > + if (prgm->p.id == id)
> > + {
> > + if (idx)
> > + *idx = i;
> > + return prgm;
> > + }
> > + }
> > + return NULL;
> > +}
> > +
> > +size_t
> > +vlc_player_GetProgramCount(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > +
> > + return player->input->program_vector.size;
> > +}
> > +
> > +const struct vlc_player_program *
> > +vlc_player_GetProgramAt(vlc_player_t *player, size_t index)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > +
> > + assert(index < player->input->program_vector.size);
> > + return &player->input->program_vector.data[index]->p;
> > +}
> > +
> > +const struct vlc_player_program *
> > +vlc_player_GetProgram(vlc_player_t *player, int id)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > +
> > + struct vlc_player_program_priv *prgm =
> > + vlc_player_program_vector_FindById(&player->input->program_vector,
> > id, + NULL);
> > + return prgm ? &prgm->p : NULL;
> > +}
> > +
> > +void
> > +vlc_player_SelectProgram(vlc_player_t *player, int id)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input != NULL);
> > +
> > + input_ControlPushHelper(player->input->thread,
> > INPUT_CONTROL_SET_PROGRAM, + &(vlc_value_t) {
> > .i_int = id });
> > +}
> > +
> > +static struct vlc_player_program_priv *
> > +vlc_player_program_Create(int id, const char *title)
> > +{
> > + struct vlc_player_program_priv *prgm = malloc(sizeof(*prgm));
> > + if (!prgm)
> > + return NULL;
> > + prgm->title = strdup(title);
> > + if (!prgm->title)
> > + {
> > + free(prgm);
> > + return NULL;
> > + }
> > + prgm->p.id = id;
> > + prgm->p.title = prgm->title;
> > + prgm->p.selected = prgm->p.scrambled = false;
> > +
> > + return prgm;
> > +}
> > +
> > +static void
> > +vlc_player_program_Destroy(struct vlc_player_program_priv *prgm)
> > +{
> > + free(prgm->title);
> > + free(prgm);
> > +}
> > +
> > +static int
> > +vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
> > + const struct vlc_input_event_program
> > *ev) +{
> > + struct vlc_player_program_priv *prgm;
> > + vlc_player_program_vector *vec = &input->program_vector;
> > +
> > + switch (ev->action)
> > + {
> > + case VLC_INPUT_PROGRAM_ADDED:
> > + assert(ev->title);
> > + prgm = vlc_player_program_Create(ev->id, ev->title);
> > + if (!prgm)
> > + return VLC_ENOMEM;
> > +
> > + if (vlc_vector_push(vec, prgm))
> > + return VLC_SUCCESS;
> > + vlc_player_program_Destroy(prgm);
> > + return VLC_ENOMEM;
> > + case VLC_INPUT_PROGRAM_DELETED:
> > + {
> > + size_t idx;
> > + prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx);
> > + if (prgm)
> > + {
> > + vlc_vector_remove(vec, idx);
> > + vlc_player_program_Destroy(prgm);
> > + return VLC_SUCCESS;
> > + }
> > + return VLC_EGENERIC;
> > + }
> > + case VLC_INPUT_PROGRAM_SELECTED:
> > + {
> > + int ret = VLC_EGENERIC;
> > + vlc_vector_foreach(prgm, vec)
> > + {
> > + if (prgm->p.id == ev->id)
> > + {
> > + prgm->p.selected = true;
> > + ret = VLC_SUCCESS;
> > + }
> > + else
> > + prgm->p.selected = false;
> > + }
> > + return ret;
> > + }
> > + case VLC_INPUT_PROGRAM_SCRAMBLED:
> > + prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL);
> > + if (prgm)
> > + {
> > + prgm->p.scrambled = ev->scrambled;
> > + return VLC_SUCCESS;
> > + }
> > + return VLC_EGENERIC;
> > + default:
> > + vlc_assert_unreachable();
> > + }
> > +}
> > +
> > +static inline vlc_player_track_vector *
> > +vlc_player_input_GetTrackVector(struct vlc_player_input *input,
> > + enum es_format_category_e cat)
> > +{
> > + switch (cat)
> > + {
> > + case VIDEO_ES:
> > + return &input->video_track_vector;
> > + case AUDIO_ES:
> > + return &input->audio_track_vector;
> > + case SPU_ES:
> > + return &input->spu_track_vector;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +static struct vlc_player_track *
> > +vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t
> > *id, + size_t *idx)
> > +{
> > + for (size_t i = 0; i < vec->size; ++i)
> > + {
> > + struct vlc_player_track *track = vec->data[i];
> > + if (track->id == id)
> > + {
> > + if (idx)
> > + *idx = i;
> > + return track;
> > + }
> > + }
> > + return NULL;
> > +}
> > +
> > +size_t
> > +vlc_player_GetTrackCount(vlc_player_t *player, enum es_format_category_e
> > cat) +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > +
> > + vlc_player_track_vector *vec =
> > + vlc_player_input_GetTrackVector(player->input, cat);
> > + if (vec)
> > + return 0;
> > + return vec->size;
> > +}
> > +
> > +const struct vlc_player_track *
> > +vlc_player_GetTrackAt(vlc_player_t *player, enum es_format_category_e cat,
> > + size_t index)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > +
> > + vlc_player_track_vector *vec =
> > + vlc_player_input_GetTrackVector(player->input, cat);
> > + if (!vec)
> > + return NULL;
> > + assert(index < vec->size);
> > + return vec->data[index];
> > +}
> > +
> > +const struct vlc_player_track *
> > +vlc_player_GetTrack(vlc_player_t *player, vlc_es_id_t *id)
> > +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > +
> > + vlc_player_track_vector *vec =
> > + vlc_player_input_GetTrackVector(input, vlc_es_id_GetCat(id));
> > + if (!vec)
> > + return NULL;
> > + return vlc_player_track_vector_FindById(vec, id, NULL);
> > +}
> > +
> > +void
> > +vlc_player_SelectTrack(vlc_player_t *player, vlc_es_id_t *id)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input != NULL);
> > +
> > + input_ControlPushEsHelper(player->input->thread, INPUT_CONTROL_SET_ES,
> > id); +}
> > +
> > +void
> > +vlc_player_UnselectTrack(vlc_player_t *player, vlc_es_id_t *id)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input != NULL);
> > +
> > + input_ControlPushEsHelper(player->input->thread,
> > INPUT_CONTROL_UNSET_ES, + id);
> > +}
> > +
> > +void
> > +vlc_player_RestartTrack(vlc_player_t *player, vlc_es_id_t *id)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input != NULL);
> > +
> > + input_ControlPushEsHelper(player->input->thread,
> > INPUT_CONTROL_RESTART_ES, + id);
> > +}
> > +
> > +void
> > +vlc_player_SelectDefaultTrack(vlc_player_t *player,
> > + enum es_format_category_e cat, const char
> > *lang) +{
> > + vlc_player_assert_locked(player);
> > + /* TODO */ (void) cat; (void) lang;
> > +}
> > +
> > +static struct vlc_player_track *
> > +vlc_player_track_New(vlc_es_id_t *id, const char *title, const es_format_t
> > *fmt) +{
> > + struct vlc_player_track *track = malloc(sizeof(*track));
> > + if (!track)
> > + return NULL;
> > + track->title = strdup(title);
> > + if (!track->title)
> > + {
> > + free(track);
> > + return NULL;
> > + }
> > +
> > + int ret = es_format_Copy(&track->fmt, fmt);
> > + if (ret != VLC_SUCCESS)
> > + {
> > + free((char *)track->title);
> > + free(track);
> > + return NULL;
> > + }
> > + track->id = vlc_es_id_Hold(id);
> > + track->selected = false;
> > +
> > + return track;
> > +}
> > +
> > +struct vlc_player_track *
> > +vlc_player_track_Dup(const struct vlc_player_track *src)
> > +{
> > + struct vlc_player_track *dup =
> > + vlc_player_track_New(src->id, src->title, &src->fmt);
> > +
> > + if (!dup)
> > + return NULL;
> > + dup->selected = src->selected;
> > + return dup;
> > +}
> > +
> > +void
> > +vlc_player_track_Release(struct vlc_player_track *track)
> > +{
> > + es_format_Clean(&track->fmt);
> > + free((char *)track->title);
> > + vlc_es_id_Release(track->id);
> > + free(track);
> > +}
> > +
> > +static int
> > +vlc_player_track_Update(struct vlc_player_track *track,
> > + const char *title, const es_format_t *fmt)
> > +{
> > + if (strcmp(title, track->title) != 0)
> > + {
> > + char *dup = strdup(title);
> > + if (!dup)
> > + return VLC_ENOMEM;
> > + free((char *)track->title);
> > + track->title = dup;
> > + }
> > +
> > + es_format_t fmtdup;
> > + int ret = es_format_Copy(&fmtdup, fmt);
> > + if (ret != VLC_SUCCESS)
> > + return ret;
> > +
> > + es_format_Clean(&track->fmt);
> > + track->fmt = fmtdup;
> > + return VLC_SUCCESS;
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
> > + const struct vlc_input_event_es *ev)
> > +{
> > + assert(ev->id && ev->title && ev->fmt);
> > +
> > + vlc_player_t *player = input->player;
> > + struct vlc_player_track *track;
> > + vlc_player_track_vector *vec =
> > + vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
> > +
> > + if (!vec)
> > + return; /* UNKNOWN_ES or DATA_ES not handled */
> > +
> > + switch (ev->action)
> > + {
> > + case VLC_INPUT_ES_ADDED:
> > + track = vlc_player_track_New(ev->id, ev->title, ev->fmt);
> > + if (!track)
> > + return;
> > +
> > + if (!vlc_vector_push(vec, track))
> > + {
> > + vlc_player_track_Release(track);
> > + break;
> > + }
> > + vlc_player_SendEvent(player, on_track_list_changed,
> > + VLC_PLAYER_LIST_ADDED, track);
> > + break;
> > + case VLC_INPUT_ES_DELETED:
> > + {
> > + size_t idx;
> > + track = vlc_player_track_vector_FindById(vec, ev->id, &idx);
> > + if (track)
> > + {
> > + vlc_player_SendEvent(player, on_track_list_changed,
> > + VLC_PLAYER_LIST_REMOVED, track);
> > + vlc_vector_remove(vec, idx);
> > + vlc_player_track_Release(track);
> > + }
> > + break;
> > + }
> > + case VLC_INPUT_ES_UPDATED:
> > + track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
> > + if (track
> > + && vlc_player_track_Update(track, ev->title, ev->fmt) == 0)
> > + vlc_player_SendEvent(player, on_track_list_changed,
> > + VLC_PLAYER_LIST_UPDATED, track);
> > + break;
> > + case VLC_INPUT_ES_SELECTED:
> > + track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
> > + if (track)
> > + {
> > + track->selected = true;
> > + vlc_player_SendEvent(player, on_track_selection_changed,
> > + NULL, track->id);
> > + }
> > + break;
> > + case VLC_INPUT_ES_UNSELECTED:
> > + track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
> > + if (track)
> > + {
> > + track->selected = false;
> > + vlc_player_SendEvent(player, on_track_selection_changed,
> > + track->id, NULL);
> > + }
> > + break;
> > + default:
> > + vlc_assert_unreachable();
> > + }
> > +}
> > +
> > +static void
> > +input_thread_events(input_thread_t *input_thread,
> > + const struct vlc_input_event *event, void *user_data)
> > +{
> > + struct vlc_player_input *input = user_data;
> > + vlc_player_t *player = input->player;
> > +
> > + assert(input_thread == input->thread);
> > +
> > + vlc_mutex_lock(&player->lock);
> > +
> > + if (!input->started)
> > + {
> > + if (event->type == INPUT_EVENT_DEAD)
> > + {
> > + /* Don't increment wait_count: already done by
> > + * vlc_player_input_StopAndClose() */
> > + vlc_list_append(&input->node, &player->destructor.inputs);
> > + vlc_cond_signal(&player->destructor.cond);
> > + }
> > +
> > + vlc_mutex_unlock(&player->lock);
> > + return;
> > + }
> > +
> > + switch (event->type)
> > + {
> > + case INPUT_EVENT_STATE:
> > + {
> > + enum vlc_player_state player_state;
> > + bool send_event = true;
> > +
> > + input->state = event->state;
> > + switch (input->state)
> > + {
> > + case OPENING_S:
> > + player_state = VLC_PLAYER_STATE_STARTED;
> > + break;
> > + case PLAYING_S:
> > + player_state = VLC_PLAYER_STATE_PLAYING;
> > + break;
> > + case PAUSE_S:
> > + player_state = VLC_PLAYER_STATE_PAUSED;
> > + break;
> > + case END_S:
> > + player_state = VLC_PLAYER_STATE_STOPPED;
> > + break;
> > + case ERROR_S:
> > + player_state = VLC_PLAYER_STATE_ERROR;
> > + break;
> > + default:
> > + send_event = false;
> > + break;
> > + }
> > + if (send_event)
> > + vlc_player_SendEvent(player, on_state_changed,
> > player_state); + break;
> > + }
> > + case INPUT_EVENT_RATE:
> > + input->rate = event->rate;
> > + vlc_player_SendEvent(player, on_rate_changed, input->rate);
> > + vlc_player_SendEvent(player, on_discontinuity, false);
> > + break;
> > + case INPUT_EVENT_CAPABILITIES:
> > + input->capabilities = event->capabilities;
> > + vlc_player_SendEvent(player, on_capabilities_changed,
> > + input->capabilities);
> > + break;
> > + case INPUT_EVENT_POSITION:
> > +#if GAPLESS
> > + /* XXX case INPUT_EVENT_EOF: */
> > + if (player->next_input == NULL)
> > + break;
> > + vlc_tick_t length = input->length;
> > + vlc_tick_t time = event->position.ms;
> > + if (length > 0 && time > 0
> > + && length - time <= AOUT_MAX_PREPARE_TIME)
> > + vlc_player_OpenNextItem(player);
> > +#endif
> > + input->position_ms = event->position.ms;
> > + input->position_percent = event->position.percentage;
> > + input->position_date = vlc_tick_now();
> > + break;
> > + case INPUT_EVENT_LENGTH:
> > + input->length = event->length;
> > + vlc_player_SendEvent(player, on_length_changed, input->length);
> > + break;
> > + case INPUT_EVENT_PROGRAM:
> > + vlc_player_input_HandleProgramEvent(input, &event->program);
> > + break;
> > + case INPUT_EVENT_ES:
> > + vlc_player_input_HandleEsEvent(input, &event->es);
> > + break;
> > + case INPUT_EVENT_TITLE:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_CHAPTER:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_RECORD:
> > + input->recording = event->record;
> > + vlc_player_SendEvent(player, on_record_changed,
> > input->recording); + break;
> > + case INPUT_EVENT_STATISTICS:
> > + input->stats = *event->stats;
> > + vlc_player_SendEvent(player, on_stats_changed, &input->stats);
> > + break;
> > + case INPUT_EVENT_SIGNAL:
> > + input->signal_quality = event->signal.quality;
> > + input->signal_strength = event->signal.strength;
> > + vlc_player_SendEvent(player, on_signal_changed,
> > + input->signal_quality,
> > input->signal_strength); + break;
> > + case INPUT_EVENT_AUDIO_DELAY:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_SUBTITLE_DELAY:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_CACHE:
> > + input->cache = event->cache;
> > + vlc_player_SendEvent(player, on_buffering, event->cache);
> > + break;
> > + case INPUT_EVENT_AOUT:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_VOUT:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_SUBITEMS:
> > + /* TODO */
> > + break;
> > + case INPUT_EVENT_DEAD:
> > + input->started = false;
> > + vlc_list_append(&input->node, &player->destructor.inputs);
> > + vlc_cond_signal(&player->destructor.cond);
> > + player->destructor.wait_count++;
> > +
> > + /* XXX: for now, play only one input at a time */
> > + if (likely(input == player->input))
> > + {
> > + player->input = NULL;
> > + if (vlc_player_OpenNextItem(player) == VLC_SUCCESS)
> > + {
> > + vlc_player_SendNewPlaybackEvent(player, player->media);
> > + vlc_player_input_Start(player->input);
> > + }
> > + }
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + vlc_mutex_unlock(&player->lock);
> > +}
> > +
> > +void
> > +vlc_player_Delete(vlc_player_t *player)
> > +{
> > + vlc_mutex_lock(&player->lock);
> > +
> > + if (player->input)
> > + vlc_player_input_StopAndClose(player->input);
> > +#if GAPLESS
> > + if (player->next_input)
> > + vlc_player_input_StopAndClose(player->next_input);
> > +#endif
> > +
> > + player->destructor.running = false;
> > + vlc_cond_signal(&player->destructor.cond);
> > +
> > + if (player->media)
> > + input_item_Release(player->media);
> > +#if GAPLESS
> > + if (player->next_media)
> > + input_item_Release(player->next_media);
> > +#endif
> > +
> > + assert(vlc_list_is_empty(&player->listeners));
> > +
> > + vlc_mutex_unlock(&player->lock);
> > +
> > + vlc_join(player->destructor.thread, NULL);
> > +
> > + vlc_mutex_destroy(&player->lock);
> > + vlc_cond_destroy(&player->destructor.cond);
> > +
> > + input_resource_Release(player->resource);
> > + if (player->renderer)
> > + vlc_renderer_item_release(player->renderer);
> > +
> > + vlc_object_release(player);
> > +}
> > +
> > +vlc_player_t *
> > +vlc_player_New(vlc_object_t *parent,
> > + const struct vlc_player_owner_cbs *owner_cbs,
> > + void *owner_cbs_data)
> > +{
> > + vlc_player_t *player = vlc_custom_create(parent, sizeof(*player),
> > "player"); + if (!player)
> > + return NULL;
> > +
> > + vlc_list_init(&player->destructor.inputs);
> > + player->renderer = NULL;
> > + player->owner_cbs = owner_cbs;
> > + player->owner_cbs_data = owner_cbs_data;
> > + player->media = NULL;
> > + player->input = NULL;
> > +#if GAPLESS
> > + player->next_media = NULL;
> > + player->next_input = NULL;
> > +#endif
> > + player->resource = input_resource_New(VLC_OBJECT(player));
> > +
> > + if (!player->resource)
> > + goto error;
> > +
> > + player->destructor.running = true;
> > + vlc_mutex_init(&player->lock);
> > + vlc_cond_init(&player->destructor.cond);
> > +
> > + if (vlc_clone(&player->destructor.thread, destructor_thread, player,
> > + VLC_THREAD_PRIORITY_LOW) != 0)
> > + {
> > + vlc_mutex_destroy(&player->lock);
> > + vlc_cond_destroy(&player->destructor.cond);
> > + goto error;
> > + }
> > +
> > + return player;
> > +
> > +error:
> > + if (player->resource)
> > + input_resource_Release(player->resource);
> > +
> > + vlc_object_release(player);
> > + return NULL;
> > +}
> > +
> > +void
> > +vlc_player_Lock(vlc_player_t *player)
> > +{
> > + vlc_mutex_lock(&player->lock);
> > +}
> > +
> > +void
> > +vlc_player_Unlock(vlc_player_t *player)
> > +{
> > + vlc_mutex_unlock(&player->lock);
> > +}
> > +
> > +struct vlc_player_listener_id *
> > +vlc_player_AddListener(vlc_player_t *player,
> > + const struct vlc_player_cbs *cbs, void *cbs_data)
> > +{
> > + assert(cbs);
> > + vlc_player_assert_locked(player);
> > +
> > + struct vlc_player_listener_id *listener = malloc(sizeof(*listener));
> > + if (!listener)
> > + return NULL;
> > +
> > + listener->cbs = cbs;
> > + listener->cbs_data = cbs_data;
> > +
> > + vlc_list_append(&listener->node, &player->listeners);
> > +
> > + return listener;
> > +}
> > +
> > +void
> > +vlc_player_RemoveListener(vlc_player_t *player,
> > + struct vlc_player_listener_id *id)
> > +{
> > + assert(id);
> > + vlc_player_assert_locked(player);
> > +
> > + vlc_list_remove(&id->node);
> > + free(id);
> > +}
> > +
> > +int
> > +vlc_player_SetCurrentMedia(vlc_player_t *player, input_item_t *media)
> > +{
> > + vlc_player_assert_locked(player);
> > +
> > + if (player->input)
> > + {
> > + vlc_player_input_StopAndClose(player->input);
> > + player->input = NULL;
> > + }
> > +#if GAPLESS
> > + if (player->next_input)
> > + {
> > + vlc_player_input_StopAndClose(player->next_input);
> > + player->next_input = NULL;
> > + }
> > +#endif
> > +
> > + if (player->media)
> > + {
> > + input_item_Release(player->media);
> > + player->media = NULL;
> > + }
> > +#if GAPLESS
> > + if (player->next_media)
> > + {
> > + input_item_Release(player->next_media);
> > + player->next_media = NULL;
> > + }
> > +#endif
> > +
> > + player->media = input_item_Hold(media);
> > + player->input = vlc_player_input_New(player, player->media);
> > +
> > + if (!player->input)
> > + {
> > + input_item_Release(player->media);
> > + player->media = NULL;
> > + return VLC_ENOMEM;
> > + }
> > + return VLC_SUCCESS;
> > +}
> > +
> > +input_item_t *
> > +vlc_player_GetCurrentMedia(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > +
> > + return player->media;
> > +}
> > +
> > +void
> > +vlc_player_InvalidateNextMedia(vlc_player_t *player)
> > +{
> > + (void) player;
> > +#if GAPLESS
> > + vlc_player_assert_locked(player);
> > + if (player->next_media)
> > + {
> > + input_item_Release(player->next_media);
> > + player->next_media = vlc_player_GetNextItem(player);
> > + }
> > + if (player->next_input)
> > + {
> > + /* Cause the get_next_media callback to be called when this input
> > is + * dead */
> > + vlc_player_input_StopAndClose(player->input);
> > + player->next_input = NULL;
> > + }
> > +#endif
> > +}
> > +
> > +int
> > +vlc_player_Start(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->media);
> > +
> > + if (!player->input)
> > + {
> > + /* Possible if the player was stopped by the user */
> > + assert(player->media);
> > + player->input = vlc_player_input_New(player, player->media);
> > +
> > + if (!player->input)
> > + return VLC_EGENERIC;
> > + }
> > + assert(!player->input->started);
> > +
> > + return vlc_player_input_Start(player->input);
> > +}
> > +
> > +void
> > +vlc_player_Stop(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input && player->input->started);
> > +
> > + vlc_player_input_StopAndClose(player->input);
> > + player->input = NULL;
> > +
> > +#if GAPLESS
> > + if (player->next_input)
> > + {
> > + vlc_player_input_StopAndClose(player->next_input);
> > + player->next_input = NULL;
> > + }
> > +#endif
> > +}
> > +
> > +void
> > +vlc_player_Pause(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input && player->input->started);
> > + assert(player->input->capabilities & VLC_INPUT_CAPABILITIES_PAUSEABLE);
> > +
> > + input_ControlPushHelper(player->input->thread, INPUT_CONTROL_SET_STATE,
> > + &(vlc_value_t) {.i_int = PAUSE_S});
> > +}
> > +
> > +void
> > +vlc_player_Resume(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > +
> > + assert(player->input && player->input->started);
> > + assert(player->input->capabilities & VLC_INPUT_CAPABILITIES_PAUSEABLE);
> > +
> > + input_ControlPushHelper(player->input->thread, INPUT_CONTROL_SET_STATE,
> > + &(vlc_value_t) { .i_int = PLAYING_S }); +}
> > +
> > +bool
> > +vlc_player_IsStarted(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + return player->input && player->input->started;
> > +}
> > +
> > +bool
> > +vlc_player_IsPaused(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + return !player->input || player->input->state != PLAYING_S;
> > +}
> > +
> > +int
> > +vlc_player_GetCapabilities(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > + return player->input->capabilities;
> > +}
> > +
> > +vlc_tick_t
> > +vlc_player_GetLength(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > + return player->input->length;
> > +}
> > +
> > +vlc_tick_t
> > +vlc_player_GetTime(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > +
> > + vlc_tick_t time = input->position_ms;
> > + return time != VLC_TICK_INVALID ?
> > + (time + (vlc_tick_now() - input->position_date) * input->rate) :
> > + VLC_TICK_INVALID;
> > +}
> > +
> > +float
> > +vlc_player_GetPosition(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + assert(player->input);
> > +
> > + return player->input->position_percent;
> > +}
> > +
> > +void
> > +vlc_player_Seek(vlc_player_t *player, const struct vlc_player_seek_arg
> > *arg) +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > + assert(arg);
> > +
> > + switch (arg->type)
> > + {
> > + case VLC_PLAYER_SEEK_BY_POS:
> > + input_ControlPush(input->thread, INPUT_CONTROL_SET_POSITION,
> > + &(input_control_param_t) {
> > + .pos.f_val = arg->position,
> > + .pos.b_fast_seek = arg->fast,
> > + .pos.b_absolute = arg->absolute
> > + });
> > + break;
> > + case VLC_PLAYER_SEEK_BY_TIME:
> > + input_ControlPush(input->thread, INPUT_CONTROL_SET_TIME,
> > + &(input_control_param_t) {
> > + .time.i_val = arg->time,
> > + .time.b_fast_seek = arg->fast,
> > + .time.b_absolute = arg->absolute,
> > + });
> > + break;
> > + default:
> > + vlc_assert_unreachable();
> > + }
> > +}
> > +
> > +void
> > +vlc_player_SetRenderer(vlc_player_t *player, vlc_renderer_item_t *renderer)
> > +{
> > + vlc_player_assert_locked(player);
> > +
> > + if (player->renderer)
> > + vlc_renderer_item_release(player->renderer);
> > + player->renderer = renderer ? vlc_renderer_item_hold(renderer) : NULL;
> > +
> > + vlc_player_foreach_inputs(input)
> > + {
> > + vlc_value_t val = {
> > + .p_address = renderer ? vlc_renderer_item_hold(renderer) : NULL
> > + };
> > + input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_RENDERER,
> > + &val);
> > + }
> > +}
> > +
> > +void
> > +vlc_player_Navigate(vlc_player_t *player, enum vlc_player_nav nav)
> > +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > +
> > + enum input_control_e control;
> > + switch (nav)
> > + {
> > + case VLC_PLAYER_NAV_ACTIVATE:
> > + control = INPUT_CONTROL_NAV_ACTIVATE;
> > + break;
> > + case VLC_PLAYER_NAV_UP:
> > + control = INPUT_CONTROL_NAV_UP;
> > + break;
> > + case VLC_PLAYER_NAV_DOWN:
> > + control = INPUT_CONTROL_NAV_DOWN;
> > + break;
> > + case VLC_PLAYER_NAV_LEFT:
> > + control = INPUT_CONTROL_NAV_LEFT;
> > + break;
> > + case VLC_PLAYER_NAV_RIGHT:
> > + control = INPUT_CONTROL_NAV_RIGHT;
> > + break;
> > + case VLC_PLAYER_NAV_POPUP:
> > + control = INPUT_CONTROL_NAV_POPUP;
> > + break;
> > + case VLC_PLAYER_NAV_MENU:
> > + control = INPUT_CONTROL_NAV_MENU;
> > + break;
> > + default:
> > + vlc_assert_unreachable();
> > + }
> > + input_ControlPushHelper(input->thread, control, NULL);
> > +}
> > +
> > +bool
> > +vlc_player_IsRecording(vlc_player_t *player)
> > +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > + return input->recording;
> > +}
> > +
> > +int
> > +vlc_player_GetSignal(vlc_player_t *player, float *quality, float *strength)
> > +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > +
> > + if (input->signal_quality >= 0 && input->signal_strength >= 0)
> > + {
> > + *quality = input->signal_quality;
> > + *strength = input->signal_strength;
> > + return VLC_SUCCESS;
> > + }
> > + return VLC_EGENERIC;
> > +}
> > +
> > +void
> > +vlc_player_GetStats(vlc_player_t *player, struct input_stats_t *stats)
> > +{
> > + vlc_player_assert_locked(player);
> > + struct vlc_player_input *input = player->input;
> > + assert(input);
> > +
> > + *stats = input->stats;
> > +}
> > diff --git a/src/libvlccore.sym b/src/libvlccore.sym
> > index a1709cb9f6..3af460923b 100644
> > --- a/src/libvlccore.sym
> > +++ b/src/libvlccore.sym
> > @@ -784,3 +784,31 @@ vlc_es_id_Hold
> > vlc_es_id_Release
> > vlc_es_id_GetInputId
> > vlc_es_id_GetCat
> > +vlc_player_AddListener
> > +vlc_player_Delete
> > +vlc_player_GetCapabilities
> > +vlc_player_GetCurrentMedia
> > +vlc_player_GetProgram
> > +vlc_player_GetProgramAt
> > +vlc_player_GetProgramCount
> > +vlc_player_GetTrack
> > +vlc_player_GetTrackAt
> > +vlc_player_GetTrackCount
> > +vlc_player_InvalidateNextMedia
> > +vlc_player_IsPaused
> > +vlc_player_IsStarted
> > +vlc_player_Lock
> > +vlc_player_New
> > +vlc_player_Pause
> > +vlc_player_RemoveListener
> > +vlc_player_RestartTrack
> > +vlc_player_Resume
> > +vlc_player_SelectDefaultTrack
> > +vlc_player_SelectProgram
> > +vlc_player_SelectTrack
> > +vlc_player_SetCurrentMedia
> > +vlc_player_SetRenderer
> > +vlc_player_Start
> > +vlc_player_Stop
> > +vlc_player_Unlock
> > +vlc_player_UnselectTrack
>
>
> --
> Rémi Denis-Courmont
>
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list