[vlc-devel] [PATCH 1/2] core: add player API
Thomas Guillem
thomas at gllm.fr
Mon Oct 22 18:11:07 CEST 2018
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,
+};
+
+/**
+ * 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)
+/** 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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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)
+ * @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)
+ * @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
+ * @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
+ * @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);
+
+/**
+ * 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
+ */
+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.
+ *
+ * @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
+ */
+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
+ */
+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;
+ 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;
+ 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;
+}
+
+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;
+ 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);
+}
+
+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)
+{
+ 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);
+ while (!player->deleting
+ || !vlc_player_destructor_IsEmpty(player))
+ {
+ 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)
+ {
+ 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)
+ {
+ 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);
+}
+
+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)
+{
+ struct vlc_player_input *input = vlc_player_get_input_locked(player);
+
+ if (input && input->signal_quality >= 0 && input->signal_strength >= 0)
+ {
+ *quality = input->signal_quality;
+ *strength = input->signal_strength;
+ return VLC_SUCCESS;
+ }
+ return VLC_EGENERIC;
+}
+
+const struct input_stats_t *
+vlc_player_GetStatistics(vlc_player_t *player)
+{
+ struct vlc_player_input *input = vlc_player_get_input_locked(player);
+
+ return input ? &input->stats : NULL;
+}
+
+vout_thread_t **
+vlc_player_GetVouts(vlc_player_t *player, size_t *count)
+{
+ vout_thread_t **vouts;
+ input_resource_HoldVouts(player->resource, &vouts, count);
+ return vouts;
+}
+
+audio_output_t *
+vlc_player_GetAout(vlc_player_t *player)
+{
+ return input_resource_HoldAout(player->resource);
+}
+
+vlc_player_aout_listener_id *
+vlc_player_aout_AddListener(vlc_player_t *player,
+ const struct vlc_player_aout_cbs *cbs,
+ void *cbs_data)
+{
+ assert(cbs);
+
+ vlc_player_aout_listener_id *listener = malloc(sizeof(*listener));
+ if (!listener)
+ return NULL;
+
+ listener->cbs = cbs;
+ listener->cbs_data = cbs_data;
+
+ vlc_mutex_lock(&player->aout_listeners_lock);
+ vlc_list_append(&listener->node, &player->aout_listeners);
+ vlc_mutex_unlock(&player->aout_listeners_lock);
+
+ return listener;
+}
+
+void
+vlc_player_aout_RemoveListener(vlc_player_t *player,
+ vlc_player_aout_listener_id *id)
+{
+ assert(id);
+
+ vlc_mutex_lock(&player->aout_listeners_lock);
+ vlc_list_remove(&id->node);
+ vlc_mutex_unlock(&player->aout_listeners_lock);
+ free(id);
+}
+
+static int
+vlc_player_AoutCallback(vlc_object_t *this, const char *var,
+ vlc_value_t oldval, vlc_value_t newval, void *data)
+{
+ vlc_player_t *player = data;
+
+ if (strcmp(var, "volume") == 0)
+ {
+ if (oldval.f_float != newval.f_float)
+ vlc_player_aout_SendEvent(player, on_volume_changed, newval.f_float);
+ }
+ else if (strcmp(var, "mute") == 0)
+ {
+ if (oldval.b_bool != newval.b_bool )
+ vlc_player_aout_SendEvent(player, on_mute_changed, newval.b_bool);
+ }
+ else
+ vlc_assert_unreachable();
+
+ (void) this;
+ return VLC_SUCCESS;
+}
+
+float
+vlc_player_aout_GetVolume(vlc_player_t *player)
+{
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (!aout)
+ return -1.f;
+ return aout_VolumeGet(aout);
+}
+
+int
+vlc_player_aout_SetVolume(vlc_player_t *player, float volume)
+{
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (!aout)
+ return -1;
+ int ret = aout_VolumeSet(aout, volume);
+ vlc_object_release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_IncrementVolume(vlc_player_t *player, float volume,
+ float *result)
+{
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (!aout)
+ return -1;
+ int ret = aout_VolumeUpdate(aout, volume, result);
+ vlc_object_release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_IsMuted(vlc_player_t *player)
+{
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (!aout)
+ return -1;
+ int ret = aout_MuteGet(aout);
+ vlc_object_release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_Mute(vlc_player_t *player, bool mute)
+{
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (!aout)
+ return -1;
+ int ret = aout_MuteSet (aout, mute);
+ vlc_object_release(aout);
+
+ return ret;
+}
+
+
+int
+vlc_player_aout_EnableFilter(vlc_player_t *player, const char *name, bool add)
+{
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (!aout)
+ return -1;
+ aout_EnableFilter(aout, name, add);
+ vlc_object_release(aout);
+ return 0;
+}
+
+vlc_player_vout_listener_id *
+vlc_player_vout_AddListener(vlc_player_t *player,
+ const struct vlc_player_vout_cbs *cbs,
+ void *cbs_data)
+{
+ assert(cbs);
+
+ vlc_player_vout_listener_id *listener = malloc(sizeof(*listener));
+ if (!listener)
+ return NULL;
+
+ listener->cbs = cbs;
+ listener->cbs_data = cbs_data;
+
+ vlc_mutex_lock(&player->vout_listeners_lock);
+ vlc_list_append(&listener->node, &player->vout_listeners);
+ vlc_mutex_unlock(&player->vout_listeners_lock);
+
+ return listener;
+}
+
+void
+vlc_player_vout_RemoveListener(vlc_player_t *player,
+ vlc_player_vout_listener_id *id)
+{
+ assert(id);
+
+ vlc_mutex_lock(&player->vout_listeners_lock);
+ vlc_list_remove(&id->node);
+ vlc_mutex_unlock(&player->vout_listeners_lock);
+ free(id);
+}
+
+bool
+vlc_player_vout_IsFullscreen(vlc_player_t *player)
+{
+ vlc_player_assert_locked(player);
+ return var_GetBool(player, "fullscreen");
+}
+
+static int
+vlc_player_VoutCallback(vlc_object_t *this, const char *var,
+ vlc_value_t oldval, vlc_value_t newval, void *data)
+{
+ vlc_player_t *player = data;
+
+ if (strcmp(var, "fullscreen") == 0)
+ {
+ if (oldval.b_bool != newval.b_bool )
+ vlc_player_vout_SendEvent(player, on_fullscreen_changed,
+ (vout_thread_t *)this, newval.b_bool);
+ }
+ else if (strcmp(var, "video-wallpaper") == 0)
+ {
+ if (oldval.b_bool != newval.b_bool )
+ vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed,
+ (vout_thread_t *)this, newval.b_bool);
+ }
+ else
+ vlc_assert_unreachable();
+
+ return VLC_SUCCESS;
+}
+
+static void
+vlc_player_vout_SetOptionEnabled(vlc_player_t *player, const char *option,
+ bool enabled)
+{
+ var_SetBool(player, option, enabled);
+
+ size_t count;
+ vout_thread_t **vouts = vlc_player_GetVouts(player, &count);
+ for (size_t i = 0; i < count; i++)
+ {
+ var_SetBool(vouts[i], option, enabled);
+ vlc_object_release(vouts[i]);
+ }
+ free(vouts);
+}
+
+void
+vlc_player_vout_SetFullscreen(vlc_player_t *player, bool enabled)
+{
+ vlc_player_assert_locked(player);
+ vlc_player_vout_SetOptionEnabled(player, "fullscreen", enabled);
+ vlc_player_vout_SendEvent(player, on_fullscreen_changed, NULL, enabled);
+}
+
+bool
+vlc_player_vout_IsWallpaperModeEnabled(vlc_player_t *player)
+{
+ return var_GetBool(player, "video-wallpaper");
+}
+
+void
+vlc_player_vout_SetWallpaperModeEnabled(vlc_player_t *player, bool enabled)
+{
+ vlc_player_assert_locked(player);
+ vlc_player_vout_SetOptionEnabled(player, "video-wallpaper", enabled);
+ vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed, NULL, enabled);
+}
+
+static void
+vlc_player_InitLocks(vlc_player_t *player)
+{
+ vlc_mutex_init(&player->lock);
+ vlc_mutex_init(&player->vout_listeners_lock);
+ vlc_mutex_init(&player->aout_listeners_lock);
+ vlc_cond_init(&player->start_delay_cond);
+ vlc_cond_init(&player->destructor.wait);
+}
+
+static void
+vlc_player_DestroyLocks(vlc_player_t *player)
+{
+ vlc_mutex_destroy(&player->lock);
+ vlc_mutex_destroy(&player->vout_listeners_lock);
+ vlc_mutex_destroy(&player->aout_listeners_lock);
+ vlc_cond_destroy(&player->start_delay_cond);
+ vlc_cond_destroy(&player->destructor.wait);
+}
+
+void
+vlc_player_Delete(vlc_player_t *player)
+{
+ vlc_mutex_lock(&player->lock);
+
+ if (player->input)
+ vlc_player_destructor_AddInput(player, player->input);
+#if GAPLESS
+ if (player->next_input)
+ vlc_player_destructor_AddInput(player, player->next_inpu);
+#endif
+
+ player->deleting = true;
+ vlc_cond_signal(&player->destructor.wait);
+
+ assert(vlc_list_is_empty(&player->listeners));
+
+ vlc_mutex_unlock(&player->lock);
+
+ vlc_join(player->destructor.thread, NULL);
+
+ if (player->media)
+ input_item_Release(player->media);
+ if (player->next_media)
+ input_item_Release(player->next_media);
+
+ vlc_player_DestroyLocks(player);
+
+ audio_output_t *aout = vlc_player_GetAout(player);
+ if (aout)
+ {
+ var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
+ var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
+ vlc_object_release(aout);
+ }
+ 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_media_provider *media_provider,
+ void *media_provider_data)
+{
+ audio_output_t *aout = NULL;
+ vlc_player_t *player = vlc_custom_create(parent, sizeof(*player), "player");
+ if (!player)
+ return NULL;
+
+ assert(!media_provider || media_provider->get_next);
+
+ vlc_list_init(&player->listeners);
+ vlc_list_init(&player->vout_listeners);
+ vlc_list_init(&player->aout_listeners);
+ vlc_list_init(&player->destructor.inputs);
+ vlc_list_init(&player->destructor.stopping_inputs);
+ vlc_list_init(&player->destructor.joinable_inputs);
+ player->media_stopped_action = VLC_PLAYER_MEDIA_STOPPED_CONTINUE;
+ player->start_paused = false;
+ player->renderer = NULL;
+ player->media_provider = media_provider;
+ player->media_provider_data = media_provider_data;
+ player->media = NULL;
+ player->input = NULL;
+ player->global_state = VLC_PLAYER_STATE_STOPPED;
+ player->started = false;
+
+ player->error_count = 0;
+
+ player->releasing_media = false;
+ player->has_next_media = false;
+ player->next_media = NULL;
+#if GAPLESS
+ player->next_input = NULL;
+#endif
+
+#define VAR_CREATE(var, flag) do { \
+ if (var_Create(player, var, flag) != VLC_SUCCESS) \
+ goto error; \
+} while(0)
+
+ VAR_CREATE("rate", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
+ VAR_CREATE("sub-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
+ VAR_CREATE("fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
+ VAR_CREATE("video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
+ VAR_CREATE("video-wallpaper", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
+ VAR_CREATE("mute", VLC_VAR_BOOL);
+ /* TODO: Override these variables since the player handle media ended
+ * action itself. */
+ VAR_CREATE("start-paused", VLC_VAR_BOOL);
+ VAR_CREATE("play-and-pause", VLC_VAR_BOOL);
+
+#undef VAR_CREATE
+
+ player->resource = input_resource_New(VLC_OBJECT(player));
+
+ if (!player->resource)
+ goto error;
+
+ aout = input_resource_GetAout(player->resource);
+ if (aout != NULL)
+ {
+ var_AddCallback(aout, "volume", vlc_player_AoutCallback, player);
+ var_AddCallback(aout, "mute", vlc_player_AoutCallback, player);
+ input_resource_PutAout(player->resource, aout);
+ }
+
+ player->deleting = false;
+ vlc_player_InitLocks(player);
+
+ if (vlc_clone(&player->destructor.thread, vlc_player_destructor_Thread,
+ player, VLC_THREAD_PRIORITY_LOW) != 0)
+ {
+ vlc_player_DestroyLocks(player);
+ goto error;
+ }
+
+ return player;
+
+error:
+ if (aout)
+ {
+ var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
+ var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
+ }
+ if (player->resource)
+ input_resource_Release(player->resource);
+
+ vlc_object_release(player);
+ return NULL;
+}
+
+
diff --git a/src/input/player.h b/src/input/player.h
new file mode 100644
index 0000000000..cc10236a98
--- /dev/null
+++ b/src/input/player.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * player.h: Player internal 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.
+ *****************************************************************************/
+
+#ifndef VLC_PLAYER_INTERNAL_H
+#define VLC_PLAYER_INTERNAL_H
+
+#include <vlc_player.h>
+
+/**
+ * Assert that the player mutex is locked.
+ *
+ * This is exposed in this internal header because the playlist and its
+ * associated player share the lock to avoid lock-order inversion issues.
+ */
+void
+vlc_player_assert_locked(vlc_player_t *player);
+
+#endif
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index cf0a7e091c..e53c20e253 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -786,3 +786,100 @@ vlc_es_id_Hold
vlc_es_id_Release
vlc_es_id_GetInputId
vlc_es_id_GetCat
+vlc_player_AddAssociatedMedia
+vlc_player_AddListener
+vlc_player_aout_AddListener
+vlc_player_aout_EnableFilter
+vlc_player_aout_GetVolume
+vlc_player_aout_IncrementVolume
+vlc_player_aout_IsMuted
+vlc_player_aout_Mute
+vlc_player_aout_RemoveListener
+vlc_player_aout_SetVolume
+vlc_player_ChangeRate
+vlc_player_CondWait
+vlc_player_DecrementRate
+vlc_player_Delete
+vlc_player_GetAout
+vlc_player_GetAudioDelay
+vlc_player_GetCapabilities
+vlc_player_GetCurrentMedia
+vlc_player_GetError
+vlc_player_GetLength
+vlc_player_GetPosition
+vlc_player_GetProgram
+vlc_player_GetProgramAt
+vlc_player_GetProgramCount
+vlc_player_GetRate
+vlc_player_GetRenderer
+vlc_player_GetSelectedChapterIdx
+vlc_player_GetSelectedTitleIdx
+vlc_player_GetSignal
+vlc_player_GetState
+vlc_player_GetStatistics
+vlc_player_GetSubtitleDelay
+vlc_player_GetTeletextPage
+vlc_player_GetTime
+vlc_player_GetTitleList
+vlc_player_GetTrack
+vlc_player_GetTrackAt
+vlc_player_GetTrackCount
+vlc_player_GetVouts
+vlc_player_HasTeletextMenu
+vlc_player_IncrementRate
+vlc_player_InvalidateNextMedia
+vlc_player_IsRecording
+vlc_player_IsStarted
+vlc_player_IsTeletextEnabled
+vlc_player_IsTeletextTransparent
+vlc_player_Lock
+vlc_player_Navigate
+vlc_player_New
+vlc_player_NextVideoFrame
+vlc_player_Pause
+vlc_player_program_Delete
+vlc_player_program_Dup
+vlc_player_RemoveListener
+vlc_player_RestartTrack
+vlc_player_Resume
+vlc_player_SeekByPos
+vlc_player_SeekByTime
+vlc_player_SelectChapter
+vlc_player_SelectChapterIdx
+vlc_player_SelectDefaultTrack
+vlc_player_SelectNextChapter
+vlc_player_SelectNextTitle
+vlc_player_SelectPrevChapter
+vlc_player_SelectPrevTitle
+vlc_player_SelectProgram
+vlc_player_SelectTeletextPage
+vlc_player_SelectTitle
+vlc_player_SelectTitleIdx
+vlc_player_SelectTrack
+vlc_player_SetAssociatedSubsFPS
+vlc_player_SetAtoBLoop
+vlc_player_SetAudioDelay
+vlc_player_SetCurrentMedia
+vlc_player_SetMediaStoppedAction
+vlc_player_SetRecordingEnabled
+vlc_player_SetRenderer
+vlc_player_SetStartPaused
+vlc_player_SetSubtitleDelay
+vlc_player_SetTeletextEnabled
+vlc_player_SetTeletextTransparency
+vlc_player_Start
+vlc_player_Stop
+vlc_player_title_list_GetAt
+vlc_player_title_list_GetCount
+vlc_player_title_list_Hold
+vlc_player_title_list_Release
+vlc_player_track_Delete
+vlc_player_track_Dup
+vlc_player_Unlock
+vlc_player_UnselectTrack
+vlc_player_vout_AddListener
+vlc_player_vout_IsFullscreen
+vlc_player_vout_IsWallpaperModeEnabled
+vlc_player_vout_RemoveListener
+vlc_player_vout_SetFullscreen
+vlc_player_vout_SetWallpaperModeEnabled
--
2.19.1
More information about the vlc-devel
mailing list