[vlc-devel] [PATCH 1/2] core: add player API

Thomas Guillem thomas at gllm.fr
Wed Oct 24 09:38:53 CEST 2018



On Tue, Oct 23, 2018, at 17:55, Hugo Beauzée-Luyssen wrote:
> Hi,
> 
> On Mon, Oct 22, 2018, at 6:11 PM, Thomas Guillem wrote:
> > This API will replace the usage of input_thread_t from interface modules. The
> > player implementation continue to use input_thread_t in private. The goal is to
> > hide the input_thread_t API when every modules are switched to the player API.
> > 
> > TODO (all need to be fixed, by me, for VLC 4.0):
> > 
> >  - Gapless: API is complete but not implemented (the player can play several
> >    medias in a row, but without gapless).
> >  - Position callbacks: still using the input_thread_t position that is really
> >    imprecise (notified every 250ms minimum, and sometime more, depending on
> >    pf_demux implementation).
> >  - Seek/discontinuity callbacks: when seeking, the player can still send
> >    position of the requested position, the actual position or the next position
> >    to come. This leads to UI inconsistency.
> > ---
> >  include/vlc_player.h | 2407 ++++++++++++++++++++++++++++++++++++
> >  po/POTFILES.in       |    1 +
> >  src/Makefile.am      |    3 +
> >  src/input/player.c   | 2804 ++++++++++++++++++++++++++++++++++++++++++
> >  src/input/player.h   |   35 +
> >  src/libvlccore.sym   |   97 ++
> >  6 files changed, 5347 insertions(+)
> >  create mode 100644 include/vlc_player.h
> >  create mode 100644 src/input/player.c
> >  create mode 100644 src/input/player.h
> > 
> > diff --git a/include/vlc_player.h b/include/vlc_player.h
> > new file mode 100644
> > index 0000000000..0e0a9f3d11
> > --- /dev/null
> > +++ b/include/vlc_player.h
> > @@ -0,0 +1,2407 @@
> > +/
> > *****************************************************************************
> > + * 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>
> > +#include <vlc_aout.h>
> > +
> > +/**
> > + * @defgroup player Player
> > + * @ingroup input
> > + * VLC Player API
> > + * @brief
> > + at dot
> > +digraph player_states {
> > +  label="Player state diagram";
> > +  new [style="invis"];
> > +  started [label="Started" URL="@ref VLC_PLAYER_STATE_STARTED"];
> > +  playing [label="Playing" URL="@ref VLC_PLAYER_STATE_PLAYING"];
> > +  paused [label="Paused" URL="@ref VLC_PLAYER_STATE_PAUSED"];
> > +  stopping [label="Stopping" URL="@ref VLC_PLAYER_STATE_STOPPING"];
> > +  stopped [label="Stopped" URL="@ref VLC_PLAYER_STATE_STOPPED"];
> > +  new -> stopped [label="vlc_player_New()" URL="@ref vlc_player_New" 
> > fontcolor="green3"];
> > +  started -> playing [style="dashed" label=<<i>internal transition</
> > i>>];
> > +  started -> stopping [label="vlc_player_Stop()" URL="@ref 
> > vlc_player_Stop" fontcolor="red"];
> > +  playing -> paused [label="vlc_player_Pause()" URL="@ref 
> > vlc_player_Pause" fontcolor="blue"];
> > +  paused -> playing [label="vlc_player_Resume()" URL="@ref 
> > vlc_player_Resume" fontcolor="blue3"];
> > +  paused -> stopping [label="vlc_player_Stop()" URL="@ref 
> > vlc_player_Stop" fontcolor="red"];
> > +  playing -> stopping [label="vlc_player_Stop()" URL="@ref 
> > vlc_player_Stop" fontcolor="red"];
> > +  stopping -> stopped [style="dashed" label=<<i>internal transition</
> > i>>];
> > +  stopped -> started [label="vlc_player_Start()" URL="@ref 
> > vlc_player_Start" fontcolor="darkgreen"];
> > +}
> > + at enddot
> > + * @{
> > + * @file
> > + * VLC Player API
> > + */
> > +
> > +/**
> > + * Player opaque 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().
> > + */
> > +typedef struct vlc_player_listener_id vlc_player_listener_id;
> > +
> > +/**
> > + * Player vout listener opaque structure.
> > + *
> > + * This opaque structure is returned by vlc_player_vout_AddListener() 
> > and can
> > + * be used to remove the listener via vlc_player_vout_RemoveListener().
> > + */
> > +typedef struct vlc_player_vout_listener_id vlc_player_vout_listener_id;
> > +
> > +/**
> > + * Player aout listener opaque structure.
> > + *
> > + * This opaque structure is returned by vlc_player_aout_AddListener() 
> > and can
> > + * be used to remove the listener via vlc_player_aout_RemoveListener().
> > + */
> > +typedef struct vlc_player_aout_listener_id vlc_player_aout_listener_id;
> > +
> > +/**
> > + * Player program structure.
> > + */
> > +struct vlc_player_program
> > +{
> > +    /** Id used for vlc_player_SelectProgram() */
> > +    int group_id;
> > +    /** Program name, always valid */
> > +    const char *name;
> > +    /** True if the program is selected */
> > +    bool selected;
> > +    /** True if the program is scrambled */
> > +    bool scrambled;
> > +};
> > +
> > +/**
> > + * Player track structure.
> > + */
> > +struct vlc_player_track
> > +{
> > +    /** Id used for any player actions, like vlc_player_SelectTrack() 
> > */
> > +    vlc_es_id_t *es_id;
> > +    /** Track name, always valid */
> > +    const char *name;
> > +    /** Es format */
> > +    es_format_t fmt;
> > +    /** True if the track is selected */
> > +    bool selected;
> > +};
> > +
> > +/**
> > + * Player chapter structure
> > + */
> > +struct vlc_player_chapter
> > +{
> > +    /** Chapter name, always valid */
> > +    const char *name;
> > +    /** Position of this chapter */
> > +    vlc_tick_t time;
> > +};
> > +
> > +/** vlc_player_title.flags: The title is a menu. */
> > +#define VLC_PLAYER_TITLE_MENU         0x01
> > +/** vlc_player_title.flags: The title is interactive. */
> > +#define VLC_PLAYER_TITLE_INTERACTIVE  0x02
> > +
> > +/** Player title structure */
> > +struct vlc_player_title
> > +{
> > +    /** Title name, always valid */
> > +    const char *name;
> > +    /** Length of the title */
> > +    vlc_tick_t length;
> > +    /** Bit flag of @ref VLC_PLAYER_TITLE_MENU and @ref
> > +     * VLC_PLAYER_TITLE_INTERACTIVE */
> > +    unsigned flags;
> > +    /** Number of chapters, can be 0 */
> > +    size_t chapter_count;
> > +    /** Array of chapters, can be NULL */
> > +    const struct vlc_player_chapter *chapters;
> > +};
> > +
> > +/**
> > + * Opaque structure representing a list of @ref vlc_player_title.
> > + *
> > + * @see vlc_player_GetTitleList()
> > + * @see vlc_player_title_list_GetCount()
> > + * @see vlc_player_title_list_GetAt()
> > + */
> > +typedef struct vlc_player_title_list vlc_player_title_list;
> > +
> > +/**
> > + * Menu (VCD/DVD/BD) and viewpoint navigations
> > + *
> > + * @see vlc_player_Navigate()
> > + */
> > +enum vlc_player_nav
> > +{
> > +    /** Activate the navigation item selected */
> > +    VLC_PLAYER_NAV_ACTIVATE,
> > +    /** Select a navigation item above or move the viewpoint up */
> > +    VLC_PLAYER_NAV_UP,
> > +    /** Select a navigation item under or move the viewpoint down */
> > +    VLC_PLAYER_NAV_DOWN,
> > +    /** Select a navigation item on the left or move the viewpoint left 
> > */
> > +    VLC_PLAYER_NAV_LEFT,
> > +    /** Select a navigation item on the right or move the viewpoint 
> > 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()), and
> > + * from any thread (even the current one).
> > + */
> > +struct vlc_player_media_provider
> > +{
> > +    /**
> > +     * Called when the player requires a new media.
> > +     *
> > +     * @note The returned media must be already held with 
> > input_item_Hold()
> > +     *
> > +     * @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)(vlc_player_t *player, void *data);
> > +};
> > +
> > +/**
> > + * Action of vlc_player_cbs.on_track_list_changed,
> > + * vlc_player_cbs.on_program_list_changed, and
> > + * vlc_player_cbs.on_vout_list_changed callbacks
> > + */
> > +enum vlc_player_list_action
> > +{
> > +    VLC_PLAYER_LIST_ADDED,
> > +    VLC_PLAYER_LIST_REMOVED,
> > +    VLC_PLAYER_LIST_UPDATED,
> > +};
> > +
> > +/**
> > + * State of the player
> > + *
> > + * During a normal playback (no errors), the user is expected to 
> > receive all
> > + * events in the following order: STARTED, PLAYING, STOPPING, STOPPED.
> > + *
> > + * @note When playing more than one media in a row, the player stay at 
> > the
> > + * PLAYING state when doing the transition from the current media to 
> > the next
> > + * media (that can be gapless). This means that STOPPING, STOPPED 
> > states (for
> > + * the current media) and STARTED, PLAYING states (for the next one) 
> > won't be
> > + * sent. Nevertheless, the vlc_player_cbs.on_current_media_changed 
> > callback
> > + * will be called during this transition.
> > + */
> > +enum vlc_player_state
> > +{
> > +    /**
> > +     * The player is stopped
> > +     *
> > +     * Initial state, or triggered by an internal transition from the 
> > STOPPING
> > +     * state.
> > +     */
> > +    VLC_PLAYER_STATE_STOPPED,
> > +
> > +    /**
> > +     * The player is started
> > +     *
> > +     * Triggered by vlc_player_Start()
> > +     */
> > +    VLC_PLAYER_STATE_STARTED,
> > +
> > +    /**
> > +     * The player is playing
> > +     *
> > +     * Triggered by vlc_player_Resume() or by an internal transition 
> > from the
> > +     * STARTED state.
> > +     */
> > +    VLC_PLAYER_STATE_PLAYING,
> > +
> > +    /**
> > +     * The player is paused
> > +     *
> > +     * Triggered by vlc_player_Pause().
> > +     */
> > +    VLC_PLAYER_STATE_PAUSED,
> > +
> > +    /**
> > +     * The player is stopping
> > +     *
> > +     * Triggered by vlc_player_Stop(), vlc_player_SetCurrentMedia() or 
> > by an
> > +     * internal transition (when the input reach the end of file for 
> > example).
> > +     */
> > +    VLC_PLAYER_STATE_STOPPING,
> 
> Will this state be triggered when switching between 2 media when gapless 
> is implemented?

No, see @note on struct vlc_player_state

> 
> > +};
> > +
> > +/**
> > + * Error of the player
> > + *
> > + * @see vlc_player_GetError()
> > + */
> > +enum vlc_player_error
> > +{
> > +    VLC_PLAYER_ERROR_NONE,
> > +    VLC_PLAYER_ERROR_GENERIC,
> > +};
> > +
> > +/**
> > + * Seek speed type
> > + *
> > + * @see vlc_player_SeekByPos()
> > + * @see vlc_player_SeekByTime()
> > + */
> > +enum vlc_player_seek_speed
> > +{
> > +    /** Do a precise seek */
> > +    VLC_PLAYER_SEEK_PRECISE,
> > +    /** Do a fast seek */
> > +    VLC_PLAYER_SEEK_FAST,
> > +};
> > +
> > +/**
> > + * Player seek/delay directive
> > + *
> > + * @see vlc_player_SeekByPos()
> > + * @see vlc_player_SeekByTime()
> > + * @see vlc_player_SetAudioDelay()
> > + * @see vlc_player_SetSubtitleDelay()
> > + */
> > +enum vlc_player_whence
> > +{
> > +    /** Given time/position */
> > +    VLC_PLAYER_WHENCE_ABSOLUTE,
> > +    /** The current position +/- the given time/position */
> > +    VLC_PLAYER_WHENCE_RELATIVE,
> > +};
> > +
> > +/**
> > + * Action when the player is stopped
> > + *
> > + * @see vlc_player_SetMediaStoppedAction()
> > + */
> > +enum vlc_player_media_stopped_action {
> > +    /** Continue (or stop if there is no next media), default behavior 
> > */
> > +    VLC_PLAYER_MEDIA_STOPPED_CONTINUE,
> > +    /** Pause when reaching the end of file */
> > +    VLC_PLAYER_MEDIA_STOPPED_PAUSE,
> > +    /** Stop, even if there is a next media to play */
> > +    VLC_PLAYER_MEDIA_STOPPED_STOP,
> > +    /** Exit VLC */
> > +    VLC_PLAYER_MEDIA_STOPPED_EXIT,
> > +};
> > +
> > +/**
> > + * A to B loop state
> > + */
> > +enum vlc_player_abloop
> > +{
> > +    VLC_PLAYER_ABLOOP_NONE,
> > +    VLC_PLAYER_ABLOOP_A,
> > +    VLC_PLAYER_ABLOOP_B,
> > +};
> > +
> > +/** Player capability: can seek */
> > +#define VLC_PLAYER_CAP_SEEK (1<<0)
> > +/** Player capability: can pause */
> > +#define VLC_PLAYER_CAP_PAUSE (1<<1)
> > +/** Player capability: can change the rate */
> > +#define VLC_PLAYER_CAP_CHANGE_RATE (1<<2)
> > +/** Player capability: can seek back */
> > +#define VLC_PLAYER_CAP_REWIND (1<<3)
> > +
> > +/** Player teletext key: Red */
> > +#define VLC_PLAYER_TELETEXT_KEY_RED ('r' << 16)
> > +/** Player teletext key: Green */
> > +#define VLC_PLAYER_TELETEXT_KEY_GREEN ('g' << 16)
> > +/** Player teletext key: Yellow */
> > +#define VLC_PLAYER_TELETEXT_KEY_YELLOW ('g' << 16)
> 
> This should probably be 'y'

Indeed,

> 
> > +/** Player teletext key: Blue */
> > +#define VLC_PLAYER_TELETEXT_KEY_BLUE ('b' << 16)
> > +/** Player teletext key: Index */
> > +#define VLC_PLAYER_TELETEXT_KEY_INDEX ('i' << 16)
> > +
> > +/**
> > + * Player callbacks
> > + *
> > + * Can be registered with vlc_player_AddListener().
> > + *
> > + * All callbacks are called with the player locked (cf. 
> > vlc_player_Lock()) and
> > + * from any threads (and even synchronously from a vlc_player function 
> > in some
> > + * cases). It is safe to call any vlc_player functions from these 
> > callbacks
> > + * except vlc_player_Delete().
> > + *
> > + * @warning To avoid deadlocks, users should never call vlc_player 
> > functions
> > + * with an external mutex locked and lock this same mutex from a player
> > + * callback.
> > + */
> > +struct vlc_player_cbs
> > +{
> > +    /**
> > +     * Called when the current media has changed
> > +     *
> > +     * @note This can be called from the PLAYING state (when the player 
> > plays
> > +     * the next media internally) or from the STOPPED state (from
> > +     * vlc_player_SetCurrentMedia() or from an internal transition).
> > +     *
> > +     * @see vlc_player_SetCurrentMedia()
> > +     * @see vlc_player_InvalidateNextMedia()
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_media new media currently played or NULL (when there 
> > is no
> > +     * more media to play)
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_current_media_changed)(vlc_player_t *player,
> > +        input_item_t *new_media, void *data);
> > +
> > +    /**
> > +     * Called when the player state has changed
> > +     *
> > +     * @see vlc_player_state
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_state new player state
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_state_changed)(vlc_player_t *player,
> > +        enum vlc_player_state new_state, void *data);
> > +
> > +    /**
> > +     * Called when a media triggered an error
> > +     *
> > +     * Can be called from any states. When it happens the player will 
> > stop
> > +     * itself. It is safe to play an other media or event restart the 
> > player
> > +     * (This will reset the error state).
> > +     *
> > +     * @param player locked player instance
> > +     * @param error player error
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_error_changed)(vlc_player_t *player,
> > +        enum vlc_player_error error, void *data);
> > +
> > +    /**
> > +     * Called when the player buffering (or cache) has changed
> > +     *
> > +     * This event is always called with the 0 and 1 values before a 
> > playback
> > +     * (in case of success).  Values in between depends of the media 
> > type.
> 
> Depends on*

noted,

> 
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_buffering buffering in the range [0:1]
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_buffering_changed)(vlc_player_t *player,
> > +        float new_buffering, void *data);
> > +
> > +    /**
> > +     * Called when the player rate has changed
> > +     *
> > +     * Triggered by vlc_player_ChangeRate(), not sent when the media 
> > starts
> > +     * with the default rate (1.f)
> > +     *
> > +     * @note The rate is saved across several medias.
> 
> I'm not sure I understand that note, do you mean multiple media are 
> sharing the rate from the player, or the rate will be saved in each 
> media, causing them not to use an updated version of the rate?

This note should not be here, but in vlc_player_ChangeRate().
I meant that the rate is saved across several media. It's like the current behavior on 3.0.

> 
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_rate player
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_rate_changed)(vlc_player_t *player,
> > +        float new_rate, void *data);
> > +
> > +    /**
> > +     * Called when the media capabilities has changed
> > +     *
> > +     * Always called when the media is opening. Can be called during 
> > playback.
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_caps player capabilities
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_capabilities_changed)(vlc_player_t *player,
> > +        int new_caps, void *data);
> > +
> > +    /**
> > +     * Called when the player position has changed
> > +     *
> > +     * @note A started and playing media doesn't have necessarily a 
> > valid time.
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_time a valid time or VLC_TICK_INVALID
> > +     * @param new_pos a valid position
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_position_changed)(vlc_player_t *player,
> > +        vlc_tick_t new_time, float new_pos, void *data);
> > +
> > +    /**
> > +     * Called when the media length has changed
> > +     *
> > +     * May be called when the media is opening or during playback.
> > +     *
> > +     * @note A started and playing media doesn't have necessarily a 
> > valid length.
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_length a valid time or VLC_TICK_INVALID
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_length_changed)(vlc_player_t *player,
> > +        vlc_tick_t new_length, void *data);
> > +
> > +    /**
> > +     * Called when a track is added, removed, or updated
> > +     *
> > +     * @note The track is only valid from this callback context. Users 
> > should
> > +     * duplicate this track via vlc_player_track_Dup() if they want to 
> > pass it
> > +     * to an other thread.
> 
> Should this be more about keeping the reference longer that the scope of 
> the callback rather than another thread? Ditto below

Yes, fixed.

> 
> > +     *
> > +     * @param player locked player instance
> > +     * @param action added, removed or updated
> > +     * @param track valid track
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_track_list_changed)(vlc_player_t *player,
> > +        enum vlc_player_list_action action,
> > +        const struct vlc_player_track *track, void *data);
> > +
> > +    /**
> > +     * Called when a new track is selected and/or unselected
> > +     *
> > +     * @param player locked player instance
> > +     * @param unselected_id valid track id or NULL (when nothing is 
> > unselected)
> > +     * @param selected_id valid track id or NULL (when nothing is 
> > selected)
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_track_selection_changed)(vlc_player_t *player,
> > +        vlc_es_id_t *unselected_id, vlc_es_id_t *selected_id, void 
> > *data);
> > +
> > +    /**
> > +     * Called when a new program is added, removed or updated
> > +     *
> > +     * @note The program is only valid from this callback context. 
> > Users should
> > +     * duplicate this program via vlc_player_program_Dup() if they want 
> > to pass
> > +     * it to an other thread.
> > +     *
> > +     * @param player locked player instance
> > +     * @param action added, removed or updated
> > +     * @param prgm valid program
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_program_list_changed)(vlc_player_t *player,
> > +        enum vlc_player_list_action action,
> > +        const struct vlc_player_program *prgm, void *data);
> > +
> > +    /**
> > +     * Called when a new program is selected and/or unselected
> > +     *
> > +     * @param player locked player instance
> > +     * @param unselected_id valid program id or NULL (when nothing is 
> > unselected)
> > +     * @param selected_id valid program id or NULL (when nothing is 
> > selected)
> 
> I suppose you mean '0' instead of NULL.

Ooops, I meant -1 in fact.

> Wouldn't it be simpler/clearer to have a 'program_id' and a 'seletected' boolean?
Then you can't handle atomic transitions when a new program is replacing an old one.

I'll add the following note:

     * @note This event can be called with both unselected_id and selected_id
     * valid. This mean that a new program is replacing the old one.

> 
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_program_selection_changed)(vlc_player_t *player,
> > +        int unselected_id, int selected_id, void *data);
> > +
> > +    /**
> > +     * Called when the media titles has changed
> > +     *
> > +     * This event is not called when the opening media doesn't have any 
> > titles.
> > +     * This title list and all its elements are constant. If an element 
> > is to
> > +     * be updated, a new list will be sent from this callback.
> > +     *
> > +     * @note Users should hold this list with 
> > vlc_player_title_list_Hold() if
> > +     * they want to pass it to an other thread.
> > +     *
> > +     * @param player locked player instance
> > +     * @param titles valid title list or NULL
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_titles_changed)(vlc_player_t *player,
> > +        vlc_player_title_list *titles, void *data);
> > +
> > +    /**
> > +     * Called when a new title is selected
> > +     *
> > +     * There are no events when a title is unselected. Titles are 
> > automatically
> > +     * unselected when the title list changes. Titles and indexes are 
> > always
> > +     * valid inside the vlc_player_title_list sent by
> > +     * vlc_player_cbs.on_titles_changed.
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_title new selected title
> > +     * @param new_idx index of this title
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_title_selection_changed)(vlc_player_t *player,
> > +        const struct vlc_player_title *new_title, size_t new_idx, void 
> > *data);
> > +
> > +    /**
> > +     * Called when a new chapter is selected
> > +     *
> > +     * There are no events when a chapter is unselected. Chapters are
> > +     * automatically unselected when the title list changes. Titles, 
> > chapters
> > +     * and indexes are always valid inside the vlc_player_title_list 
> > sent by
> > +     * vlc_player_cbs.on_titles_changed.
> > +     *
> > +     * @param player locked player instance
> > +     * @param title selected title
> > +     * @param title_idx selected title index
> > +     * @param chapter new selected chapter
> > +     * @param chapter_idx new selected chapter index
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_chapter_selection_changed)(vlc_player_t *player,
> > +        const struct vlc_player_title *title, size_t title_idx,
> > +        const struct vlc_player_chapter *new_chapter, size_t 
> > new_chapter_idx,
> > +        void *data);
> > +
> > +    /**
> > +     * Called when the media has a teletext menu
> > +     *
> > +     * @param player locked player instance
> > +     * @param has_teletext_menu true if the media has a teletext menu
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_teletext_menu_changed)(vlc_player_t *player,
> > +        bool has_teletext_menu, void *data);
> > +
> > +    /**
> > +     * Called when teletext is enabled or disabled
> > +     *
> > +     * @see vlc_player_SetTeletextEnabled()
> > +     *
> > +     * @param player locked player instance
> > +     * @param enabled true if teletext is enabled
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_teletext_enabled_changed)(vlc_player_t *player,
> > +        bool enabled, void *data);
> > +
> > +    /**
> > +     * Called when the teletext page has changed
> > +     *
> > +     * @see vlc_player_SelectTeletextPage()
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_page page in the range ]0;888]
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_teletext_page_changed)(vlc_player_t *player,
> > +        unsigned new_page, void *data);
> > +
> > +    /**
> > +     * Called when the teletext transparency has changed
> > +     *
> > +     * @see vlc_player_SetTeletextTransparency()
> > +     *
> > +     * @param player locked player instance
> > +     * @param enabled true is the teletext overlay is transparent
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_teletext_transparency_changed)(vlc_player_t *player,
> > +        bool enabled, void *data);
> > +
> > +    /**
> > +     * Called when the player audio delay has changed
> > +     *
> > +     * @see vlc_player_SetAudioDelay()
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_delay audio delay
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_audio_delay_changed)(vlc_player_t *player,
> > +        vlc_tick_t new_delay, void *data);
> > +
> > +    /**
> > +     * Called when the player subtitle delay has changed
> > +     *
> > +     * @see vlc_player_SetSubtitleDelay()
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_delay subtitle delay
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_subtitle_delay_changed)(vlc_player_t *player,
> > +        vlc_tick_t new_delay, void *data);
> > +
> > +    /**
> > +     * Called when associated subtitle has changed
> > +     *
> > +     * @see vlc_player_SetAssociatedSubsFPS()
> > +     *
> > +     * @param player locked player instance
> > +     * @param sub_fps subtitle fps
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_associated_subs_fps_changed)(vlc_player_t *player,
> > +        float subs_fps, void *data);
> > +
> > +    /**
> > +     * Called when a new renderer item is set
> > +     *
> > +     * @see vlc_player_SetRenderer()
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_item a valid renderer item or NULL (if unset)
> 
> Is the renderer item held?

Yes, I'll add a comment

> 
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_renderer_changed)(vlc_player_t *player,
> > +        vlc_renderer_item_t *new_item, void *data);
> > +
> > +    /**
> > +     * Called when the player recording state has changed
> > +     *
> > +     * @see vlc_player_SetRecordingEnabled()
> > +     *
> > +     * @param player locked player instance
> > +     * @param recording true if recording is enabled
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_recording_changed)(vlc_player_t *player,
> > +        bool recording, void *data);
> > +
> > +    /**
> > +     * Called when the media signal has changed
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_quality signal quality
> > +     * @param new_strength signal strength,
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_signal_changed)(vlc_player_t *player,
> > +        float quality, float strength, void *data);
> > +
> > +    /**
> > +     * Called when the player has new statisics
> > +     *
> > +     * @note The stats structure is only valid from this callback 
> > context. It
> > +     * can be copied in order to pass it to an other thread.
> > +     *
> > +     * @param player locked player instance
> > +     * @param stats valid stats, only valid from this context
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_statistics_changed)(vlc_player_t *player,
> > +        const struct input_stats_t *stats, void *data);
> > +
> > +    /**
> > +     * Called when the A to B loop has changed
> > +     *
> > +     * @see vlc_player_SetAtoBLoop()
> > +     *
> > +     * @param player locked player instance
> > +     * @param state A, when only A is set, B when both A and B are set, 
> > None by
> > +     * default
> > +     * @param time valid time or VLC_TICK_INVALID of the current state
> > +     * @param pos valid pos of the current state
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_atobloop_changed)(vlc_player_t *player,
> > +        enum vlc_player_abloop new_state, vlc_tick_t time, float pos,
> > +        void *data);
> > +
> > +    /**
> > +     * Called when media stopped action has changed
> > +     *
> > +     * @see vlc_player_SetMediaStoppedAction()
> > +     *
> > +     * @param player locked player instance
> > +     * @param new_action action to execute when a media is stopped
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_media_stopped_action_changed)(vlc_player_t *player,
> > +        enum vlc_player_media_stopped_action new_action, void *data);
> > +
> > +    /**
> > +     * Called when the media meta has changed
> > +     *
> > +     * @param player locked player instance
> > +     * @param media valid media
> 
> Is the media held? And ditto below

This media is the current media held by the player. This is no need to release it from this callback.
If not mentioned, is it obvious to *not* release it by default?

> 
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_media_meta_changed)(vlc_player_t *player,
> > +        input_item_t *media, void *data);
> > +
> > +    /**
> > +     * Called when media epg has changed
> > +     *
> > +     * @param player locked player instance
> > +     * @param media valid media
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_media_epg_changed)(vlc_player_t *player,
> > +        input_item_t *media, void *data);
> > +
> > +    /**
> > +     * Called when the media has new subitems
> > +     *
> > +     * @param player locked player instance
> > +     * @param media valid media
> > +     * @param new_subitems node representing all media subitems
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_media_subitems_changed)(vlc_player_t *player,
> > +        input_item_t *media, input_item_node_t *new_subitems, void 
> > *data);
> > +
> > +    /**
> > +     * Called when a new vout is added or removed
> > +     *
> > +     * @param player locked player instance
> > +     * @param action added or removed
> > +     * @param vout vout this added or removed
> 
> I think you typo'ed :)

yep

> 
> > +     * @param data opaque pointer set by vlc_player_AddListener()
> > +     */
> > +    void (*on_vout_list_changed)(vlc_player_t *player,
> > +        enum vlc_player_list_action action, vout_thread_t *vout, void 
> > *data);
> > +};
> > +
> > +/**
> > + * Player vout callbacks
> > + *
> > + * Can be registered with vlc_player_vout_AddListener().
> > + *
> > + * These callbacks are *not* called with the player locked. It is safe 
> > to lock
> > + * the player and call any vlc_player functions from these callbacks.
> > + *
> > + * @note The state changed from the callbacks can be either applied on 
> > the
> > + * player (and all future video outputs), or on a specified video 
> > output. The
> > + * state is applied on the player when the vout argument is NULL.
> > + *
> > + * @warning To avoid deadlocks, users should never call vout_thread_t 
> > functions
> > + * from these callbacks.
> > + */
> > +struct vlc_player_vout_cbs
> > +{
> > +    /**
> > +     * Called when the player and/or vout fullscreen state has changed
> > +     *
> > +     * @see vlc_player_vout_SetFullscreen()
> > +     *
> > +     * @param player unlocked player instance
> > +     * @param vout cf. vlc_player_vout_cbs note
> > +     * @param enabled true when fullscreen is enabled
> > +     * @param data opaque pointer set by vlc_player_vout_AddListener()
> > +     */
> > +    void (*on_fullscreen_changed)(vlc_player_t *player,
> > +        vout_thread_t *vout, bool enabled, void *data);
> > +
> > +    /**
> > +     * Called when the player and/or vout wallpaper mode has changed
> > +     *
> > +     * @see vlc_player_vout_SetWallpaperModeEnabled()
> > +     *
> > +     * @param player unlocked player instance
> > +     * @param vout cf. vlc_player_vout_cbs note
> > +     * @param enabled true when wallpaper mode is enabled
> > +     * @param data opaque pointer set by vlc_player_vout_AddListener()
> > +     */
> > +    void (*on_wallpaper_mode_changed)(vlc_player_t *player,
> > +        vout_thread_t *vout, bool enabled, void *data);
> > +};
> > +
> > +/**
> > + * Player aout callbacks
> > + *
> > + * Can be registered with vlc_player_aout_AddListener().
> > + *
> > + * These callbacks are *not* called with the player locked. It is safe 
> > to lock
> > + * the player and call any vlc_player functions from these callbacks.
> > + *
> > + * @warning To avoid deadlocks, users should never call audio_output_t
> > + * functions from these callbacks.
> > + */
> > +struct vlc_player_aout_cbs
> > +{
> > +    /**
> > +     * Called when the volume has changed
> > +     *
> > +     * @see vlc_player_aout_SetVolume()
> > +     *
> > +     * @param player unlocked player instance
> > +     * @param new_volume volume in the range [0;8.f]
> > +     * @param data opaque pointer set by vlc_player_vout_AddListener()
> > +     */
> > +    void (*on_volume_changed)(vlc_player_t *player,
> > +        float new_volume, void *data);
> > +
> > +    /**
> > +     * Called when the mute state has changed
> > +     *
> > +     * @see vlc_player_aout_Mute()
> > +     *
> > +     * @param player unlocked player instance
> > +     * @param new_mute true if muted
> > +     * @param data opaque pointer set by vlc_player_vout_AddListener()
> > +     */
> > +    void (*on_mute_changed)(vlc_player_t *player,
> > +        bool new_muted, void *data);
> > +};
> > +
> > +/**
> > + * Duplicate a track
> > + *
> > + * This function can be used to pass a track from a callback to an 
> > other
> > + * thread, the es_id will be held by the duplicated track.
> > + *
> > + * @see vlc_player_cbs.on_track_list_changed
> > + *
> > + * @return a duplicated track or NULL on allocation error
> > + */
> > +VLC_API struct vlc_player_track *
> > +vlc_player_track_Dup(const struct vlc_player_track *track);
> > +
> > +/**
> > + * Delete a duplicated track
> > + */
> > +VLC_API void
> > +vlc_player_track_Delete(struct vlc_player_track *track);
> > +
> > +/**
> > + * Duplicate a program
> > + *
> > + * This function can be used to pass a program from a callback to an 
> > other
> > + * thread.
> > + *
> > + * @see vlc_player_cbs.on_program_list_changed
> > + *
> > + * @return a duplicated program or NULL on allocation error
> > + */
> > +VLC_API struct vlc_player_program *
> > +vlc_player_program_Dup(const struct vlc_player_program *prgm);
> > +
> > +/**
> > + * Delete a duplicated program
> > + */
> > +VLC_API void
> > +vlc_player_program_Delete(struct vlc_player_program *prgm);
> > +
> > +/**
> > + * Hold the title list of the player
> > + *
> > + * This function can be used to pass this title list from a callback to 
> > an
> > + * other thread.
> > + *
> > + * @see vlc_player_cbs.on_titles_changed
> > + *
> > + * @return the same instance
> > + */
> > +VLC_API vlc_player_title_list *
> > +vlc_player_title_list_Hold(vlc_player_title_list *titles);
> > +
> > +/**
> > + * Release of previously held title list
> > + */
> > +VLC_API void
> > +vlc_player_title_list_Release(vlc_player_title_list *titles);
> > +
> > +/**
> > + * Get the number of title of a list
> > + */
> > +VLC_API size_t
> > +vlc_player_title_list_GetCount(vlc_player_title_list *titles);
> > +
> > +/**
> > + * Get the title at a given index
> > + *
> > + * @param idx index in the range [0; count[
> > + * @return a valid title (can't be NULL)
> > + */
> > +VLC_API const struct vlc_player_title *
> > +vlc_player_title_list_GetAt(vlc_player_title_list *titles, size_t idx);
> > +
> > +/**
> > + * Create a new player instance
> > + *
> > + * @param parent parent VLC object
> > + * @param media_provider pointer to a media_provider structure or NULL, 
> > the
> > + * structure must be valid during the lifetime of the player
> > + * @param media_provider_data opaque data used by provider 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_media_provider *media_provider,
> > +               void *media_provider_data);
> > +
> > +/**
> > + * Delete a player instance
> > + *
> > + * This function stop any playback previously started and wait for 
> > their
> > + * termination.
> > + *
> > + * @warning Blocking function if the player state is not STOPPED, don't 
> > call it
> > + * from an UI thread in that case.
> > + *
> > + * @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);
> > +
> > +/**
> > + * Wait on a condition variable
> > + *
> > + * This call allow users to use their own condition with the player 
> > mutex.
> > + *
> > + * @param player locked player instance
> > + * @param cond external condition
> > + */
> > +VLC_API void
> > +vlc_player_CondWait(vlc_player_t *player, vlc_cond_t *cond);
> > +
> > +/**
> > + * Add a listener callback
> > + *
> > + * @note Every registered callbacks need to be removed by the caller 
> > with
> > + * vlc_player_RemoveListener().
> > + *
> > + * @param player locked player instance
> > + * @param cbs pointer to a vlc_player_cbs structure, the structure must 
> > be
> > + * valid during the lifetime of the player
> > + * @param cbs_data opaque pointer used by the callbacks
> > + * @return a valid listener id, or NULL in case of allocation error
> > + */
> > +VLC_API 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 listener_id listener id returned by vlc_player_AddListener()
> > + */
> > +VLC_API void
> > +vlc_player_RemoveListener(vlc_player_t *player,
> > +                          vlc_player_listener_id *listener_id);
> > +
> > +/**
> > + * Set the current media
> > + *
> > + * This function replaces the current and next medias.
> > + *
> > + * @note A successful call will always result of
> > + * vlc_player_cbs.on_current_media_changed being called. This function 
> > is not
> > + * blocking. If a media is currently being played, this media will be 
> > stopped
> > + * and the requested media will be set after.
> > + *
> > + * @warning This function is either synchronous (if the player state is
> > + * STOPPED) or asynchronous. In the later case, 
> > vlc_player_GetCurrentMedia()
> > + * will return the old media, even after this call, and until the
> > + * vlc_player_cbs.on_current_media_changed is called.
> > + *
> > + * @param player locked player instance
> > + * @param media new media to play
> > + * @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.
> > + *
> > + * @see vlc_player_cbs.on_current_media_changed
> > + *
> > + * @param player locked player instance
> > + * @return a valid media or NULL (if no media is set)
> > + */
> > +VLC_API input_item_t *
> > +vlc_player_GetCurrentMedia(vlc_player_t *player);
> > +
> > +/**
> > + * Helper that hold the current media
> > + */
> > +static inline input_item_t *
> > +vlc_player_HoldCurrentMedia(vlc_player_t *player)
> > +{
> > +    input_item_t *item = vlc_player_GetCurrentMedia(player);
> > +    return item ? input_item_Hold(item) : NULL;
> > +}
> > +
> > +/**
> > + * Invalidate the next media.
> > + *
> > + * This function can be used to invalidate the media returned by the
> > + * vlc_player_media_provider.get_next 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_media_provider.get_next callback to be called again.
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_InvalidateNextMedia(vlc_player_t *player);
> > +
> > +/**
> > + * Ask to started in a paused state
> > + *
> > + * This function can be used before vlc_player_Start()
> > + *
> > + * @param player locked player instance
> > + * @param start_paused true to start in a paused state
> > + */
> > +VLC_API void
> > +vlc_player_SetStartPaused(vlc_player_t *player, bool start_paused);
> > +
> 
> Can this be called with false to cancel a previous call? If not, is the 
> boolean parameter required?

Yes indeed, 

> 
> > +/**
> > + * Setup an action when a media is stopped
> > + *
> > + * @param player locked player instance
> > + * @param action action to do when a media is stopped
> > + */
> > +VLC_API void
> > +vlc_player_SetMediaStoppedAction(vlc_player_t *player,
> > +                                 enum vlc_player_media_stopped_action 
> > action);
> > +
> > +/**
> > + * Start the playback of the 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
> > + *
> > + * @note This function is asynchronous. Users should wait on
> > + * STOPPED state event to know when the stop is finished.
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Stop(vlc_player_t *player);
> > +
> > +/**
> > + * Pause the playback
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Pause(vlc_player_t *player);
> > +
> > +/**
> > + * Resume the playback from a pause
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_Resume(vlc_player_t *player);
> > +
> > +/**
> > + * Pause and display the next video frame
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_NextVideoFrame(vlc_player_t *player);
> > +
> > +/**
> > + * Get the state of the player
> > + *
> > + * @note Since all players actions are asynchronous, this function 
> > won't
> > + * reflect the new state immediately. Wait for the
> > + * vlc_players_cbs.on_state_changed event to be notified.
> > + *
> > + * @see vlc_player_state
> > + * @see vlc_player_cbs.on_state_changed
> > + *
> > + * @param player locked player instance
> > + * @return the current player state
> > + */
> > +VLC_API enum vlc_player_state
> > +vlc_player_GetState(vlc_player_t *player);
> > +
> > +/**
> > + * Get the error state of the player
> > + *
> > + * @see vlc_player_cbs.on_capabilities_changed
> > + *
> > + * @param player locked player instance
> > + * @return the current error state
> > + */
> > +VLC_API enum vlc_player_error
> > +vlc_player_GetError(vlc_player_t *player);
> > +
> > +/**
> > + * Helper to get the started state
> > + */
> > +static inline bool
> > +vlc_player_IsStarted(vlc_player_t *player)
> > +{
> > +    switch (vlc_player_GetState(player))
> > +    {
> > +        case VLC_PLAYER_STATE_STARTED:
> > +        case VLC_PLAYER_STATE_PLAYING:
> > +        case VLC_PLAYER_STATE_PAUSED:
> > +            return true;
> > +        default:
> > +            return false;
> > +    }
> > +}
> > +
> > +/**
> > + * Helper to get the paused state
> > + */
> > +static inline bool
> > +vlc_player_IsPaused(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetState(player) == VLC_PLAYER_STATE_PAUSED;
> > +}
> > +
> > +/**
> > + * Helper to toggle the pause state
> > + */
> > +static inline void
> > +vlc_player_TogglePause(vlc_player_t *player)
> > +{
> > +    if (vlc_player_IsStarted(player))
> > +    {
> > +        if (vlc_player_IsPaused(player))
> > +            vlc_player_Resume(player);
> > +        else
> > +            vlc_player_Pause(player);
> > +    }
> > +}
> > +
> > +/**
> > + * Get the player capabilities
> > + *
> > + * @see vlc_player_cbs.on_capabilities_changed
> > + *
> > + * @param player locked player instance
> > + * @return the player capabilities, a bitwise mask of @ref 
> > VLC_PLAYER_CAP_SEEK,
> > + * @ref VLC_PLAYER_CAP_PAUSE, @ref VLC_PLAYER_CAP_CHANGE_RATE, @ref
> > + * VLC_PLAYER_CAP_REWIND
> > + */
> > +VLC_API int
> > +vlc_player_GetCapabilities(vlc_player_t *player);
> > +
> > +/**
> > + * Helper to get the seek capability
> > + */
> > +static inline bool
> > +vlc_player_CanSeek(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetCapabilities(player) & VLC_PLAYER_CAP_SEEK;
> > +}
> > +
> > +/**
> > + * Helper to get the pause capability
> > + */
> > +static inline bool
> > +vlc_player_CanPause(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetCapabilities(player) & VLC_PLAYER_CAP_PAUSE;
> > +}
> > +
> > +/**
> > + * Helper to get the change-rate capability
> > + */
> > +static inline bool
> > +vlc_player_CanChangeRate(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetCapabilities(player) & 
> > VLC_PLAYER_CAP_CHANGE_RATE;
> > +}
> > +
> > +/**
> > + * Helper to get the rewindable capability
> > + */
> > +static inline bool
> > +vlc_player_CanRewind(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetCapabilities(player) & VLC_PLAYER_CAP_REWIND;
> > +}
> > +
> > +/**
> > + * Get the rate of the player
> > + *
> > + * @see vlc_player_cbs.on_rate_changed
> > + *
> > + * @param player locked player instance
> > + * @return rate of the player (< 1.f is slower, > 1.f is faster)
> > + */
> > +VLC_API float
> > +vlc_player_GetRate(vlc_player_t *player);
> > +
> > +/**
> > + * Change the rate of the player
> > + *
> > + * @note The rate is saved across several medias
> > + *
> > + * @param player locked player instance
> > + * @param rate new player (< 1.f is slower, > 1.f is faster)
> > + */
> > +VLC_API void
> > +vlc_player_ChangeRate(vlc_player_t *player, float rate);
> > +
> > +/**
> > + * Increment the rate of the player (faster)
> > + *
> > + * @param player locked player instance
> 
> The rate table should probably be part of the public header or mentioned 
> in the documentation no?

I don't know, I can make this function an helper...

> 
> > + */
> > +VLC_API void
> > +vlc_player_IncrementRate(vlc_player_t *player);
> > +
> > +/**
> > + * Decrement the rate of the player (Slower)
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API void
> > +vlc_player_DecrementRate(vlc_player_t *player);
> > +
> > +/**
> > + * Get the length of the current media
> > + *
> > + * @note A started and playing media doesn't have necessarily a valid 
> > length.
> > + *
> > + * @see vlc_player_cbs.on_length_changed
> > + *
> > + * @param player locked player instance
> > + * @return a valid length or VLC_TICK_INVALID (if no media is set,
> > + * playback is not yet started or in case of error)
> > + */
> > +VLC_API vlc_tick_t
> > +vlc_player_GetLength(vlc_player_t *player);
> > +
> > +/**
> > + * Get the time of the current media
> > + *
> > + * @note A started and playing media doesn't have necessarily a valid 
> > time.
> > + *
> > + * @see vlc_player_cbs.vlc_player_cbs.on_position_changed
> > + *
> > + * @param player locked player instance
> > + * @return a valid time or VLC_TICK_INVALID (if no media is set, the 
> > media
> > + * doesn't have any time, if playback is not yet started or in case of 
> > error)
> > + */
> > +VLC_API vlc_tick_t
> > +vlc_player_GetTime(vlc_player_t *player);
> > +
> > +/**
> > + * Get the position of the current media
> > + *
> > + * @see vlc_player_cbs.on_position_changed
> > + *
> > + * @param player locked player instance
> > + * @return a valid position in the range [0.f;1.f] or -1.f (if no media 
> > is
> > + * set,if playback is not yet started or in case of error)
> > + */
> > +VLC_API float
> > +vlc_player_GetPosition(vlc_player_t *player);
> > +
> > +/**
> > + * Seek the current media by position
> > + *
> > + * @note This function can be called before vlc_player_Start() in order 
> > to set
> > + * a starting position.
> > + *
> > + * @param player locked player instance
> > + * @param position position in the range [0.f;1.f]
> > + * @param speed precise of fast
> > + * @param whence absolute or relative
> > + */
> > +VLC_API void
> > +vlc_player_SeekByPos(vlc_player_t *player, float position,
> > +                     enum vlc_player_seek_speed speed,
> > +                     enum vlc_player_whence whence);
> > +
> > +/**
> > + * Seek the current media by time
> > + *
> > + * @note This function can be called before vlc_player_Start() in order 
> > to set
> > + * a starting position.
> > + *
> > + * @warning This function has an effect only if the media has a valid 
> > length.
> > + *
> > + * @param player locked player instance
> > + * @param time a time in the range [0;length]
> > + * @param speed precise of fast
> > + * @param whence absolute or relative
> > + */
> > +VLC_API void
> > +vlc_player_SeekByTime(vlc_player_t *player, vlc_tick_t time,
> > +                      enum vlc_player_seek_speed speed,
> > +                      enum vlc_player_whence whence);
> > +
> > +/**
> > + * Helper to set the absolute position precisely
> > + */
> > +static inline void
> > +vlc_player_SetPosition(vlc_player_t *player, float position)
> > +{
> > +    vlc_player_SeekByPos(player, position, VLC_PLAYER_SEEK_PRECISE,
> > +                         VLC_PLAYER_WHENCE_ABSOLUTE);
> > +}
> > +
> > +/**
> > + * Helper to set the absolute position fast
> > + */
> > +static inline void
> > +vlc_player_SetPositionFast(vlc_player_t *player, float position)
> > +{
> > +    vlc_player_SeekByPos(player, position, VLC_PLAYER_SEEK_FAST,
> > +                         VLC_PLAYER_WHENCE_ABSOLUTE);
> > +}
> > +
> > +/**
> > + * Helper to jump the position precisely
> > + */
> > +static inline void
> > +vlc_player_JumpPos(vlc_player_t *player, float 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_SeekByPos(player, jumppos, VLC_PLAYER_SEEK_PRECISE,
> > +                         VLC_PLAYER_WHENCE_RELATIVE);
> > +}
> > +
> > +/**
> > + * Helper to set the absolute time precisely
> > + */
> > +static inline void
> > +vlc_player_SetTime(vlc_player_t *player, vlc_tick_t time)
> > +{
> > +    vlc_player_SeekByTime(player, time, VLC_PLAYER_SEEK_PRECISE,
> > +                          VLC_PLAYER_WHENCE_ABSOLUTE);
> > +}
> > +
> > +/**
> > + * Helper to set the absolute time fast
> > + */
> > +static inline void
> > +vlc_player_SetTimeFast(vlc_player_t *player, vlc_tick_t time)
> > +{
> > +    vlc_player_SeekByTime(player, time, VLC_PLAYER_SEEK_FAST,
> > +                          VLC_PLAYER_WHENCE_ABSOLUTE);
> > +}
> > +
> > +/**
> > + * Helper to jump the time precisely
> > + */
> > +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_SeekByTime(player, jumptime, VLC_PLAYER_SEEK_PRECISE,
> > +                          VLC_PLAYER_WHENCE_RELATIVE);
> > +}
> > +
> > +/**
> > + * Enable A to B loop of the current media
> > + *
> > + * This function need to be called 2 times with VLC_PLAYER_ABLOOP_A and
> > + * VLC_PLAYER_ABLOOP_B to setup an A to B loop. It current the current
> > + * time/position when called. The B time must be higher than the A 
> > time.
> > + *
> > + * @param player locked player instance
> > + * @return VLC_SUCCESS or a VLC error code
> > + */
> > +VLC_API int
> > +vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop 
> > abloop);
> > +
> > +/**
> > + * Get the A to B loop status
> > + *
> > + * @note If the returned status is VLC_PLAYER_ABLOOP_A, then a_time and 
> > a_pos
> > + * will be valid. If the returned status is VLC_PLAYER_ABLOOP_B, then 
> > all
> > + * output parameters are valid. If the returned status is
> > + * VLC_PLAYER_ABLOOP_NONE, then all output parameters are invalid.
> > + *
> > + * @see vlc_player_cbs.on_atobloop_changed
> > + *
> > + * @param player locked player instance
> > + * @param a_time A time or VLC_TICK_INVALID (if the media doesn't have 
> > valid
> > + * times)
> > + * @param a_pos A position
> > + * @param b_time B time or VLC_TICK_INVALID (if the media doesn't have 
> > valid
> > + * times)
> > + * @param b_pos B position
> > + * @return A to B loop status
> > + */
> > +VLC_API enum vlc_player_abloop
> > +vlc_player_GetAtoBLoop(vlc_player_t *player, vlc_tick_t *a_time, float 
> > *a_pos,
> > +                       vlc_tick_t *b_time, float *b_pos);
> > +
> > +/**
> > + * Get the number of tracks for an ES category
> > + *
> > + * @warning The returned size becomes invalid when the player is 
> > unlocked.
> > + *
> > + * @param player locked player instance
> > + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
> > + * @return number of tracks, or 0 (in case of error, or if the media is 
> > not
> > + * started)
> > + */
> > +VLC_API size_t
> > +vlc_player_GetTrackCount(vlc_player_t *player, enum 
> > es_format_category_e cat);
> > +
> > +/**
> > + * Get the track at a specific index for an ES category
> > + *
> > + * @warning The behaviour is undefined if the index is not valid.
> > + *
> > + * @warning The returned pointer becomes invalid when the player is 
> > unlocked.
> > + * The referenced structure can be safely copied with 
> > vlc_player_track_Dup().
> > + *
> > + * @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 track (can't be NULL if vlc_player_GetTrackCount() 
> > returned
> > + * a valid count)
> > + */
> > +VLC_API const struct vlc_player_track *
> > +vlc_player_GetTrackAt(vlc_player_t *player, enum es_format_category_e 
> > cat,
> > +                      size_t index);
> > +
> > +/**
> > + * Helper to get the video track count
> > + */
> > +static inline size_t
> > +vlc_player_GetVideoTrackCount(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetTrackCount(player, VIDEO_ES);
> > +}
> > +
> > +/**
> > + * Helper to get a video track at a specific index
> > + */
> > +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);
> > +}
> > +
> > +/**
> > + * Helper to get the audio track count
> > + */
> > +static inline size_t
> > +vlc_player_GetAudioTrackCount(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetTrackCount(player, AUDIO_ES);
> > +}
> > +
> > +/**
> > + * Helper to get an audio track at a specific index
> > + */
> > +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);
> > +}
> > +
> > +/**
> > + * Helper to get the subtitle track count
> > + */
> > +static inline size_t
> > +vlc_player_GetSubtitleTrackCount(vlc_player_t *player)
> > +{
> > +    return vlc_player_GetTrackCount(player, SPU_ES);
> > +}
> > +
> > +/**
> > + * Helper to get a subtitle track at a specific index
> > + */
> > +static inline const struct vlc_player_track *
> > +vlc_player_GetSubtitleTrackAt(vlc_player_t *player, size_t index)
> > +{
> > +    return vlc_player_GetTrackAt(player, SPU_ES, index);
> > +}
> > +
> > +/**
> > + * Get a track from an ES identifier
> > + *
> > + * @warning The returned pointer becomes invalid when the player is 
> > unlocked.
> > + * The referenced structure can be safely copied with 
> > vlc_player_track_Dup().
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from 
> > vlc_player_cbs.on_track_list_changed or
> > + * 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 *es_id);
> > +
> > +/**
> > + * Select a track from an ES identifier
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_track_selection_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from 
> > vlc_player_cbs.on_track_list_changed or
> > + * vlc_player_GetTrackAt())
> > + */
> > +VLC_API void
> > +vlc_player_SelectTrack(vlc_player_t *player, vlc_es_id_t *es_id);
> > +
> > +/**
> > + * Unselect a track from an ES identifier
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_track_selection_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from 
> > vlc_player_cbs.on_track_list_changed or
> > + * vlc_player_GetTrackAt())
> > + */
> > +VLC_API void
> > +vlc_player_UnselectTrack(vlc_player_t *player, vlc_es_id_t *es_id);
> > +
> > +/**
> > + * Helper to unselect all tracks from an ES category
> > + */
> > +static inline void
> > +vlc_player_UnselectTrackCategory(vlc_player_t *player,
> > +                                 enum es_format_category_e cat)
> > +{
> > +    size_t count = vlc_player_GetTrackCount(player, cat);
> > +    for (size_t i = 0; i < count; ++i)
> > +    {
> > +        const struct vlc_player_track *track =
> > +            vlc_player_GetTrackAt(player, cat, i);
> > +        assert(track);
> > +        if (track->selected)
> > +            vlc_player_UnselectTrack(player, track->es_id);
> > +    }
> > +}
> > +
> > +/**
> > + * Restart a track from an ES identifier
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_track_selection_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param id an ES ID (retrieved from 
> > vlc_player_cbs.on_track_list_changed or
> > + * vlc_player_GetTrackAt())
> > + */
> > +VLC_API void
> > +vlc_player_RestartTrack(vlc_player_t *player, vlc_es_id_t *es_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.
> > + *
> > + * @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);
> > +
> > +/**
> > + * Helper to select the default video track
> > + */
> > +static inline void
> > +vlc_player_SelectDefaultVideoTrack(vlc_player_t *player, const char 
> > *lang)
> > +{
> > +    vlc_player_SelectDefaultTrack(player, VIDEO_ES, lang);
> > +}
> > +
> > +/**
> > + * Helper to select the default audio track
> > + */
> > +static inline void
> > +vlc_player_SelectDefaultAudioTrack(vlc_player_t *player, const char 
> > *lang)
> > +{
> > +    vlc_player_SelectDefaultTrack(player, AUDIO_ES, lang);
> > +}
> > +
> > +/**
> > + * Helper to select the default spu track
> > + */
> > +static inline void
> > +vlc_player_SelectDefaultSubtitleTrack(vlc_player_t *player, const char 
> > *lang)
> > +{
> > +    vlc_player_SelectDefaultTrack(player, SPU_ES, lang);
> > +}
> > +
> > +/**
> > + * Get the number of programs
> > + *
> > + * @warning The returned size becomes invalid when the player is 
> > unlocked.
> > + *
> > + * @param player locked player instance
> > + * @return number of programs, or 0 (in case of error, or if the media 
> > is not
> > + * started)
> > + */
> > +VLC_API size_t
> > +vlc_player_GetProgramCount(vlc_player_t *player);
> > +
> > +/**
> > + * Get the program at a specific index
> > + *
> > + * @warning The behaviour is undefined if the index is not valid.
> > + *
> > + * @warning The returned pointer becomes invalid when the player is 
> > unlocked.
> > + * The referenced structure can be safely copied with 
> > vlc_player_program_Dup().
> > + *
> > + * @param player locked player instance
> > + * @param index valid index in the range [0; count[
> > + * @return a valid program (can't be NULL if 
> > vlc_player_GetProgramCount()
> > + * returned a valid count)
> > + */
> > +VLC_API const struct vlc_player_program *
> > +vlc_player_GetProgramAt(vlc_player_t *player, size_t index);
> > +
> > +/**
> > + * Get a program from an ES group identifier
> > + *
> > + * @param player locked player instance
> > + * @param group_id a program ID (retrieved from
> > + * vlc_player_cbs.on_program_list_changed or 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 group_id);
> > +
> > +/**
> > + * Select a program from an ES group identifier
> > + *
> > + * @param player locked player instance
> > + * @param group_id a program ID (retrieved from
> > + * vlc_player_cbs.on_program_list_changed or vlc_player_GetProgramAt())
> > + */
> > +VLC_API void
> > +vlc_player_SelectProgram(vlc_player_t *player, int group_id);
> > +
> > +/**
> > + * Check if the media has a teletext menu
> > + *
> > + * @see vlc_player_cbs.on_teletext_menu_changed
> > + *
> > + * @param player locked player instance
> > + * @return true if the media has a teletext menu
> > + */
> > +VLC_API bool
> > +vlc_player_HasTeletextMenu(vlc_player_t *player);
> > +
> > +/**
> > + * Enable or disable teletext
> > + *
> > + * This function has an effect only if the player has a teletext menu.
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_teletext_enabled_changed event.
> > + * 
> > + * @param player locked player instance
> > + * @param enabled true to enable
> > + */
> > +VLC_API void
> > +vlc_player_SetTeletextEnabled(vlc_player_t *player, bool enabled);
> > +
> > +/**
> > + * Check if teletext is enabled
> > + *
> > + * @see vlc_player_cbs.on_teletext_enabled_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API bool
> > +vlc_player_IsTeletextEnabled(vlc_player_t *player);
> > +
> > +/**
> > + * Select a teletext page or do an action from a key
> > + *
> > + * This function has an effect only if the player has a teletext menu.
> > + *
> > + * @note Page keys can be the following: @ref 
> > VLC_PLAYER_TELETEXT_KEY_RED,
> > + * @ref VLC_PLAYER_TELETEXT_KEY_GREEN, @ref 
> > VLC_PLAYER_TELETEXT_KEY_YELLOW,
> > + * @ref VLC_PLAYER_TELETEXT_KEY_BLUE or @ref 
> > VLC_PLAYER_TELETEXT_KEY_INDEX.
> > +
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_teletext_page_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param page a page in the range ]0;888] or a valid key
> > + */
> > +VLC_API void
> > +vlc_player_SelectTeletextPage(vlc_player_t *player, unsigned page);
> > +
> > +/**
> > + * Get the current teletext page
> > + *
> > + * @see vlc_player_cbs.on_teletext_page_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API unsigned
> > +vlc_player_GetTeletextPage(vlc_player_t *player);
> > +
> > +/**
> > + * Enable or disable teletext transparency
> > + *
> > + * This function has an effect only if the player has a teletext menu.
> > +
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_teletext_transparency_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param enabled true to enable
> > + */
> > +VLC_API void
> > +vlc_player_SetTeletextTransparency(vlc_player_t *player, bool enabled);
> > +
> > +/**
> > + * Check if teletext is transparent
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API bool
> > +vlc_player_IsTeletextTransparent(vlc_player_t *player);
> > +
> > +/**
> > + * Get the title list of the current media
> > + *
> > + * @see vlc_player_cbs.on_titles_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API vlc_player_title_list *
> > +vlc_player_GetTitleList(vlc_player_t *player);
> > +
> > +/**
> > + * Get the selected title index for the current media
> > + *
> > + * @see vlc_player_cbs.on_title_selection_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API ssize_t
> > +vlc_player_GetSelectedTitleIdx(vlc_player_t *player);
> > +
> > +/**
> > + * Helper to get the current selected title
> > + */
> > +static inline const struct vlc_player_title *
> > +vlc_player_GetSelectedTitle(vlc_player_t *player)
> > +{
> > +    vlc_player_title_list *titles = vlc_player_GetTitleList(player);
> > +    if (!titles)
> > +        return NULL;
> > +    ssize_t selected_idx = vlc_player_GetSelectedTitleIdx(player);
> > +    if (selected_idx < 0)
> > +        return NULL;
> > +    return vlc_player_title_list_GetAt(titles, selected_idx);
> > +}
> > +
> > +/**
> > + * Select a title index for the current media
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_title_selection_changed event.
> > + *
> > + * @see vlc_player_title_list_GetAt()
> > + * @see vlc_player_title_list_GetCount()
> > + *
> > + * @param player locked player instance
> > + * @param index valid index in the range [0;count[
> > + */
> > +VLC_API void
> > +vlc_player_SelectTitleIdx(vlc_player_t *player, size_t index);
> > +
> > +/**
> > + * Select a title for the current media
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_title_selection_changed event.
> > + *
> > + * @see vlc_player_title_list_GetAt()
> > + * @see vlc_player_title_list_GetCount()
> > + *
> > + * @param player locked player instance
> > + * @param title a valid title coming from the vlc_player_title_list
> > + */
> > +VLC_API void
> > +vlc_player_SelectTitle(vlc_player_t *player,
> > +                       const struct vlc_player_title *title);
> > +
> > +/**
> > + * Select a chapter for the current media
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_chapter_selection_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param title the selected title
> > + * @param chapter_idx index from vlc_player_title.chapters
> > + */
> > +VLC_API void
> > +vlc_player_SelectChapter(vlc_player_t *player,
> > +                         const struct vlc_player_title *title,
> > +                         size_t chapter_idx);
> > +
> > +/**
> > + * Select the next title for the current media
> > + *
> > + * @see vlc_player_SelectTitleIdx()
> > + */
> > +VLC_API void
> > +vlc_player_SelectNextTitle(vlc_player_t *player);
> > +
> > +/**
> > + * Select the previous title for the current media
> > + *
> > + * @see vlc_player_SelectTitleIdx()
> > + */
> > +VLC_API void
> > +vlc_player_SelectPrevTitle(vlc_player_t *player);
> > +
> > +/**
> > + * Get the selected chapter index for the current media
> > + *
> > + * @see vlc_player_cbs.on_chapter_selection_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API ssize_t
> > +vlc_player_GetSelectedChapterIdx(vlc_player_t *player);
> > +
> > +/**
> > + * Helper to get the current selected chapter
> > + */
> > +static inline const struct vlc_player_chapter *
> > +vlc_player_GetSelectedChapter(vlc_player_t *player)
> > +{
> > +    const struct vlc_player_title *title = 
> > vlc_player_GetSelectedTitle(player);
> > +    if (!title || !title->chapter_count)
> > +        return NULL;
> > +    ssize_t chapter_idx = vlc_player_GetSelectedChapterIdx(player);
> > +    return chapter_idx >= 0 ? &title->chapters[chapter_idx] : NULL;
> > +}
> > +
> > +/**
> > + * Select a chapter index for the current media
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_chaper_selection_changed event.
> > + *
> > + * @see vlc_player_title.chapters
> > + *
> > + * @param player locked player instance
> > + * @param index valid index in the range 
> > [0;vlc_player_title.chapter_count[
> > + */
> > +VLC_API void
> > +vlc_player_SelectChapterIdx(vlc_player_t *player, size_t index);
> > +
> > +/**
> > + * Select the next chapter for the current media
> > + *
> > + * @see vlc_player_SelectChapterIdx()
> > + */
> > +VLC_API void
> > +vlc_player_SelectNextChapter(vlc_player_t *player);
> > +
> > +/**
> > + * Select the previous chapter for the current media
> > + *
> > + * @see vlc_player_SelectChapterIdx()
> > + */
> > +VLC_API void
> > +vlc_player_SelectPrevChapter(vlc_player_t *player);
> > +
> > +/**
> > + * Add an associated (or external) media to the current media
> > + *
> > + * @param player locked player instance
> > + * @param cat AUDIO_ES or SPU_ES
> > + * @param uri absolute uri of the external media
> > + * @param select true to select the track of this external media
> > + * @param notify true to notify the OSD
> > + * @param check_ext true to check subtitles extension
> > + */
> > +VLC_API int
> > +vlc_player_AddAssociatedMedia(vlc_player_t *player,
> > +                              enum es_format_category_e cat, const char 
> > *uri,
> > +                              bool select, bool notify, bool 
> > check_ext);
> > +
> > +/**
> > + * Set the associated subtitle FPS
> > + *
> > + * In order to correct the rate of the associated media according to 
> > this FPS
> > + * and the media video FPS.
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_associated_subs_fps_changed event.
> > + *
> > + * @warning this function will change the rate of all external subtitle 
> > files
> > + * associated with the current media.
> > + *
> > + * @param player locked player instance
> > + * @param fps FPS of the subtitle file
> > + */
> > +VLC_API void
> > +vlc_player_SetAssociatedSubsFPS(vlc_player_t *player, float fps);
> > +
> > +/**
> > + * Get the associated subtitle FPS
> > + *
> > + * @param player locked player instance
> > + * @return fps
> > + */
> > +VLC_API float
> > +vlc_player_GetAssociatedSubsFPS(vlc_player_t *player);
> > +
> > +/**
> > + * Set the renderer
> > + *
> > + * Valid for the current media and all future ones.
> > + *
> > + * @note A successful call will trigger the 
> > vlc_player_cbs.on_renderer_changed
> > + * event.
> > + *
> > + * @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);
> > +
> > +/**
> > + * Get the renderer
> > + *
> > + * @see vlc_player_cbs.on_renderer_changed
> > + *
> > + * @param player locked player instance
> > + * @return the renderer item set by vlc_player_SetRenderer()
> > + */
> > +VLC_API vlc_renderer_item_t *
> > +vlc_player_GetRenderer(vlc_player_t *player);
> > +
> > +/**
> > + * Navigate (for DVD/Bluray menus or viewpoint)
> > + *
> > + * @param player locked player instance
> > + * @param nav navigation key
> > + */
> > +VLC_API void
> > +vlc_player_Navigate(vlc_player_t *player, enum vlc_player_nav nav);
> > +
> > +/**
> > + * Check if the playing is recording
> > + *
> > + * @see vlc_player_cbs.on_recording_changed
> > + *
> > + * @param player locked player instance
> > + * @return true if the player is recording
> > + */
> > +VLC_API bool
> > +vlc_player_IsRecording(vlc_player_t *player);
> > +
> > +/**
> > + * Enable or disable recording for the current media
> > + *
> > + * @note A successful call will trigger the 
> > vlc_player_cbs.on_recording_changed
> > + * event.
> > + *
> > + * @param player locked player instance
> > + * @param enabled true to enable recording
> > + */
> > +VLC_API void
> > +vlc_player_SetRecordingEnabled(vlc_player_t *player, bool enabled);
> > +
> > +/**
> > + * Helper to toggle the recording state
> > + */
> > +static inline void
> > +vlc_player_ToggleRecording(vlc_player_t *player)
> > +{
> > +    vlc_player_SetRecordingEnabled(player, !
> > vlc_player_IsRecording(player));
> > +}
> > +
> > +/**
> > + * Get the audio delay for the current media
> > + *
> > + * @see vlc_player_cbs.on_audio_delay_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API vlc_tick_t
> > +vlc_player_GetAudioDelay(vlc_player_t *player);
> > +
> > +/**
> > + * Set the audio delay for the current media
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_audio_delay_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param delay a valid time
> > + * @param whence absolute or relative
> > + */
> > +VLC_API void
> > +vlc_player_SetAudioDelay(vlc_player_t *player, vlc_tick_t delay,
> > +                         enum vlc_player_whence whence);
> > +
> > +/**
> > + * Get the subtitle delay for the current media
> > + *
> > + * @see vlc_player_cbs.on_audio_delay_changed
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API vlc_tick_t
> > +vlc_player_GetSubtitleDelay(vlc_player_t *player);
> > +
> > +/**
> > + * Set the subtitle delay for the current media
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_cbs.on_subtitle_delay_changed event.
> > + *
> > + * @param player locked player instance
> > + * @param delay a valid time
> > + * @param whence absolute or relative
> > + */
> > +VLC_API void
> > +vlc_player_SetSubtitleDelay(vlc_player_t *player, vlc_tick_t delay,
> > +                            enum vlc_player_whence whence);
> > +
> > +/**
> > + * Get the signal quality and strength of the current media
> > + *
> > + * @param player locked player instance
> > + */
> > +VLC_API int
> > +vlc_player_GetSignal(vlc_player_t *player, float *quality, float 
> > *strength);
> > +
> > +/**
> > + * Get the statistics of the current media
> > + *
> > + * @warning The returned pointer becomes invalid when the player is 
> > unlocked.
> > + * The referenced structure can be safely copied.
> > + *
> > + * @see vlc_player_cbs.on_statistics_changed
> > + *
> > + * @return pointer to the player stats structure or NULL.
> > + * safely copied.
> > + */
> > +VLC_API const struct input_stats_t *
> > +vlc_player_GetStatistics(vlc_player_t *player);
> > +
> > +/**
> > + * Get the list of video output
> > + *
> > + * @warning All vout_thread_t * element of the array must be released 
> > with
> > + * vlc_object_release(). The returned must be freed.
> 
> returned array*

Yes,

> 
> > + *
> > + * @see vlc_players_cbs.on_vout_list_changed
> > + *
> > + * @param player locked player instance
> > + * @param count valid pointer to store the array count
> > + * @return a array of vout_thread_t * or NULL, cf. warning
> > + */
> > +VLC_API vout_thread_t **
> > +vlc_player_GetVouts(vlc_player_t *player, size_t *count);
> > +
> > +/**
> > + * Get the audio output
> > + *
> > + * @warning The returned pointer must be released with 
> > vlc_object_release().
> > + *
> > + * @param player locked player instance
> > + * @return a valid audio_output_t * or NULL
> > + */
> > +VLC_API audio_output_t *
> > +vlc_player_GetAout(vlc_player_t *player);
> > +
> > +/**
> > + * Add a listener callback for audio output events
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_aout_*()
> > + * functions.
> > + * @note Every registered callbacks need to be removed by the caller 
> > with
> > + * vlc_player_aout_RemoveListener().
> > + *
> > + * @param player player instance
> > + * @param cbs pointer to a vlc_player_aout_cbs structure, the structure 
> > must be
> > + * valid during the lifetime of the player
> > + * @param cbs_data opaque pointer used by the callbacks
> > + * @return a valid listener id, or NULL in case of allocation error
> > + */
> > +VLC_API vlc_player_aout_listener_id *
> > +vlc_player_aout_AddListener(vlc_player_t *player,
> > +                            const struct vlc_player_aout_cbs *cbs,
> > +                            void *cbs_data);
> > +
> > +/**
> > + * Remove a aout listener callback
> > + *
> > + * @param player player instance
> > + * @param listener_id listener id returned by 
> > vlc_player_aout_AddListener()
> > + */
> > +VLC_API void
> > +vlc_player_aout_RemoveListener(vlc_player_t *player,
> > +                               vlc_player_aout_listener_id 
> > *listener_id);
> > +
> > +/**
> > + * Get the audio volume
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_aout_*()
> > + * functions.
> > + *
> > + * @see vlc_player_aout_cbs.on_volume_changed
> > + *
> > + * @param player player instance
> > + * @return volume in the range [0;8.f] or -1.f if there is no audio 
> > outputs
> 
> I think it would be a good idea to mention that the returned value isn't 
> affected by the mute state

Hum, I don't think that it is necessary

> 
> > + */
> > +VLC_API float
> > +vlc_player_aout_GetVolume(vlc_player_t *player);
> > +
> > +/**
> > + * Set the audio volume
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_aout_*()
> > + * functions.
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_vout_cbs.on_volume_changed event.
> > + *
> > + * @param player player instance
> > + * @param volume volume in the range [0;8.f]
> > + * @return VLC_SUCCESS or VLC_EGENERIC if there is no audio outputs
> > + */
> > +VLC_API int
> > +vlc_player_aout_SetVolume(vlc_player_t *player, float volume);
> > +
> > +/**
> > + * Increment the audio volume
> > + *
> > + * @see vlc_player_aout_SetVolume()
> > + *
> > + * @param player player instance
> > + * @param volume volume in the range [0;8.f]
> > + * @param result pointer to store the resulting volume
> > + * @return VLC_SUCCESS or VLC_EGENERIC if there is no audio outputs
> > + */
> > +VLC_API int
> > +vlc_player_aout_IncrementVolume(vlc_player_t *player, float volume,
> > +                                float *result);
> > +
> > +/**
> > + * Helper to decrement the audio volume
> > + */
> > +static inline int
> > +vlc_player_aout_DecrementVolume(vlc_player_t *player, float volume,
> > +                                float *result)
> > +{
> > +    return vlc_player_aout_IncrementVolume(player, -volume, result);
> > +}
> > +
> > +/**
> > + * Check if the audio output is muted
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_aout_*()
> > + * functions.
> > + *
> > + * @see vlc_player_aout_cbs.on_mute_changed
> > + *
> > + * @param player player instance
> > + * @return 0 if not muted, 1 if mutex, -1 if there is no audio outputs
> 
> I think you mean 'muted' :D

Indeed,

> 
> > + */
> > +VLC_API int
> > +vlc_player_aout_IsMuted(vlc_player_t *player);
> > +
> > +/**
> > + * Mute or unmute the audio output
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_aout_*()
> > + * functions.
> > + *
> > + * @note A successful call will trigger the
> > + * vlc_player_aout_cbs.on_mute_changed event.
> > + *
> > + * @param player player instance
> > + * @param mute true to mute
> > + * @return VLC_SUCCESS or VLC_EGENERIC if there is no audio outputs
> > + */
> > +VLC_API int
> > +vlc_player_aout_Mute(vlc_player_t *player, bool mute);
> > +
> > +/**
> > + * Enable or disable an audio filter
> > + *
> > + * @see aout_EnableFilter()
> > + *
> > + * @return VLC_SUCCESS or VLC_EGENERIC if there is no audio outputs
> > + */
> > +VLC_API int
> > +vlc_player_aout_EnableFilter(vlc_player_t *player, const char *name, 
> > bool add);
> > +
> > +/**
> > + * Add a listener callback for video output events
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_vout_*()
> > + * functions.
> > + * @note Every registered callbacks need to be removed by the caller 
> > with
> > + * vlc_player_vout_RemoveListener().
> > + *
> > + * @param player player instance
> > + * @param cbs pointer to a vlc_player_vout_cbs structure, the structure 
> > must be
> > + * valid during the lifetime of the player
> > + * @param cbs_data opaque pointer used by the callbacks
> > + * @return a valid listener id, or NULL in case of allocation error
> > + */
> > +VLC_API vlc_player_vout_listener_id *
> > +vlc_player_vout_AddListener(vlc_player_t *player,
> > +                            const struct vlc_player_vout_cbs *cbs,
> > +                            void *cbs_data);
> > +
> > +/**
> > + * Remove a vout listener callback
> > + *
> > + * @param player player instance
> > + * @param listener_id listener id returned by 
> > vlc_player_vout_AddListener()
> > + */
> > +VLC_API void
> > +vlc_player_vout_RemoveListener(vlc_player_t *player,
> > +                               vlc_player_vout_listener_id 
> > *listener_id);
> > +
> > +/**
> > + * Check if the player is fullscreen
> > + *
> > + * @warning The fullscreen state of the player and all vouts can be 
> > different.
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_vout_*()
> > + * functions.
> > + *
> > + * @see vlc_player_vout_cbs.on_fullscreen_changed
> > + *
> > + * @param player player instance
> > + * @return true if the player is fullscreen
> > + */
> > +VLC_API bool
> > +vlc_player_vout_IsFullscreen(vlc_player_t *player);
> > +
> > +/**
> > + * Enable or disable the player fullscreen state
> > + *
> > + * This will have an effect on all current and future vouts.
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_vout_*()
> > + * functions.
> > + * @note A successful call will trigger the
> > + * vlc_player_vout_cbs.on_fullscreen_changed event.
> > + *
> > + * @param player player instance
> > + * @param enabled true to enable fullscreen
> > + */
> > +VLC_API void
> > +vlc_player_vout_SetFullscreen(vlc_player_t *player, bool enabled);
> > +
> > +/**
> > + * Helper to toggle the player fullscreen state
> > + */
> > +static inline void
> > +vlc_player_vout_ToggleFullscreen(vlc_player_t *player)
> > +{
> > +    vlc_player_vout_SetFullscreen(player,
> > +                                  !
> > vlc_player_vout_IsFullscreen(player));
> > +}
> > +
> > +/**
> > + * Check if the player has wallpaper-mode enaled
> > + *
> > + * @warning The wallpaper-mode state of the player and all vouts can be
> > + * different.
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_vout_*()
> > + * functions.
> > + *
> > + * @see vlc_player_vout_cbs.on_wallpaper_mode_changed
> > + *
> > + * @param player player instance
> > + * @return true if the player is fullscreen
> > + */
> > +VLC_API bool
> > +vlc_player_vout_IsWallpaperModeEnabled(vlc_player_t *player);
> > +
> > +/**
> > + * Enable or disable the player wallpaper-mode
> > + *
> > + * This will have an effect on all current and future vouts.
> > + *
> > + * @note The player instance doesn't need to be locked for 
> > vlc_player_vout_*()
> > + * functions.
> > + * @note A successful call will trigger the
> > + * vlc_player_vout_cbs.on_wallpaper_mode_changed event.
> > + *
> > + * @param player player instance
> > + * @param enabled true to enable wallpaper-mode
> > + */
> > +VLC_API void
> > +vlc_player_vout_SetWallpaperModeEnabled(vlc_player_t *player, bool 
> > enabled);
> > +
> > +/**
> > + * Helper to toggle the player wallpaper-mode state
> > + */
> > +static inline void
> > +vlc_player_vout_ToggleWallpaperMode(vlc_player_t *player)
> > +{
> > +    vlc_player_vout_SetWallpaperModeEnabled(player,
> > +        !vlc_player_vout_IsWallpaperModeEnabled(player));
> > +}
> > +
> > +/** @} */
> > +#endif
> > diff --git a/po/POTFILES.in b/po/POTFILES.in
> > index 504e3ffd4b..fdcd521891 100644
> > --- a/po/POTFILES.in
> > +++ b/po/POTFILES.in
> > @@ -79,6 +79,7 @@ src/input/input.c
> >  src/input/input_internal.h
> >  src/input/item.c
> >  src/input/meta.c
> > +src/input/player.c
> >  src/input/stream.c
> >  src/input/stream.h
> >  src/input/stream_memory.c
> > diff --git a/src/Makefile.am b/src/Makefile.am
> > index 4a28cdb837..7a6df52864 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,8 @@ libvlccore_la_SOURCES = \
> >  	input/es_out.c \
> >  	input/es_out_timeshift.c \
> >  	input/input.c \
> > +	input/player.c \
> > +	input/player.h \
> >  	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..215b4c37ac
> > --- /dev/null
> > +++ b/src/input/player.c
> > @@ -0,0 +1,2804 @@
> > +/
> > *****************************************************************************
> > + * 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 "player.h"
> > +#include <vlc_aout.h>
> > +#include <vlc_interface.h>
> > +#include <vlc_renderer_discovery.h>
> > +#include <vlc_list.h>
> > +#include <vlc_vector.h>
> > +#include <vlc_atomic.h>
> > +
> > +#include "libvlc.h"
> > +#include "input_internal.h"
> > +#include "resource.h"
> > +#include "../audio_output/aout_internal.h"
> > +
> > +#define RETRY_TIMEOUT_BASE VLC_TICK_FROM_MS(100)
> > +#define RETRY_TIMEOUT_MAX VLC_TICK_FROM_MS(3200)
> > +
> > +static_assert(VLC_PLAYER_CAP_SEEK == VLC_INPUT_CAPABILITIES_SEEKABLE &&
> > +              VLC_PLAYER_CAP_PAUSE == VLC_INPUT_CAPABILITIES_PAUSEABLE 
> > &&
> > +              VLC_PLAYER_CAP_CHANGE_RATE == 
> > VLC_INPUT_CAPABILITIES_CHANGE_RATE &&
> > +              VLC_PLAYER_CAP_REWIND == 
> > VLC_INPUT_CAPABILITIES_REWINDABLE,
> > +              "player/input capabilities mismatch");
> > +
> > +static_assert(VLC_PLAYER_TITLE_MENU == INPUT_TITLE_MENU &&
> > +              VLC_PLAYER_TITLE_INTERACTIVE == INPUT_TITLE_INTERACTIVE,
> > +              "player/input title flag mismatch");
> > +
> > +#define GAPLESS 0 /* TODO */
> > +
> > +typedef struct VLC_VECTOR(struct vlc_player_program *)
> > +    vlc_player_program_vector;
> > +
> > +typedef struct VLC_VECTOR(struct vlc_player_track *)
> > +    vlc_player_track_vector;
> > +
> > +struct vlc_player_listener_id
> > +{
> > +    const struct vlc_player_cbs *cbs;
> > +    void *cbs_data;
> > +    struct vlc_list node;
> > +};
> > +
> > +struct vlc_player_vout_listener_id
> > +{
> > +    const struct vlc_player_vout_cbs *cbs;
> > +    void *cbs_data;
> > +    struct vlc_list node;
> > +};
> > +
> > +struct vlc_player_aout_listener_id
> > +{
> > +    const struct vlc_player_aout_cbs *cbs;
> > +    void *cbs_data;
> > +    struct vlc_list node;
> > +};
> > +
> > +struct vlc_player_title_list
> > +{
> > +    vlc_atomic_rc_t rc;
> > +    size_t count;
> > +    struct vlc_player_title array[];
> > +};
> > +
> > +struct vlc_player_input
> > +{
> > +    input_thread_t *thread;
> > +    vlc_player_t *player;
> > +    bool started;
> > +
> > +    enum vlc_player_state state;
> > +    enum vlc_player_error error;
> > +    float rate;
> > +    int capabilities;
> > +    vlc_tick_t length;
> > +
> > +    vlc_tick_t position_ms;
> 
> Isn't the name confusing, since it's a number of ticks?

Yes, I'll rename

> 
> > +    float position_percent;
> > +
> > +    bool recording;
> > +
> > +    float signal_quality;
> > +    float signal_strength;
> > +    float cache;
> > +
> > +    struct input_stats_t stats;
> > +
> > +    vlc_tick_t audio_delay;
> > +    vlc_tick_t subtitle_delay;
> > +
> > +    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_player_track *teletext_menu;
> > +
> > +    struct vlc_player_title_list *titles;
> > +
> > +    size_t title_selected;
> > +    size_t chapter_selected;
> > +
> > +    struct vlc_list node;
> > +
> > +    bool teletext_enabled;
> > +    bool teletext_transparent;
> > +    unsigned teletext_page;
> > +
> > +    struct
> > +    {
> > +        vlc_tick_t time;
> > +        float pos;
> > +        bool set;
> > +    } abloop_state[2];
> > +};
> > +
> > +struct vlc_player_t
> > +{
> > +    struct vlc_common_members obj;
> > +    vlc_mutex_t lock;
> > +    vlc_mutex_t aout_listeners_lock;
> > +    vlc_mutex_t vout_listeners_lock;
> > +    vlc_cond_t start_delay_cond;
> > +
> > +    enum vlc_player_media_stopped_action media_stopped_action;
> > +    bool start_paused;
> > +
> > +    const struct vlc_player_media_provider *media_provider;
> > +    void *media_provider_data;
> > +
> > +    struct vlc_list listeners;
> > +    struct vlc_list aout_listeners;
> > +    struct vlc_list vout_listeners;
> > +
> > +    input_resource_t *resource;
> > +    vlc_renderer_item_t *renderer;
> > +
> > +    input_item_t *media;
> > +    struct vlc_player_input *input;
> > +
> > +    bool releasing_media;
> > +    bool has_next_media;
> 
> Can't this be infered from next_media?

Naup since you need to remember if the next media is NULL intentionally (End of every medias).

> 
> > +    input_item_t *next_media;
> > +#if GAPLESS
> > +    struct vlc_player_input *next_input;
> > +#endif
> > +
> > +    enum vlc_player_state global_state;
> > +    bool started;
> > +
> > +    unsigned error_count;
> > +
> > +    bool deleting;
> > +    struct
> > +    {
> > +        vlc_thread_t thread;
> > +        vlc_cond_t wait;
> > +        vlc_cond_t notify;
> > +        struct vlc_list inputs;
> > +        struct vlc_list stopping_inputs;
> > +        struct vlc_list joinable_inputs;
> > +    } destructor;
> > +};
> > +
> > +#define vlc_player_SendEvent(player, event, ...) do { \
> > +    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)
> > +
> > +#define vlc_player_aout_SendEvent(player, event, ...) do { \
> > +    vlc_mutex_lock(&player->aout_listeners_lock); \
> > +    vlc_player_aout_listener_id *listener; \
> > +    vlc_list_foreach(listener, &player->aout_listeners, node) \
> > +    { \
> > +        if (listener->cbs->event) \
> > +            listener->cbs->event(player, ##__VA_ARGS__, listener-
> > >cbs_data); \
> > +    } \
> > +    vlc_mutex_unlock(&player->aout_listeners_lock); \
> > +} while(0)
> > +
> > +#define vlc_player_vout_SendEvent(player, event, ...) do { \
> > +    vlc_mutex_lock(&player->vout_listeners_lock); \
> > +    vlc_player_vout_listener_id *listener; \
> > +    vlc_list_foreach(listener, &player->vout_listeners, node) \
> > +    { \
> > +        if (listener->cbs->event) \
> > +            listener->cbs->event(player, ##__VA_ARGS__, listener-
> > >cbs_data); \
> > +    } \
> > +    vlc_mutex_unlock(&player->vout_listeners_lock); \
> > +} 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 void
> > +vlc_player_input_HandleState(struct vlc_player_input *, enum 
> > vlc_player_state);
> > +static int
> > +vlc_player_VoutCallback(vlc_object_t *this, const char *var,
> > +                        vlc_value_t oldval, vlc_value_t newval, void 
> > *data);
> > +
> > +
> > +void
> > +vlc_player_assert_locked(vlc_player_t *player)
> > +{
> > +    assert(player);
> > +    vlc_mutex_assert(&player->lock);
> > +}
> > +
> > +static inline struct vlc_player_input *
> > +vlc_player_get_input_locked(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    return player->input;
> > +}
> > +
> > +static char *
> > +vlc_player_program_DupTitle(int id, const char *title)
> > +{
> > +    char *dup;
> > +    if (title)
> > +        dup = strdup(title);
> > +    else if (asprintf(&dup, "%d", id) == -1)
> > +        dup = NULL;
> > +    return dup;
> > +}
> > +
> > +static struct vlc_player_program *
> > +vlc_player_program_New(int id, const char *name)
> > +{
> > +    struct vlc_player_program *prgm = malloc(sizeof(*prgm));
> > +    if (!prgm)
> > +        return NULL;
> > +    prgm->name = vlc_player_program_DupTitle(id, name);
> > +    if (!prgm->name)
> > +    {
> > +        free(prgm);
> > +        return NULL;
> > +    }
> > +    prgm->group_id = id;
> > +    prgm->selected = prgm->scrambled = false;
> > +
> > +    return prgm;
> > +}
> > +
> > +static int
> > +vlc_player_program_Update(struct vlc_player_program *prgm, int id,
> > +                          const char *name)
> > +{
> > +    free((char *)prgm->name);
> > +    prgm->name = vlc_player_program_DupTitle(id, name);
> > +    return prgm->name != NULL ? VLC_SUCCESS : VLC_ENOMEM;
> > +}
> > +
> > +struct vlc_player_program *
> > +vlc_player_program_Dup(const struct vlc_player_program *src)
> > +{
> > +    struct vlc_player_program *dup =
> > +        vlc_player_program_New(src->group_id, src->name);
> > +
> > +    if (!dup)
> > +        return NULL;
> > +    dup->selected = src->selected;
> > +    dup->scrambled = src->scrambled;
> > +    return dup;
> > +}
> > +
> > +void
> > +vlc_player_program_Delete(struct vlc_player_program *prgm)
> > +{
> > +    free((char *)prgm->name);
> > +    free(prgm);
> > +}
> > +
> > +static struct vlc_player_program *
> > +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 *prgm = vec->data[i];
> > +        if (prgm->group_id == id)
> > +        {
> > +            if (idx)
> > +                *idx = i;
> > +            return prgm;
> > +        }
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static struct vlc_player_track *
> > +vlc_player_track_New(vlc_es_id_t *id, const char *name, const 
> > es_format_t *fmt)
> > +{
> > +    struct vlc_player_track *track = malloc(sizeof(*track));
> > +    if (!track)
> > +        return NULL;
> > +    track->name = strdup(name);
> > +    if (!track->name)
> > +    {
> > +        free(track);
> > +        return NULL;
> > +    }
> > +
> > +    int ret = es_format_Copy(&track->fmt, fmt);
> > +    if (ret != VLC_SUCCESS)
> > +    {
> > +        free((char *)track->name);
> > +        free(track);
> > +        return NULL;
> > +    }
> > +    track->es_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->es_id, src->name, &src->fmt);
> > +
> > +    if (!dup)
> > +        return NULL;
> > +    dup->selected = src->selected;
> > +    return dup;
> > +}
> > +
> > +void
> > +vlc_player_track_Delete(struct vlc_player_track *track)
> > +{
> > +    es_format_Clean(&track->fmt);
> > +    free((char *)track->name);
> > +    vlc_es_id_Release(track->es_id);
> > +    free(track);
> > +}
> > +
> > +static int
> > +vlc_player_track_Update(struct vlc_player_track *track,
> > +                        const char *name, const es_format_t *fmt)
> > +{
> > +    if (strcmp(name, track->name) != 0)
> > +    {
> > +        char *dup = strdup(name);
> > +        if (!dup)
> > +            return VLC_ENOMEM;
> > +        free((char *)track->name);
> > +        track->name = 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;
> > +}
> > +
> > +struct vlc_player_title_list *
> > +vlc_player_title_list_Hold(struct vlc_player_title_list *titles)
> > +{
> > +    vlc_atomic_rc_inc(&titles->rc);
> > +    return titles;
> > +}
> > +
> > +void
> > +vlc_player_title_list_Release(struct vlc_player_title_list *titles)
> > +{
> > +    if (!vlc_atomic_rc_dec(&titles->rc))
> > +        return;
> > +    for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
> > +    {
> > +        struct vlc_player_title *title = &titles->array[title_idx];
> > +        free((char *)title->name);
> > +        for (size_t chapter_idx = 0; chapter_idx < title-
> > >chapter_count;
> > +             ++chapter_idx)
> > +        {
> > +            const struct vlc_player_chapter *chapter =
> > +                &title->chapters[chapter_idx];
> > +            free((char *)chapter->name);
> > +        }
> > +        free((void *)title->chapters);
> > +    }
> > +    free(titles);
> > +}
> > +
> > +static char *
> > +input_title_GetName(const struct input_title_t *input_title, int idx,
> > +                    int title_offset)
> > +{
> > +    int ret;
> > +    char length_str[MSTRTIME_MAX_SIZE + sizeof(" []")];
> > +
> > +    if (input_title->i_length > 0)
> > +    {
> > +        strcpy(length_str, " [");
> > +        secstotimestr(&length_str[2], SEC_FROM_VLC_TICK(input_title-
> > >i_length));
> > +        strcat(length_str, "]");
> > +    }
> > +    else
> > +        length_str[0] = '\0';
> > +
> > +    char *dup;
> > +    if (input_title->psz_name && input_title->psz_name[0] != '\0')
> > +        ret = asprintf(&dup, "%s%s", input_title->psz_name, 
> > length_str);
> > +    else
> > +        ret = asprintf(&dup, _("Title %i%s"), idx + title_offset, 
> > length_str);
> > +    if (ret == -1)
> > +        return NULL;
> > +    return dup;
> 
> Wouldn't it be clearer to refactore this in 4 asprintf's?

I don't know, this part is copied from the input/var.c (the copied part will be removed when we stop using input_thread_t directly)

> 
> > +}
> > +
> > +static char *
> > +seekpoint_GetName(seekpoint_t *seekpoint, int idx, int chapter_offset)
> > +{
> > +    if (seekpoint->psz_name && seekpoint->psz_name[0] != '\0' )
> > +        return strdup(seekpoint->psz_name);
> > +
> > +    char *dup;
> > +    int ret = asprintf(&dup, _("Chapter %i"), idx + chapter_offset);
> > +    if (ret == -1)
> > +        return NULL;
> > +    return dup;
> > +}
> > +
> > +static struct vlc_player_title_list *
> > +vlc_player_title_list_Create(const input_title_t **array, size_t count,
> > +                             int title_offset, int chapter_offset)
> > +{
> > +    if (count == 0)
> > +        return NULL;
> > +
> > +    /* Allocate the struct + the whole list */
> > +    size_t size;
> > +    if (mul_overflow(count, sizeof(struct vlc_player_title), &size))
> > +        return NULL;
> > +    if (add_overflow(size, sizeof(struct vlc_player_title_list), 
> > &size))
> > +        return NULL;
> > +    struct vlc_player_title_list *titles = malloc(size);
> > +    if (!titles)
> > +        return NULL;
> > +
> > +    vlc_atomic_rc_init(&titles->rc);
> > +    titles->count = count;
> > +
> > +    for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
> > +    {
> > +        const struct input_title_t *input_title = array[title_idx];
> > +        struct vlc_player_title *title = &titles->array[title_idx];
> > +
> > +        title->name = input_title_GetName(input_title, title_idx, 
> > title_offset);
> > +        title->length = input_title->i_length;
> > +        title->flags = input_title->i_flags;
> > +        const size_t seekpoint_count = input_title->i_seekpoint > 0 ?
> > +                                       input_title->i_seekpoint : 0;
> > +        title->chapter_count = seekpoint_count;
> > +
> > +        struct vlc_player_chapter *chapters = title->chapter_count == 
> > 0 ? NULL :
> > +            vlc_alloc(title->chapter_count, sizeof(*chapters));
> > +
> > +        if (chapters)
> > +        {
> > +            for (size_t chapter_idx = 0; chapter_idx < title-
> > >chapter_count;
> > +                 ++chapter_idx)
> > +            {
> > +                struct vlc_player_chapter *chapter = 
> > &chapters[chapter_idx];
> > +                seekpoint_t *seekpoint = input_title-
> > >seekpoint[chapter_idx];
> > +
> > +                chapter->name = seekpoint_GetName(seekpoint, 
> > chapter_idx,
> > +                                                  chapter_offset);
> > +                chapter->time = seekpoint->i_time_offset;
> > +                if (!chapter->name) /* Will trigger the error path */
> > +                    title->chapter_count = chapter_idx;
> > +            }
> > +        }
> > +        else if (seekpoint_count > 0) /* Will trigger the error path */
> > +            title->chapter_count = 0;
> > +
> > +        title->chapters = chapters;
> > +
> > +        if (!title->name || seekpoint_count != title->chapter_count)
> > +        {
> > +            titles->count = title_idx;
> 
> That seems unrequired

Yes it is, I'll add the following comment:
/* Release titles up to title_idx */

> 
> > +            vlc_player_title_list_Release(titles);
> > +            return NULL;
> > +        }
> > +    }
> > +    return titles;
> > +}
> > +
> > +const struct vlc_player_title *
> > +vlc_player_title_list_GetAt(struct vlc_player_title_list *titles, 
> > size_t idx)
> > +{
> > +    assert(idx < titles->count);
> > +    return &titles->array[idx];
> > +}
> > +
> > +size_t
> > +vlc_player_title_list_GetCount(struct vlc_player_title_list *titles)
> > +{
> > +    return titles->count;
> > +}
> > +
> > +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 = VLC_PLAYER_STATE_STOPPED;
> > +    input->error = VLC_PLAYER_ERROR_NONE;
> > +    input->rate = 1.f;
> > +    input->capabilities = 0;
> > +    input->length = input->position_ms =
> > +    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));
> > +
> > +    input->audio_delay = input->subtitle_delay = 0;
> > +
> > +    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->teletext_menu = NULL;
> > +
> > +    input->titles = NULL;
> > +    input->title_selected = input->chapter_selected = 0;
> > +
> > +    input->teletext_enabled = input->teletext_transparent = false;
> > +    input->teletext_page = 0;
> > +
> > +    input->abloop_state[0].set = input->abloop_state[1].set = false;
> > +
> > +    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)
> > +{
> > +    vlc_player_t *player = input->player;
> > +
> > +    if (input->titles)
> > +    {
> > +        vlc_player_title_list_Release(input->titles);
> > +        vlc_player_SendEvent(player, on_titles_changed, NULL);
> > +    }
> > +
> > +    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);
> > +    assert(input->teletext_menu == NULL);
> > +
> > +    vlc_vector_destroy(&input->program_vector);
> > +    vlc_vector_destroy(&input->video_track_vector);
> > +    vlc_vector_destroy(&input->audio_track_vector);
> > +    vlc_vector_destroy(&input->spu_track_vector);
> > +
> > +    vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPED);
> > +    const bool started = player->started;
> > +    vlc_player_Unlock(player);
> > +
> > +    const bool keep_sout = var_GetBool(input->thread, "sout-keep");
> > +    input_Close(input->thread);
> > +    if (!keep_sout)
> > +        input_resource_TerminateSout(player->resource);
> > +
> > +    if (!started)
> > +        input_resource_TerminateVout(player->resource);
> > +
> > +    free(input);
> > +
> > +    vlc_player_Lock(player);
> 
> I highly doubt this is an error, but it could use a comment as it's 
> quite counter intuitive IMO
> In case it's because of the destructor thread's mainloop implementation, 
> I believe it should be locked back in there

