[vlc-devel] [RFC PATCH 1/2] core: add vlc_player_t (input manager)

Thomas Guillem thomas at gllm.fr
Thu Sep 6 13:11:31 CEST 2018


---
 PLAYER_TODO.txt      |    4 +
 include/vlc_player.h |  759 +++++++++++++++++++++++++
 src/Makefile.am      |    2 +
 src/input/player.c   | 1276 ++++++++++++++++++++++++++++++++++++++++++
 src/libvlccore.sym   |   28 +
 5 files changed, 2069 insertions(+)
 create mode 100644 PLAYER_TODO.txt
 create mode 100644 include/vlc_player.h
 create mode 100644 src/input/player.c

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



More information about the vlc-devel mailing list