All player functions except vlc_player_Delete() are called with the locked already held. This function is unlocking the mutex, so it should lock it back. I added a comment for that corner case.

> 
> > +}
> > +
> > +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_GetNextMedia(vlc_player_t *player)
> 
> Since this doesn't return the next media, I think something like 
> "PrepareNextMedia" would be more appropriate

Indeed,

> 
> > +{
> > +    vlc_player_assert_locked(player);
> > +
> > +    if (!player->media_provider 
> > +     || player->media_stopped_action != 
> > VLC_PLAYER_MEDIA_STOPPED_CONTINUE
> > +     || player->has_next_media)
> > +        return;
> > +
> > +    assert(player->next_media == NULL);
> > +    player->next_media =
> > +        player->media_provider->get_next(player, player-
> > >media_provider_data);
> > +    player->has_next_media = true;
> > +}
> > +
> > +static int
> > +vlc_player_OpenNextMedia(vlc_player_t *player)
> > +{
> > +    assert(player->input == NULL);
> > +
> > +    player->has_next_media = false;
> > +
> > +    int ret = VLC_SUCCESS;
> > +    if (player->releasing_media)
> > +    {
> > +        assert(player->media);
> > +        input_item_Release(player->media);
> > +        player->media = NULL;
> > +        player->releasing_media = false;
> > +    }
> > +    else
> > +    {
> > +        if (!player->next_media)
> > +            return VLC_EGENERIC;
> > +
> > +        if (player->media)
> > +            input_item_Release(player->media);
> > +        player->media = player->next_media;
> > +        player->next_media = NULL;
> > +
> > +        player->input = vlc_player_input_New(player, player->media);
> > +        if (!player->input)
> > +        {
> > +            input_item_Release(player->media);
> > +            player->media = NULL;
> > +            ret = VLC_ENOMEM;
> > +        }
> > +    }
> > +    vlc_player_SendEvent(player, on_current_media_changed, player-
> > >media);
> > +    return ret;
> > +}
> > +
> > +static void
> > +vlc_player_CancelWaitError(vlc_player_t *player)
> > +{
> > +    if (player->error_count != 0)
> > +    {
> > +        player->error_count = 0;
> > +        vlc_cond_signal(&player->start_delay_cond);
> > +    }
> > +}
> > +
> > +static bool
> > +vlc_list_HasInput(struct vlc_list *list, struct vlc_player_input 
> > *input)
> > +{
> > +    struct vlc_player_input *other_input;
> > +    vlc_list_foreach(other_input, list, node)
> > +    {
> > +        if (other_input == input)
> > +            return true;
> > +    }
> > +    return false;
> > +}
> > +
> > +static void
> > +vlc_player_destructor_AddInput(vlc_player_t *player,
> > +                               struct vlc_player_input *input)
> > +{
> > +    /* Add this input to the stop list: it will be stopped by the
> > +     * destructor thread */
> > +    input->started = false;
> > +    assert(!vlc_list_HasInput(&player->destructor.stopping_inputs, 
> > input));
> > +    assert(!vlc_list_HasInput(&player->destructor.joinable_inputs, 
> > input));
> > +
> > +    vlc_list_append(&input->node, &player->destructor.inputs);
> > +    vlc_cond_signal(&input->player->destructor.wait);
> > +}
> > +
> > +static void
> > +vlc_player_destructor_AddStoppingInput(vlc_player_t *player,
> > +                                       struct vlc_player_input *input)
> > +{
> > +    /* Add this input to the stopping list */
> > +    if (vlc_list_HasInput(&player->destructor.inputs, input))
> > +        vlc_list_remove(&input->node);
> > +    if (!vlc_list_HasInput(&player->destructor.stopping_inputs, input))
> > +    {
> > +        vlc_list_append(&input->node, &player-
> > >destructor.stopping_inputs);
> > +        vlc_cond_signal(&input->player->destructor.wait);
> > +    }
> > +}
> > +
> > +static void
> > +vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
> > +                                       struct vlc_player_input *input)
> > +{
> > +    /* Add this input to the joinable list: it will be deleted by the
> > +     * destructor thread */
> > +    assert(!vlc_list_HasInput(&player->destructor.inputs, input));
> > +    assert(!vlc_list_HasInput(&player->destructor.joinable_inputs, 
> > input));
> > +
> > +    assert(vlc_list_HasInput(&player->destructor.stopping_inputs, 
> > input));
> > +    vlc_list_remove(&input->node);
> > +
> > +    vlc_list_append(&input->node, &player->destructor.joinable_inputs);
> > +
> > +    vlc_cond_signal(&input->player->destructor.wait);
> > +}
> > +
> > +static bool vlc_player_destructor_IsEmpty(vlc_player_t *player)
> > +{
> > +    return vlc_list_is_empty(&player->destructor.inputs)
> > +        && vlc_list_is_empty(&player->destructor.stopping_inputs)
> > +        && vlc_list_is_empty(&player->destructor.joinable_inputs);
> > +}
> > +
> > +static void *
> > +vlc_player_destructor_Thread(void *data)
> > +{
> > +    vlc_player_t *player = data;
> > +
> > +    vlc_mutex_lock(&player->lock);
> 
> You probably should disable cancellation explicitly

Why ? This thread is never cancelled.

> 
> > +    while (!player->deleting
> > +        || !vlc_player_destructor_IsEmpty(player))
> 
> !player->deleting is the only relevant condition WRT stopping the thread no?

No, even when deleting, this thread will terminate only when all input are stopped and closed. (I'll add a comment)

> 
> > +    {
> > +        struct vlc_player_input *input = NULL;
> > +        vlc_cond_wait(&player->destructor.wait, &player->lock);
> > +
> > +        vlc_list_foreach(input, &player->destructor.inputs, node)
> > +        {
> > +            vlc_list_remove(&input->node);
> > +
> > +            vlc_player_input_HandleState(input, 
> > VLC_PLAYER_STATE_STOPPING);
> > +            input_Stop(input->thread);
> > +        }
> > +
> > +        vlc_list_foreach(input, &player->destructor.joinable_inputs, 
> > node)
> > +        {
> > +            vlc_list_remove(&input->node);
> > +            vlc_player_input_Delete(input);
> > +        }
> > +    }
> > +    vlc_mutex_unlock(&player->lock);
> > +    return NULL;
> > +}
> > +
> > +static bool
> > +vlc_player_WaitRetryDelay(vlc_player_t *player)
> > +{
> > +    if (player->error_count)
> > +    {
> > +        /* Delay the next opening in case of error to avoid busy loops 
> > */
> > +        vlc_tick_t delay = RETRY_TIMEOUT_BASE;
> > +        for (unsigned i = 1; i < player->error_count
> > +          && delay < RETRY_TIMEOUT_MAX; ++i)
> > +            delay *= 2; /* Wait 100, 200, 400, 800, 1600 and finally 
> > 3200ms */
> > +        delay += vlc_tick_now();
> > +
> > +        while (player->error_count > 0
> > +            && vlc_cond_timedwait(&player->start_delay_cond, &player-
> > >lock,
> > +                                  delay) == 0);
> > +        if (player->error_count == 0)
> > +            return false; /* canceled */
> > +    }
> > +    return true;
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleState(struct vlc_player_input *input,
> > +                             enum vlc_player_state state)
> > +{
> > +    vlc_player_t *player = input->player;
> > +
> > +    /* The STOPPING state can be set earlier by the player. In that 
> > case,
> > +     * ignore all future events except the STOPPED one */
> > +    if (input->state == VLC_PLAYER_STATE_STOPPING
> > +     && state != VLC_PLAYER_STATE_STOPPED)
> > +        return;
> > +
> > +    input->state = state;
> > +
> > +    /* Override the global state if the player is still playing and has 
> > a next
> > +     * media to play */
> > +    bool send_event = player->global_state != state;
> > +    switch (input->state)
> > +    {
> > +        case VLC_PLAYER_STATE_STOPPED:
> > +            assert(!input->started);
> > +            assert(input != player->input);
> > +
> > +            if (input->error != VLC_PLAYER_ERROR_NONE)
> > +                player->error_count++;
> > +            else
> > +                player->error_count = 0;
> > +
> > +            vlc_player_WaitRetryDelay(player);
> > +
> > +            if (!player->deleting)
> > +                vlc_player_OpenNextMedia(player);
> > +            if (!player->input)
> > +                player->started = false;
> > +
> > +            switch (player->media_stopped_action)
> > +            {
> > +                case VLC_PLAYER_MEDIA_STOPPED_EXIT:
> > +                    if (player->input && player->started)
> > +                        vlc_player_input_Start(player->input);
> > +                    else
> > +                        libvlc_Quit(player->obj.libvlc);
> > +                    break;
> > +                case VLC_PLAYER_MEDIA_STOPPED_CONTINUE:
> > +                    if (player->input && player->started)
> > +                        vlc_player_input_Start(player->input);
> > +                    break;
> > +                default:
> > +                    break;
> > +            }
> > +
> > +            send_event = !player->started;
> > +            break;
> > +        case VLC_PLAYER_STATE_STOPPING:
> > +            input->started = false;
> > +            if (input == player->input)
> > +                player->input = NULL;
> > +
> > +            if (player->started)
> 
> Can the player not be started if it's stopping?

No, here the input_thread_t is stopping but not necessarily the player.
Player and input share the same enum type but some not all events of the input are sent.

> 
> > +            {
> > +                vlc_player_GetNextMedia(player);
> > +                if (!player->next_media)
> > +                    player->started = false;
> > +            }
> > +            send_event = !player->started;
> > +            vlc_player_destructor_AddStoppingInput(player, input);
> > +            break;
> > +        case VLC_PLAYER_STATE_STARTED:
> > +        case VLC_PLAYER_STATE_PLAYING:
> > +            if (player->started &&
> > +                player->global_state == VLC_PLAYER_STATE_PLAYING)
> > +                send_event = false;
> > +            break;
> > +
> > +        case VLC_PLAYER_STATE_PAUSED:
> > +            assert(player->started && input->started);
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +
> > +    if (send_event)
> > +    {
> > +        player->global_state = input->state;
> > +        vlc_player_SendEvent(player, on_state_changed, player-
> > >global_state);
> > +    }
> > +}
> > +
> > +size_t
> > +vlc_player_GetProgramCount(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    return input ? input->program_vector.size : 0;
> > +}
> > +
> > +const struct vlc_player_program *
> > +vlc_player_GetProgramAt(vlc_player_t *player, size_t index)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return NULL;
> > +
> > +    assert(index < input->program_vector.size);
> > +    return input->program_vector.data[index];
> > +}
> > +
> > +const struct vlc_player_program *
> > +vlc_player_GetProgram(vlc_player_t *player, int id)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return NULL;
> > +
> > +    struct vlc_player_program *prgm =
> > +        vlc_player_program_vector_FindById(&input->program_vector, id, 
> > NULL);
> > +    return prgm;
> > +}
> > +
> > +void
> > +vlc_player_SelectProgram(vlc_player_t *player, int id)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (input)
> > +        input_ControlPushHelper(input->thread, 
> > INPUT_CONTROL_SET_PROGRAM,
> > +                                &(vlc_value_t) { .i_int = id });
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
> > +                                    const struct 
> > vlc_input_event_program *ev)
> > +{
> > +    vlc_player_t *player = input->player;
> > +    struct vlc_player_program *prgm;
> > +    vlc_player_program_vector *vec = &input->program_vector;
> > +
> > +    switch (ev->action)
> > +    {
> > +        case VLC_INPUT_PROGRAM_ADDED:
> > +            prgm = vlc_player_program_New(ev->id, ev->title);
> > +            if (!prgm)
> > +                break;
> > +
> > +            if (!vlc_vector_push(vec, prgm))
> > +            {
> > +                vlc_player_program_Delete(prgm);
> > +                break;
> > +            }
> > +            vlc_player_SendEvent(player, on_program_list_changed,
> > +                                 VLC_PLAYER_LIST_ADDED, prgm);
> > +            break;
> > +        case VLC_INPUT_PROGRAM_DELETED:
> > +        {
> > +            size_t idx;
> > +            prgm = vlc_player_program_vector_FindById(vec, ev->id, 
> > &idx);
> > +            if (prgm)
> > +            {
> > +                vlc_player_SendEvent(player, on_program_list_changed,
> > +                                     VLC_PLAYER_LIST_REMOVED, prgm);
> > +                vlc_vector_remove(vec, idx);
> > +                vlc_player_program_Delete(prgm);
> > +            }
> > +            break;
> > +        }
> > +        case VLC_INPUT_PROGRAM_UPDATED:
> > +        case VLC_INPUT_PROGRAM_SCRAMBLED:
> > +            prgm = vlc_player_program_vector_FindById(vec, ev->id, 
> > NULL);
> > +            if (!prgm)
> > +                break;
> > +            if (ev->action == VLC_INPUT_PROGRAM_UPDATED)
> > +            {
> > +                if (vlc_player_program_Update(prgm, ev->id, ev-
> > >title) != 0)
> > +                    break;
> > +            }
> > +            else
> > +                prgm->scrambled = ev->scrambled;
> > +            vlc_player_SendEvent(player, on_program_list_changed,
> > +                                 VLC_PLAYER_LIST_UPDATED, prgm);
> > +            break;
> > +        case VLC_INPUT_PROGRAM_SELECTED:
> > +        {
> > +            int unselected_id = -1, selected_id = -1;
> > +            vlc_vector_foreach(prgm, vec)
> > +            {
> > +                if (prgm->group_id == ev->id)
> > +                {
> > +                    if (!prgm->selected)
> > +                    {
> > +                        assert(selected_id == -1);
> > +                        prgm->selected = true;
> > +                        selected_id = prgm->group_id;
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    if (prgm->selected)
> > +                    {
> > +                        assert(unselected_id == -1);
> > +                        prgm->selected = false;
> > +                        unselected_id = prgm->group_id;
> > +                    }
> > +                }
> > +            }
> > +            if (unselected_id != -1 || selected_id != -1)
> > +                vlc_player_SendEvent(player, 
> > on_program_selection_changed,
> > +                                     unselected_id, selected_id);
> > +            break;
> > +        }
> > +        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->es_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)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return 0;
> > +    vlc_player_track_vector *vec = 
> > vlc_player_input_GetTrackVector(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)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return NULL;
> > +    vlc_player_track_vector *vec = 
> > vlc_player_input_GetTrackVector(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)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return NULL;
> > +    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)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (input)
> > +        input_ControlPushEsHelper(input->thread, INPUT_CONTROL_SET_ES, 
> > id);
> > +}
> > +
> > +void
> > +vlc_player_UnselectTrack(vlc_player_t *player, vlc_es_id_t *id)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (input)
> > +        input_ControlPushEsHelper(input->thread, 
> > INPUT_CONTROL_UNSET_ES, id);
> > +}
> > +
> > +void
> > +vlc_player_RestartTrack(vlc_player_t *player, vlc_es_id_t *id)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (input)
> > +        input_ControlPushEsHelper(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 void
> > +vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input,
> > +                                    const struct vlc_input_event_es 
> > *ev)
> > +{
> > +    vlc_player_t *player = input->player;
> > +    switch (ev->action)
> > +    {
> > +        case VLC_INPUT_ES_ADDED:
> > +            if (input->teletext_menu)
> > +            {
> > +                msg_Warn(player, "Can't handle more than one teletext 
> > menu "
> > +                         "track. Using the last one.");
> > +                vlc_player_track_Delete(input->teletext_menu);
> > +            }
> > +            input->teletext_menu = vlc_player_track_New(ev->id, ev-
> > >title,
> > +                                                        ev->fmt);
> > +            if (!input->teletext_menu)
> > +                return;
> > +
> > +            vlc_player_SendEvent(player, on_teletext_menu_changed, 
> > true);
> > +            break;
> > +        case VLC_INPUT_ES_DELETED:
> > +        {
> > +            if (input->teletext_menu && input->teletext_menu->es_id == 
> > ev->id)
> > +            {
> > +                assert(!input->teletext_enabled);
> > +
> > +                vlc_player_track_Delete(input->teletext_menu);
> > +                input->teletext_menu = NULL;
> > +                vlc_player_SendEvent(player, on_teletext_menu_changed, 
> > false);
> > +            }
> > +            break;
> > +        }
> > +        case VLC_INPUT_ES_UPDATED:
> > +            break;
> > +        case VLC_INPUT_ES_SELECTED:
> > +        case VLC_INPUT_ES_UNSELECTED:
> > +            if (input->teletext_menu->es_id == ev->id)
> > +            {
> > +                input->teletext_enabled = ev->action == 
> > VLC_INPUT_ES_SELECTED;
> > +                vlc_player_SendEvent(player, 
> > on_teletext_enabled_changed,
> > +                                     input->teletext_enabled);
> > +            }
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +}
> > +
> > +void
> > +vlc_player_SetTeletextEnabled(vlc_player_t *player, bool enabled)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (!input || !input->teletext_menu)
> > +        return;
> > +    if (enabled)
> > +        vlc_player_SelectTrack(player, input->teletext_menu->es_id);
> > +    else
> > +        vlc_player_UnselectTrack(player, input->teletext_menu->es_id);
> > +}
> > +
> > +void
> > +vlc_player_SelectTeletextPage(vlc_player_t *player, unsigned page)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (!input || !input->teletext_menu)
> > +        return;
> > +
> > +    input_ControlPush(input->thread, INPUT_CONTROL_SET_VBI_PAGE,
> > +        &(input_control_param_t) {
> > +            .vbi_page.id = input->teletext_menu->es_id,
> > +            .vbi_page.page = page,
> > +    });
> > +}
> > +
> > +void
> > +vlc_player_SetTeletextTransparency(vlc_player_t *player, bool enabled)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (!input || !input->teletext_menu)
> > +        return;
> > +
> > +    input_ControlPush(input->thread, 
> > INPUT_CONTROL_SET_VBI_TRANSPARENCY,
> > +        &(input_control_param_t) {
> > +            .vbi_transparency.id = input->teletext_menu->es_id,
> > +            .vbi_transparency.enabled = enabled,
> > +    });
> > +}
> > +
> > +bool
> > +vlc_player_HasTeletextMenu(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input && input->teletext_menu;
> > +}
> > +
> > +bool
> > +vlc_player_IsTeletextEnabled(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input && input->teletext_enabled)
> > +    {
> > +        assert(input->teletext_menu);
> > +        return true;
> > +    }
> > +    return false;
> > +}
> > +
> > +unsigned
> > +vlc_player_GetTeletextPage(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return vlc_player_IsTeletextEnabled(player) ? input-
> > >teletext_page : 0;
> > +}
> > +
> > +bool
> > +vlc_player_IsTeletextTransparent(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return vlc_player_IsTeletextEnabled(player) && input-
> > >teletext_transparent;
> > +}
> > +
> > +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);
> > +
> > +    if (ev->fmt->i_cat == SPU_ES && ev->fmt->i_codec == 
> > VLC_CODEC_TELETEXT
> > +     && (ev->fmt->subs.teletext.i_magazine == 1
> > +      || ev->fmt->subs.teletext.i_magazine == -1))
> > +    {
> > +        vlc_player_input_HandleTeletextMenu(input, ev);
> > +        return;
> > +    }
> > +
> > +    vlc_player_track_vector *vec =
> > +        vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
> > +    if (!vec)
> > +        return; /* UNKNOWN_ES or DATA_ES not handled */
> > +
> > +    vlc_player_t *player = input->player;
> > +    struct vlc_player_track *track;
> > +    switch (ev->action)
> > +    {
> > +        case VLC_INPUT_ES_ADDED:
> > +            track = vlc_player_track_New(ev->id, ev->title, ev->fmt);
> > +            if (!track)
> > +                break;
> > +
> > +            if (!vlc_vector_push(vec, track))
> > +            {
> > +                vlc_player_track_Delete(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)
> 
> maybe assert?

No, the input_thread can send the player ES that are not handled (like DATA_ES), in that case, it's normal to don't find the track.

> 
> > +            {
> > +                vlc_player_SendEvent(player, on_track_list_changed,
> > +                                     VLC_PLAYER_LIST_REMOVED, track);
> > +                vlc_vector_remove(vec, idx);
> > +                vlc_player_track_Delete(track);
> > +            }
> > +            break;
> > +        }
> > +        case VLC_INPUT_ES_UPDATED:
> > +            track = vlc_player_track_vector_FindById(vec, ev->id, 
> > NULL);
> > +            if (!track)
> > +                break;
> > +            if (vlc_player_track_Update(track, ev->title, ev->fmt) != 
> > 0)
> > +                break;
> > +            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->es_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->es_id, NULL);
> > +            }
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
> > +                                  const struct vlc_input_event_title 
> > *ev)
> > +{
> > +    vlc_player_t *player = input->player;
> > +    switch (ev->action)
> > +    {
> > +        case VLC_INPUT_TITLE_NEW_LIST:
> > +        {
> > +            input_thread_private_t *input_th = input_priv(input-
> > >thread);
> > +            const int title_offset = input_th->i_title_offset;
> > +            const int chapter_offset = input_th->i_seekpoint_offset;
> > +
> > +            if (input->titles)
> > +                vlc_player_title_list_Release(input->titles);
> > +            input->title_selected = input->chapter_selected = 0;
> > +            input->titles =
> > +                vlc_player_title_list_Create(ev->list.array, ev-
> > >list.count,
> > +                                             title_offset, 
> > chapter_offset);
> > +            vlc_player_SendEvent(player, on_titles_changed, input-
> > >titles);
> > +            if (input->titles)
> > +                vlc_player_SendEvent(player, 
> > on_title_selection_changed,
> > +                                     &input->titles->array[0], 0);
> > +            break;
> > +        }
> > +        case VLC_INPUT_TITLE_SELECTED:
> > +            if (!input->titles)
> > +                return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed 
> > */
> > +            assert(ev->selected_idx < input->titles->count);
> > +            input->title_selected = ev->selected_idx;
> > +            vlc_player_SendEvent(player, on_title_selection_changed,
> > +                                 &input->titles->array[input-
> > >title_selected],
> > +                                 input->title_selected);
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleChapterEvent(struct vlc_player_input *input,
> > +                                    const struct 
> > vlc_input_event_chapter *ev)
> > +{
> > +    vlc_player_t *player = input->player;
> > +    if (!input->titles || ev->title < 0 || ev->seekpoint < 0)
> > +        return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
> > +
> > +    assert((size_t)ev->title < input->titles->count);
> > +    const struct vlc_player_title *title = &input->titles->array[ev-
> > >title];
> > +    if (!title->chapter_count)
> > +        return;
> > +
> > +    assert(ev->seekpoint < (int)title->chapter_count);
> > +    input->title_selected = ev->title;
> > +    input->chapter_selected = ev->seekpoint;
> > +
> > +    const struct vlc_player_chapter *chapter = &title->chapters[ev-
> > >seekpoint];
> > +    vlc_player_SendEvent(player, on_chapter_selection_changed, title, 
> > ev->title,
> > +                         chapter, ev->seekpoint);
> > +}
> > +
> > +struct vlc_player_title_list *
> > +vlc_player_GetTitleList(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input ? input->titles : NULL;
> > +}
> > +
> > +ssize_t
> > +vlc_player_GetSelectedTitleIdx(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return -1;
> > +    return input->title_selected;
> > +}
> > +
> > +static ssize_t
> > +vlc_player_GetTitleIdx(vlc_player_t *player,
> > +                       const struct vlc_player_title *title)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input && input->titles)
> > +        for (size_t i = 0; i < input->titles->count; ++i)
> > +            if (&input->titles->array[i] == title)
> > +                return i;
> > +    return -1;
> > +}
> > +
> > +void
> > +vlc_player_SelectTitleIdx(vlc_player_t *player, size_t index)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_TITLE,
> > +                                &(vlc_value_t){ .i_int = index });
> > +}
> > +
> > +void
> > +vlc_player_SelectTitle(vlc_player_t *player,
> > +                       const struct vlc_player_title *title)
> > +{
> > +    ssize_t idx = vlc_player_GetTitleIdx(player, title);
> > +    if (idx != -1)
> > +        vlc_player_SelectTitleIdx(player, idx);
> > +}
> > +
> > +void
> > +vlc_player_SelectChapter(vlc_player_t *player,
> > +                         const struct vlc_player_title *title,
> > +                         size_t chapter_idx)
> > +{
> > +    ssize_t idx = vlc_player_GetTitleIdx(player, title);
> > +    if (idx != -1 && idx == vlc_player_GetSelectedTitleIdx(player))
> > +        vlc_player_SelectChapterIdx(player, chapter_idx);
> > +}
> > +
> > +void
> > +vlc_player_SelectNextTitle(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPush(input->thread, INPUT_CONTROL_SET_TITLE_NEXT, 
> > NULL);
> > +}
> > +
> > +void
> > +vlc_player_SelectPrevTitle(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPush(input->thread, INPUT_CONTROL_SET_TITLE_PREV, 
> > NULL);
> > +}
> > +
> > +ssize_t
> > +vlc_player_GetSelectedChapterIdx(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return -1;
> > +    return input->chapter_selected;
> > +}
> > +
> > +void
> > +vlc_player_SelectChapterIdx(vlc_player_t *player, size_t index)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPushHelper(input->thread, 
> > INPUT_CONTROL_SET_SEEKPOINT,
> > +                                &(vlc_value_t){ .i_int = index });
> > +}
> > +
> > +void
> > +vlc_player_SelectNextChapter(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPush(input->thread, 
> > INPUT_CONTROL_SET_SEEKPOINT_NEXT, NULL);
> > +}
> > +
> > +void
> > +vlc_player_SelectPrevChapter(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPush(input->thread, 
> > INPUT_CONTROL_SET_SEEKPOINT_PREV, NULL);
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
> > +                                 const struct vlc_input_event_vout *ev)
> > +{
> > +    assert(ev->vout);
> > +
> > +    vlc_player_t *player = input->player;
> > +    enum vlc_player_list_action action;
> > +    switch (ev->action)
> > +    {
> > +        case VLC_INPUT_EVENT_VOUT_ADDED:
> > +            action = VLC_PLAYER_LIST_ADDED;
> > +            break;
> > +        case VLC_INPUT_EVENT_VOUT_DELETED:
> > +            action = VLC_PLAYER_LIST_REMOVED;
> > +            /* Un-register vout callbacks before the vout list event */
> > +            var_DelCallback(ev->vout, "fullscreen",
> > +                            vlc_player_VoutCallback, player);
> > +            var_DelCallback(ev->vout, "video-wallpaper",
> > +                            vlc_player_VoutCallback, player);
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +    vlc_player_SendEvent(player, on_vout_list_changed, action, ev-
> > >vout);
> > +
> > +    /* Register vout callbacks after the vout list event */
> > +    if (ev->action == VLC_INPUT_EVENT_VOUT_ADDED)
> > +    {
> > +        var_AddCallback(ev->vout, "fullscreen",
> > +                        vlc_player_VoutCallback, player);
> > +        var_AddCallback(ev->vout, "video-wallpaper",
> > +                        vlc_player_VoutCallback, player);
> > +    }
> > +}
> > +
> > +static void
> > +vlc_player_input_HandleStateEvent(struct vlc_player_input *input,
> > +                                  input_state_e state)
> > +{
> > +    switch (state)
> > +    {
> > +        case OPENING_S:
> > +            vlc_player_input_HandleState(input, 
> > VLC_PLAYER_STATE_STARTED);
> > +            break;
> > +        case PLAYING_S:
> > +            vlc_player_input_HandleState(input, 
> > VLC_PLAYER_STATE_PLAYING);
> > +            break;
> > +        case PAUSE_S:
> > +            vlc_player_input_HandleState(input, 
> > VLC_PLAYER_STATE_PAUSED);
> > +            break;
> > +        case END_S:
> > +            vlc_player_input_HandleState(input, 
> > VLC_PLAYER_STATE_STOPPING);
> > +            break;
> > +        case ERROR_S:
> > +            /* Contrary to the input_thead_t, an error is not a state 
> > */
> > +            input->error = VLC_PLAYER_ERROR_GENERIC;
> > +            vlc_player_SendEvent(input->player, on_error_changed, 
> > input->error);
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +}
> > +
> > +static void
> > +vlc_player_HandleAtoBLoop(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    assert(input);
> > +    assert(input->abloop_state[0].set && input->abloop_state[1].set);
> > +
> > +    if (input->position_ms != VLC_TICK_INVALID
> > +     && input->abloop_state[0].time != VLC_TICK_INVALID
> > +     && input->abloop_state[1].time != VLC_TICK_INVALID)
> > +    {
> > +        if (input->position_ms >= input->abloop_state[1].time)
> > +            vlc_player_SetTime(player, input->abloop_state[0].time);
> > +    }
> > +    else if (input->position_percent >= input->abloop_state[1].pos)
> > +        vlc_player_SetPosition(player, input->abloop_state[0].pos);
> > +}
> > +
> > +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);
> > +
> > +    switch (event->type)
> > +    {
> > +        case INPUT_EVENT_STATE:
> > +            vlc_player_input_HandleStateEvent(input, event->state);
> > +            break;
> > +        case INPUT_EVENT_RATE:
> > +            input->rate = event->rate;
> > +            vlc_player_SendEvent(player, on_rate_changed, input->rate);
> > +            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_OpenNextMedia(player);
> > +#endif
> > +            if (input->position_ms != event->position.ms ||
> > +                input->position_percent != event->position.percentage)
> > +            {
> > +                input->position_ms = event->position.ms;
> > +                input->position_percent = event->position.percentage;
> > +                vlc_player_SendEvent(player, on_position_changed,
> > +                                     input->position_ms,
> > +                                     input->position_percent);
> > +
> > +                if (input->abloop_state[0].set && input-
> > >abloop_state[1].set
> > +                 && input == player->input)
> > +                    vlc_player_HandleAtoBLoop(player);
> > +            }
> > +            break;
> > +        case INPUT_EVENT_LENGTH:
> > +            if (input->length != 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:
> > +            vlc_player_input_HandleTitleEvent(input, &event->title);
> > +            break;
> > +        case INPUT_EVENT_CHAPTER:
> > +            vlc_player_input_HandleChapterEvent(input, &event-
> > >chapter);
> > +            break;
> > +        case INPUT_EVENT_RECORD:
> > +            input->recording = event->record;
> > +            vlc_player_SendEvent(player, on_recording_changed, input-
> > >recording);
> > +            break;
> > +        case INPUT_EVENT_STATISTICS:
> > +            input->stats = *event->stats;
> > +            vlc_player_SendEvent(player, on_statistics_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:
> > +            input->audio_delay = event->audio_delay;
> > +            vlc_player_SendEvent(player, on_audio_delay_changed,
> > +                                 input->audio_delay);
> > +            break;
> > +        case INPUT_EVENT_SUBTITLE_DELAY:
> > +            input->subtitle_delay = event->subtitle_delay;
> > +            vlc_player_SendEvent(player, on_subtitle_delay_changed,
> > +                                 input->subtitle_delay);
> > +            break;
> > +        case INPUT_EVENT_CACHE:
> > +            input->cache = event->cache;
> > +            vlc_player_SendEvent(player, on_buffering_changed, event-
> > >cache);
> > +            break;
> > +        case INPUT_EVENT_VOUT:
> > +            vlc_player_input_HandleVoutEvent(input, &event->vout);
> > +            break;
> > +        case INPUT_EVENT_ITEM_META:
> > +            vlc_player_SendEvent(player, on_media_meta_changed,
> > +                                 input_GetItem(input->thread));
> > +            break;
> > +        case INPUT_EVENT_ITEM_EPG:
> > +            vlc_player_SendEvent(player, on_media_epg_changed,
> > +                                 input_GetItem(input->thread));
> > +            break;
> > +        case INPUT_EVENT_SUBITEMS:
> > +            vlc_player_SendEvent(player, on_media_subitems_changed,
> > +                                 input_GetItem(input->thread), event-
> > >subitems);
> > +            break;
> > +        case INPUT_EVENT_DEAD:
> > +            assert(!input->started);
> > +            vlc_player_destructor_AddJoinableInput(player, input);
> > +            break;
> > +        case INPUT_EVENT_VBI_PAGE:
> > +            input->teletext_page = event->vbi_page < 999 ? event-
> > >vbi_page : 100;
> > +            vlc_player_SendEvent(player, on_teletext_page_changed,
> > +                                 input->teletext_page);
> > +            break;
> > +        case INPUT_EVENT_VBI_TRANSPARENCY:
> > +            input->teletext_transparent = event->vbi_transparent;
> > +            vlc_player_SendEvent(player, 
> > on_teletext_transparency_changed,
> > +                                 input->teletext_transparent);
> > +            break;
> > +        default:
> > +            break;
> > +    }
> > +
> > +    vlc_mutex_unlock(&player->lock);
> > +}
> > +
> > +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);
> > +}
> > +
> > +void
> > +vlc_player_CondWait(vlc_player_t *player, vlc_cond_t *cond)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    vlc_cond_wait(cond, &player->lock);
> > +}
> > +
> > +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);
> > +
> > +    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,
> > +                          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);
> > +
> > +    vlc_player_CancelWaitError(player);
> > +
> > +    vlc_player_InvalidateNextMedia(player);
> > +
> > +    if (media)
> > +    {
> > +        /* Switch to this new media when the current input is stopped 
> > */
> > +        player->next_media = input_item_Hold(media);
> > +        player->releasing_media = false;
> > +        player->has_next_media = true;
> > +    }
> > +    else
> > +    {
> > +        /* The current media will be set to NULL once the current input 
> > is
> > +         * stopped */
> > +        player->releasing_media = true;
> > +        player->has_next_media = false;
> > +    }
> > +
> > +    if (player->input)
> > +    {
> > +        vlc_player_destructor_AddInput(player, player->input);
> > +        player->input = NULL;
> > +    }
> > +
> > +    assert(media == player->next_media);
> > +    if (!vlc_player_destructor_IsEmpty(player))
> > +    {
> > +        /* This media will be opened when the input is finally stopped 
> > */
> > +        return VLC_SUCCESS;
> > +    }
> > +
> > +    /* We can switch to the next media directly */
> > +    return vlc_player_OpenNextMedia(player);
> > +}
> > +
> > +input_item_t *
> > +vlc_player_GetCurrentMedia(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +
> > +    return player->media;
> > +}
> > +
> > +int
> > +vlc_player_AddAssociatedMedia(vlc_player_t *player,
> > +                              enum es_format_category_e cat, const char 
> > *uri,
> > +                              bool select, bool notify, bool check_ext)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return VLC_EGENERIC;
> > +
> > +    enum slave_type type;
> > +    switch (cat)
> > +    {
> > +        case AUDIO_ES:
> > +            type = SLAVE_TYPE_AUDIO;
> > +            break;
> > +        case SPU_ES:
> > +            type = SLAVE_TYPE_SPU;
> > +            break;
> > +        default:
> > +            return VLC_EGENERIC;
> > +    }
> > +    return input_AddSlave(input->thread, type, uri, select, notify, 
> > check_ext);
> > +}
> > +
> > +void
> > +vlc_player_SetAssociatedSubsFPS(vlc_player_t *player, float fps)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    var_SetFloat(player, "sub-fps", fps);
> > +    if (input)
> > +        input_ControlPushHelper(input->thread, 
> > INPUT_CONTROL_SET_SUBS_FPS,
> > +                                &(vlc_value_t) { .f_float = fps });
> > +    vlc_player_SendEvent(player, on_associated_subs_fps_changed, fps);
> > +}
> > +
> > +float
> > +vlc_player_GetAssociatedSubsFPS(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    return var_GetFloat(player, "sub-fps");
> > +}
> > +
> > +void
> > +vlc_player_InvalidateNextMedia(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    if (player->next_media)
> > +    {
> > +        input_item_Release(player->next_media);
> > +        player->next_media = NULL;
> > +    }
> > +    player->has_next_media = false;
> > +
> > +#if GAPLESS
> > +    if (player->next_input)
> > +    {
> > +        /* Cause the get_next callback to be called when this input is
> > +         * dead */
> > +        vlc_player_destructor_AddInput(player, player->next_input);
> > +        player->next_input = NULL;
> > +    }
> > +#endif
> > +}
> > +
> > +int
> > +vlc_player_Start(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +
> > +    vlc_player_CancelWaitError(player);
> > +
> > +    if (player->started)
> > +        return VLC_SUCCESS;
> > +
> > +    if (!vlc_player_destructor_IsEmpty(player))
> > +    {
> > +        if (player->next_media)
> > +        {
> > +            player->started = true;
> > +            return VLC_SUCCESS;
> > +        }
> > +        else
> > +            return VLC_EGENERIC;
> > +    }
> > +
> > +    if (!player->media)
> > +        return VLC_EGENERIC;
> > +
> > +    if (!player->input)
> > +    {
> > +        /* Possible if the player was stopped by the user */
> > +        player->input = vlc_player_input_New(player, player->media);
> > +
> > +        if (!player->input)
> > +            return VLC_ENOMEM;
> > +    }
> > +    assert(!player->input->started);
> > +
> > +    if (player->start_paused)
> > +    {
> > +        var_Create(player->input->thread, "start-paused", 
> > VLC_VAR_BOOL);
> > +        var_SetBool(player->input->thread, "start-paused", true);
> > +    }
> > +
> > +    int ret = vlc_player_input_Start(player->input);
> > +    if (ret == VLC_SUCCESS)
> > +        player->started = true;
> > +    return ret;
> > +}
> > +
> > +void
> > +vlc_player_Stop(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    vlc_player_CancelWaitError(player);
> > +
> > +    if (!input || !player->started)
> > +        return;
> > +    player->started = false;
> > +
> > +    vlc_player_destructor_AddInput(player, input);
> > +    player->input = NULL;
> > +
> > +#if GAPLESS
> > +    if (player->next_input)
> > +    {
> > +        vlc_player_destructor_AddInput(player, next_input);
> > +        player->next_input = NULL;
> > +    }
> > +#endif
> > +}
> > +
> > +void
> > +vlc_player_SetMediaStoppedAction(vlc_player_t *player,
> > +                                 enum vlc_player_media_stopped_action 
> > action)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    player->media_stopped_action = action;
> > +    var_SetBool(player, "play-and-pause",
> > +                action == VLC_PLAYER_MEDIA_STOPPED_PAUSE);
> > +    vlc_player_SendEvent(player, on_media_stopped_action_changed, 
> > action);
> > +}
> > +
> > +void
> > +vlc_player_SetStartPaused(vlc_player_t *player, bool start_paused)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    player->start_paused = start_paused;
> > +}
> > +
> > +static void
> > +vlc_player_SetPause(vlc_player_t *player, bool pause)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input || !input->started)
> > +        return;
> > +
> > +    vlc_value_t val = { .i_int = pause ? PAUSE_S : PLAYING_S };
> > +    input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_STATE, 
> > &val);
> > +}
> > +
> > +void
> > +vlc_player_Pause(vlc_player_t *player)
> > +{
> > +    vlc_player_SetPause(player, true);
> > +}
> > +
> > +void
> > +vlc_player_Resume(vlc_player_t *player)
> > +{
> > +    vlc_player_SetPause(player, false);
> > +}
> 
> out of curiosity, any reason these aren't static inline along with TogglePause?

No reason ;) These function can be helper yes.

> 
> > +
> > +void
> > +vlc_player_NextVideoFrame(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPushHelper(input->thread, 
> > INPUT_CONTROL_SET_FRAME_NEXT,
> > +                                NULL);
> > +}
> > +
> > +enum vlc_player_state
> > +vlc_player_GetState(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    return player->global_state;
> > +}
> > +
> > +enum vlc_player_error
> > +vlc_player_GetError(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input ? input->error : VLC_PLAYER_ERROR_NONE;
> > +}
> > +
> > +int
> > +vlc_player_GetCapabilities(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input ? input->capabilities : 0;
> > +}
> > +
> > +float
> > +vlc_player_GetRate(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        return input->rate;
> > +    else
> > +        return var_GetFloat(player, "rate");
> > +}
> > +
> > +void
> > +vlc_player_ChangeRate(vlc_player_t *player, float rate)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (rate == 0.0)
> > +        return;
> > +
> > +    /* Save rate accross inputs */
> > +    var_SetFloat(player, "rate", rate);
> > +
> > +    if (input)
> > +    {
> > +        input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_RATE,
> > +            &(vlc_value_t) { .i_int = INPUT_RATE_DEFAULT / rate });
> > +    }
> > +    else
> > +        vlc_player_SendEvent(player, on_rate_changed, rate);
> > +}
> > +
> > +static void
> > +vlc_player_ChangeRateOffset(vlc_player_t *player, bool increment)
> > +{
> > +    static const float rates[] = {
> > +        1.0/64, 1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0/3, 1.0/2, 2.0/3,
> > +        1.0/1,
> > +        3.0/2, 2.0/1, 3.0/1, 4.0/1, 8.0/1, 16.0/1, 32.0/1, 64.0/1,
> > +    };
> > +    float rate = vlc_player_GetRate(player) * (increment ? 1.1f : 
> > 0.9f);
> > +
> > +    /* find closest rate (if any) in the desired direction */
> > +    for (size_t i = 0; i < ARRAY_SIZE(rates); ++i)
> > +    {
> > +        if ((increment && rates[i] > rate) ||
> > +            (!increment && rates[i] >= rate && i))
> > +        {
> > +            rate = increment ? rates[i] : rates[i-1];
> > +            break;
> > +        }
> > +    }
> > +
> > +    vlc_player_ChangeRate(player, rate);
> > +}
> > +
> > +void
> > +vlc_player_IncrementRate(vlc_player_t *player)
> > +{
> > +    vlc_player_ChangeRateOffset(player, true);
> > +}
> > +
> > +void
> > +vlc_player_DecrementRate(vlc_player_t *player)
> > +{
> > +    vlc_player_ChangeRateOffset(player, false);
> > +}
> > +
> > +vlc_tick_t
> > +vlc_player_GetLength(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input ? input->length : VLC_TICK_INVALID;
> > +}
> > +
> > +vlc_tick_t
> > +vlc_player_GetTime(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input || input->position_ms == VLC_TICK_INVALID)
> > +        return VLC_TICK_INVALID;
> > +
> > +    return input->position_ms;
> > +}
> > +
> > +float
> > +vlc_player_GetPosition(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    return input ? input->position_percent : -1.f;
> > +}
> > +
> > +static inline void
> > +vlc_player_assert_seek_params(enum vlc_player_seek_speed speed,
> > +                              enum vlc_player_whence whence)
> > +{
> > +    assert(speed == VLC_PLAYER_SEEK_PRECISE
> > +        || speed == VLC_PLAYER_SEEK_FAST);
> > +    assert(whence == VLC_PLAYER_WHENCE_ABSOLUTE
> > +        || whence == VLC_PLAYER_WHENCE_RELATIVE);
> > +    (void) speed; (void) whence;
> > +}
> > +
> > +void
> > +vlc_player_SeekByPos(vlc_player_t *player, float position,
> > +                     enum vlc_player_seek_speed speed,
> > +                     enum vlc_player_whence whence)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    vlc_player_assert_seek_params(speed, whence);
> > +
> > +    const int type =
> > +        whence == VLC_PLAYER_WHENCE_ABSOLUTE ? 
> > INPUT_CONTROL_SET_POSITION
> > +                                             : 
> > INPUT_CONTROL_JUMP_POSITION;
> > +    if (input)
> > +        input_ControlPush(input->thread, type,
> > +            &(input_control_param_t) {
> > +                .pos.f_val = position,
> > +                .pos.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
> > +        });
> > +}
> > +
> > +void
> > +vlc_player_SeekByTime(vlc_player_t *player, vlc_tick_t time,
> > +                      enum vlc_player_seek_speed speed,
> > +                      enum vlc_player_whence whence)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    vlc_player_assert_seek_params(speed, whence);
> > +
> > +    const int type =
> > +        whence == VLC_PLAYER_WHENCE_ABSOLUTE ? INPUT_CONTROL_SET_TIME
> > +                                             : INPUT_CONTROL_JUMP_TIME;
> > +    if (input)
> > +        input_ControlPush(input->thread, type,
> > +            &(input_control_param_t) {
> > +                .time.i_val = time,
> > +                .time.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
> > +        });
> > +}
> > +
> > +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);
> > +    }
> > +    vlc_player_SendEvent(player, on_renderer_changed, player-
> > >renderer);
> > +}
> > +
> > +vlc_renderer_item_t *
> > +vlc_player_GetRenderer(vlc_player_t *player)
> > +{
> > +    vlc_player_assert_locked(player);
> > +    return player->renderer;
> > +}
> > +
> > +int
> > +vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop 
> > abloop)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input || !vlc_player_CanSeek(player))
> > +        return VLC_EGENERIC;
> > +
> > +    vlc_tick_t time = vlc_player_GetTime(player);
> > +    float pos = vlc_player_GetPosition(player);
> > +    int ret = VLC_SUCCESS;
> > +    switch (abloop)
> > +    {
> > +        case VLC_PLAYER_ABLOOP_A:
> > +            if (input->abloop_state[1].set)
> > +                return VLC_EGENERIC;
> > +            input->abloop_state[0].time = time;
> > +            input->abloop_state[0].pos = pos;
> > +            input->abloop_state[0].set = true;
> > +            break;
> > +        case VLC_PLAYER_ABLOOP_B:
> > +            if (!input->abloop_state[0].set)
> > +                return VLC_EGENERIC;
> > +            input->abloop_state[1].time = time;
> > +            input->abloop_state[1].pos = pos;
> > +            input->abloop_state[1].set = true;
> > +            if (input->abloop_state[0].time != VLC_TICK_INVALID
> > +             && time != VLC_TICK_INVALID)
> > +            {
> > +                if (time > input->abloop_state[0].time)
> > +                {
> > +                    vlc_player_SetTime(player, input-
> > >abloop_state[0].time);
> > +                    break;
> > +                }
> > +            }
> > +            else if (pos > input->abloop_state[0].pos)
> > +            {
> > +                vlc_player_SetPosition(player, input-
> > >abloop_state[0].pos);
> > +                break;
> > +            }
> > +
> > +            /* Error: A time is superior to B time. */
> > +            abloop = VLC_PLAYER_ABLOOP_NONE;
> > +            ret = VLC_EGENERIC;
> > +            /* fall-through */
> > +        case VLC_PLAYER_ABLOOP_NONE:
> > +            input->abloop_state[0].set = input->abloop_state[1].set = 
> > false;
> > +            time = VLC_TICK_INVALID;
> > +            pos = 0.f;
> > +            break;
> > +        default:
> > +            vlc_assert_unreachable();
> > +    }
> > +    vlc_player_SendEvent(player, on_atobloop_changed, abloop, time, 
> > pos);
> > +    return ret;
> > +}
> > +
> > +enum vlc_player_abloop
> > +vlc_player_GetAtoBLoop(vlc_player_t *player, vlc_tick_t *a_time, float 
> > *a_pos,
> > +                       vlc_tick_t *b_time, float *b_pos)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input || !vlc_player_CanSeek(player) || !input-
> > >abloop_state[0].set)
> > +        return VLC_PLAYER_ABLOOP_NONE;
> > +
> > +    if (a_time)
> > +        *a_time = input->abloop_state[0].time;
> > +    if (a_pos)
> > +        *a_pos = input->abloop_state[0].pos;
> > +    if (!input->abloop_state[1].set)
> > +        return VLC_PLAYER_ABLOOP_A;
> > +
> > +    if (b_time)
> > +        *b_time = input->abloop_state[1].time;
> > +    if (b_pos)
> > +        *b_pos = input->abloop_state[1].pos;
> > +    return VLC_PLAYER_ABLOOP_B;
> > +}
> > +
> > +void
> > +vlc_player_Navigate(vlc_player_t *player, enum vlc_player_nav nav)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (!input)
> > +        return;
> > +
> > +    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)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    return input ? input->recording : false;
> > +}
> > +
> > +void
> > +vlc_player_SetRecordingEnabled(vlc_player_t *player, bool enabled)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPushHelper(input->thread, 
> > INPUT_CONTROL_SET_RECORD_STATE,
> > +                                &(vlc_value_t) { .b_bool = enabled });
> > +}
> > +
> > +void
> > +vlc_player_SetAudioDelay(vlc_player_t *player, vlc_tick_t delay,
> > +                         enum vlc_player_whence whence)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    if (input)
> > +        input_ControlPush(input->thread, INPUT_CONTROL_SET_AUDIO_DELAY,
> > +            &(input_control_param_t) {
> > +                .delay = {
> > +                    .b_absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE,
> > +                    .i_val = delay,
> > +                },
> > +        });
> > +}
> > +
> > +vlc_tick_t
> > +vlc_player_GetAudioDelay(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input ? input->audio_delay : 0;
> > +}
> > +
> > +void
> > +vlc_player_SetSubtitleDelay(vlc_player_t *player, vlc_tick_t delay,
> > +                            enum vlc_player_whence whence)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> > +    if (input)
> > +        input_ControlPush(input->thread, INPUT_CONTROL_SET_SPU_DELAY,
> > +            &(input_control_param_t) {
> > +                .delay = {
> > +                    .b_absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE,
> > +                    .i_val = delay,
> > +                },
> > +        });
> > +}
> > +
> > +vlc_tick_t
> > +vlc_player_GetSubtitleDelay(vlc_player_t *player)
> > +{
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +    return input ? input->subtitle_delay : 0;
> > +}
> > +
> > +int
> > +vlc_player_GetSignal(vlc_player_t *player, float *quality, float 
> > *strength)
> > +{
> 
> Could probably use an assert for quality & strength
> 
> > +    struct vlc_player_input *input = 
> > vlc_player_get_input_locked(player);
> > +
> 
> 
> -- 
>   Hugo Beauzée-Luyssen
>   hugo at beauzee.fr
> _______________________________________________
> 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