diff --git a/PLAYER_TODO.txt b/PLAYER_TODO.txt new file mode 100644 index 0000000000000000000000000000000000000000..a3478b1fd65a6d0b6cd614c4c7e177caab501dd2 --- /dev/null +++ b/PLAYER_TODO.txt @@ -0,0 +1,6 @@ + - Remove input/control.c (move remaining implementation to player.c) + - Remove bookmark handling from input + - Remove INPUT_GET_VOUTS/INPUT_GET_AOUT + - Remove INPUT_GET_FULL_TITLE_INFO and priv->*title + - http cookies jar + - Seek callback diff --git a/include/vlc_player.h b/include/vlc_player.h new file mode 100644 index 0000000000000000000000000000000000000000..a460cbda225ec6097f1c625d6a1b352dd6dffdc7 --- /dev/null +++ b/include/vlc_player.h @@ -0,0 +1,2761 @@ +/***************************************************************************** + * 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 +@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"]; +} +@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, +}; + +/** + * 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 media 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, +}; + +/** + * Subtitle synchronisation + * + * @see vlc_player_SetSubtitleSync() + */ +enum vlc_player_subtitle_sync +{ + VLC_PLAYER_SUBTITLE_SYNC_RESET, + VLC_PLAYER_SUBTITLE_SYNC_MARK_AUDIO, + VLC_PLAYER_SUBTITLE_SYNC_MARK_SUBTITLE, + VLC_PLAYER_SUBTITLE_SYNC_APPLY, +}; + +/** + * Vout filter type + * + * @warning Temporary enum, waiting for a refined vout filter API + * + * @see vlc_player_vout_SetFilter() + */ +enum vlc_vout_filter_type +{ + VLC_VOUT_FILTER_VIDEO_SPLITTER, + VLC_VOUT_FILTER_VIDEO_FILTER, + VLC_VOUT_FILTER_SUB_SOURCE, + VLC_VOUT_FILTER_SUB_FILTER, +}; + +/** 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 ('y' << 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) + +/** + * 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); +}; + +/** + * 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 on 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) + * + * @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 use it + * from an other context. + * + * @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 + * + * @note This event can be called with both unselected_id and selected_id + * valid. This mean that a new track is replacing the old one. + * + * @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 use + * it from an other context. + * + * @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 + * + * @note This event can be called with both unselected_id and selected_id + * valid. This mean that a new program is replacing the old one. + * + * @param player locked player instance + * @param unselected_id valid program id or -1 (when nothing is unselected) + * @param selected_id valid program id or -1 (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 use it from an other context. + * + * @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 use it from an other context. + * + * @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 current 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 current 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 current 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 new vout + * @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); + + /** + * Called when the player is corked + * + * The player can be corked when the audio output loose focus or when a + * renderer was paused from the outside. + * + * @note called only if pause on cork was not set to true (by + * vlc_player_SetPauseOnCork()) + * @note a cork_count higher than 0 means the player is corked. In that + * case, the user should pause the player and release all external resource + * needed by the player. A value higher than 1 mean that the player was + * corked more than one time (for different reasons). A value of 0 means + * the player is no longer corked. In that case, the user could resume the + * player. + * + * @param player locked player instance + * @param cork_count 0 for uncorked, > 0 for corked + * @param data opaque pointer set by vlc_player_AddListener() + */ + void (*on_cork_changed)(vlc_player_t *player, unsigned cork_count, + 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;2.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 + * context. 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 + * context. + * + * @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 (will be held by the player) + * @return VLC_SUCCESS or a VLC error code + */ +VLC_API int +vlc_player_SetCurrentMedia(vlc_player_t *player, input_item_t *media); + +/** + * Get the current played media. + * + * @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, false to cancel it + */ +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 rate (< 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); +} + +/** + * Display the player position on the vout OSD + * + * @param player locked player instance + */ +VLC_API void +vlc_player_DisplayPosition(vlc_player_t *player); + +/** + * 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); + +/** + * Helper to get the selected track from an ES category + * + * @warning The player can have more than one selected track for a same ES + * category. This function will only return the first selected one. Use + * vlc_player_GetTrackAt() and vlc_player_GetTrackCount() to iterate through + * several selected tracks. + */ +static inline const struct vlc_player_track * +vlc_player_GetSelectedTrack(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) + return track; + } + return NULL; +} + +/** + * 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); + +/** + * Select the next track + * + * If the last track is already selected, a call to this function will disable + * this last track. And a second call will select the first track. + * + * @warning This function has no effects if there are several tracks selected + * for a same category. + * + * @param player locked player instance + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES + */ +VLC_API void +vlc_player_SelectNextTrack(vlc_player_t *player, + enum es_format_category_e cat); + +/** + * Select the Previous track + * + * If the first track is already selected, a call to this function will disable + * this first track. And a second call will select the last track. + * + * @warning This function has no effects if there are several tracks selected + * for a same category. + * + * @param player locked player instance + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES + */ +VLC_API void +vlc_player_SelectPrevTrack(vlc_player_t *player, + enum es_format_category_e cat); + +/** + * 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); + +/** + * Helper to restart all selected tracks from an ES category + */ +static inline void +vlc_player_RestartTrackCategory(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_RestartTrack(player, track->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); +} + +/** + * Enable or disable a track category + * + * If a track category is disabled, the player won't select any tracks of this + * category automatically or via an user action (vlc_player_SelectTrack()). + * + * @param player locked player instance + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES + * @param enabled true to enable + */ +VLC_API void +vlc_player_SetTrackCategoryEnabled(vlc_player_t *player, + enum es_format_category_e cat, bool enabled); + +/** + * Check if a track category is enabled + * + * @param player locked player instance + * @param cat VIDEO_ES, AUDIO_ES or SPU_ES + */ +VLC_API bool +vlc_player_IsTrackCategoryEnabled(vlc_player_t *player, + enum es_format_category_e cat); + +/** + * Helper to enable or disable video tracks + */ +static inline void +vlc_player_SetVideoEnabled(vlc_player_t *player, bool enabled) +{ + vlc_player_SetTrackCategoryEnabled(player, VIDEO_ES, enabled); +} + +/** + * Helper to check if video tracks are enabled + */ +static inline bool +vlc_player_IsVideoEnabled(vlc_player_t *player) +{ + return vlc_player_IsTrackCategoryEnabled(player, VIDEO_ES); +} + +/** + * Helper to enable or disable audio tracks + */ +static inline void +vlc_player_SetAudioEnabled(vlc_player_t *player, bool enabled) +{ + vlc_player_SetTrackCategoryEnabled(player, AUDIO_ES, enabled); +} + +/** + * Helper to check if audio tracks are enabled + */ +static inline bool +vlc_player_IsAudioEnabled(vlc_player_t *player) +{ + return vlc_player_IsTrackCategoryEnabled(player, AUDIO_ES); +} + +/** + * Helper to enable or disable subtitle tracks + */ +static inline void +vlc_player_SetSubtitleEnabled(vlc_player_t *player, bool enabled) +{ + vlc_player_SetTrackCategoryEnabled(player, SPU_ES, enabled); +} + +/** + * Helper to check if subtitle tracks are enabled + */ +static inline bool +vlc_player_IsSubtitleEnabled(vlc_player_t *player) +{ + return vlc_player_IsTrackCategoryEnabled(player, SPU_ES); +} + +/** + * 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); + +/** + * Select the next program + * + * @param player locked player instance + */ +VLC_API void +vlc_player_SelectNextProgram(vlc_player_t *player); + +/** + * Select the previous program + * + * @param player locked player instance + */ +VLC_API void +vlc_player_SelectPrevProgram(vlc_player_t *player); + +/** + * 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), the item will + * be held by the player + */ +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); + +/** + * Update the viewpoint + * + * @param player locked player instance + * @param viewpoint the viewpoint value + * @param whence absolute or relative + * @return VLC_SUCCESS or a VLC error code + */ +VLC_API void +vlc_player_UpdateViewpoint(vlc_player_t *player, + const vlc_viewpoint_t *viewpoint, + enum vlc_player_whence whence); + +/** + * 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_subtitle_delay_changed + * + * @param player locked player instance + */ +VLC_API vlc_tick_t +vlc_player_GetSubtitleDelay(vlc_player_t *player); + +/** + * Set subtitle synchronisation + * + * This function can be used to synchronise subtitles with the audio. + * + * Call this function with VLC_PLAYER_SUBTITLE_SYNC_MARK_AUDIO when your hear a + * voice you want to synchronise with subtitles. Then, call this function with + * VLC_PLAYER_SUBTITLE_SYNC_MARK_SUBTITLE when the subtitle corresponding to + * the voice is displayed. Finally call this function with + * VLC_PLAYER_SUBTITLE_SYNC_APPLY to apply the subtitle delay. Calling this + * function with VLC_PLAYER_SUBTITLE_SYNC_RESET will reset the subtitle + * synchronisation and set a subtitle delay of 0. + * + * @param player locked player instance + * @param sync synchronisation action + */ +VLC_API void +vlc_player_SetSubtitleSync(vlc_player_t *player, + enum vlc_player_subtitle_sync sync); + +/** + * 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); + +/** + * Set the subtitle text scaling factor + * + * @note This function have an effect only if the subtitle track is a text type. + * + * @param player locked player instance + * @param scale factor in the range [10;500] (default: 100) + */ +VLC_API void +vlc_player_SetSubtitleTextScale(vlc_player_t *player, unsigned scale); + +/** + * Get the subtitle text scaling factor + * + * @param player locked player instance + * @return scale factor + */ +VLC_API unsigned +vlc_player_GetSubtitleTextScale(vlc_player_t *player); + +/** + * 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 + * + * @param player locked player instance + * @return pointer to the player stats structure or NULL + */ +VLC_API const struct input_stats_t * +vlc_player_GetStatistics(vlc_player_t *player); + +/** + * Enable or disable pause on cork event + * + * If enabled, the player will automatically pause and resume on cork events. + * In that case, cork events won't be propagated via callbacks. + * @see vlc_player_cbs.on_cork_changed + * + * @param player locked player instance + * @param enabled true to enable + */ +VLC_API void +vlc_player_SetPauseOnCork(vlc_player_t *player, bool enabled); + +/** + * Get the audio output + * + * @warning The returned pointer must be released with vlc_object_release(). + * + * @param player player instance + * @return a valid audio_output_t * or NULL (if there is no aouts) + */ +VLC_API audio_output_t * +vlc_player_aout_Hold(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;2.f] or -1.f if there is no audio outputs + * (independent of mute) + */ +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;2.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 steps number of "volume-step" + * @param result pointer to store the resulting volume (can be NULL) + * @return VLC_SUCCESS or VLC_EGENERIC if there is no audio outputs + */ +VLC_API int +vlc_player_aout_IncrementVolume(vlc_player_t *player, int steps, float *result); + +/** + * Helper to decrement the audio volume + */ +static inline int +vlc_player_aout_DecrementVolume(vlc_player_t *player, int steps, float *result) +{ + return vlc_player_aout_IncrementVolume(player, -steps, 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 muted, -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); + +/** + * Get and hold the main video output + * + * @warning the returned vout_thread_t * must be released with + * vlc_object_release(). + * @see vlc_players_cbs.on_vout_list_changed + * + * @param player player instance + * @return a valid vout_thread_t * or NULL, cf. warning + */ +VLC_API vout_thread_t * +vlc_player_vout_Hold(vlc_player_t *player); + +/** + * Get and hold the list of video output + * + * @warning All vout_thread_t * element of the array must be released with + * vlc_object_release(). The returned array must be freed. + * + * @see vlc_players_cbs.on_vout_list_changed + * + * @param player 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_vout_HoldAll(vlc_player_t *player, size_t *count); + +/** + * 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)); +} + +/** + * Set a filter chain to all vouts and all future vouts + * + * @warning This is a temporary function, waiting for a refined vout filter + * API. + * + * @param player instance + * @param type filter type + * @param value a valid chain of filter (separated with ',') or NULL + */ +VLC_API void +vlc_player_vout_SetFilter(vlc_player_t *player, enum vlc_vout_filter_type type, + const char *value); + +/** + * Get the filter chain value applied to all vouts + * + * @warning This is a temporary function, waiting for a refined vout filter + * API. + * + * @param player instance + * @param type filter type + * @return an allocated string representing the filter chain, free it with + * free(), can be NULL + */ +VLC_API char * +vlc_player_vout_GetFilter(vlc_player_t *player, enum vlc_vout_filter_type type); + +/** + * Take a snapshot on all vouts + * + * @param player player instance + */ +VLC_API void +vlc_player_vout_Snapshot(vlc_player_t *player); + +/** + * Display an OSD message on all vouts + * + * @param player player instance + * @param fmt format string + */ +VLC_API void +vlc_player_vout_OSDMessage(vlc_player_t *player, const char *fmt, ...); + + +/** @} */ +#endif diff --git a/po/POTFILES.in b/po/POTFILES.in index de134cd667d8f2cc556a6de3334e2198545a0038..65494473ff89e65013ea3afdb1ef81a955dbf556 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 c344df087558483b7e347a5108e9d42dacc859f6..fd1e90904e49b3ce492a411198e48c00fa044665 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -77,6 +77,7 @@ pluginsinclude_HEADERS = \ ../include/vlc_picture_fifo.h \ ../include/vlc_picture_pool.h \ ../include/vlc_playlist_legacy.h \ + ../include/vlc_player.h \ ../include/vlc_plugin.h \ ../include/vlc_probe.h \ ../include/vlc_rand.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 0000000000000000000000000000000000000000..2014db8bf3beb0f6332d3dfbf9fc052cdf321b25 --- /dev/null +++ b/src/input/player.c @@ -0,0 +1,3585 @@ +/***************************************************************************** + * 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 <vlc_tick.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 time; + float position; + + 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; + + struct + { + vlc_tick_t audio_time; + vlc_tick_t subtitle_time; + } subsync; + + 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; + + bool pause_on_cork; + bool corked; + + 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); +static int +vlc_player_VoutOSDCallback(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 vout_thread_t ** +vlc_player_vout_OSDHoldAll(vlc_player_t *player, size_t *count) +{ + vout_thread_t **vouts = vlc_player_vout_HoldAll(player, count); + + for (size_t i = 0; i < *count; ++i) + { + vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD); + vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER); + vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER); + } + return vouts; +} + +static void +vlc_player_vout_OSDReleaseAll(vlc_player_t *player, vout_thread_t **vouts, + size_t count) +{ + for (size_t i = 0; i < count; ++i) + vlc_object_release(vouts[i]); + free(vouts); + (void) player; +} + +static inline void +vouts_osd_Message(vout_thread_t **vouts, size_t count, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + for (size_t i = 0; i < count; ++i) + vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, args); + va_end(args); +} + +static inline void +vouts_osd_Icon(vout_thread_t **vouts, size_t count, short type) +{ + for (size_t i = 0; i < count; ++i) + vout_OSDIcon(vouts[i], VOUT_SPU_CHANNEL_OSD, type); +} + +static inline void +vouts_osd_Slider(vout_thread_t **vouts, size_t count, int position, short type) +{ + int channel = type == OSD_HOR_SLIDER ? + VOUT_SPU_CHANNEL_OSD_HSLIDER : VOUT_SPU_CHANNEL_OSD_VSLIDER; + for (size_t i = 0; i < count; ++i) + vout_OSDSlider(vouts[i], channel, position, type); +} + +void +vlc_player_vout_OSDMessage(vlc_player_t *player, const char *fmt, ...) +{ + size_t count; + vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count); + + va_list args; + va_start(args, fmt); + for (size_t i = 0; i < count; ++i) + vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, args); + va_end(args); + + vlc_player_vout_OSDReleaseAll(player, vouts, count); +} + +static void +vlc_player_vout_OSDIcon(vlc_player_t *player, short type) +{ + size_t count; + vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count); + + vouts_osd_Icon(vouts, count, type); + + vlc_player_vout_OSDReleaseAll(player, vouts, count); +} + +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) + { + /* Release titles up to title_idx */ + 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->time = VLC_TICK_INVALID; + input->position = 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; + + input->subsync.audio_time = + input->subsync.subtitle_time = VLC_TICK_INVALID; + + 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) +{ + assert(input->titles == 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); + + input_Close(input->thread); + free(input); +} + +static int +vlc_player_input_Start(struct vlc_player_input *input) +{ + int ret = input_Start(input->thread); + if (ret != VLC_SUCCESS) + return ret; + input->started = true; + return ret; +} + +static void +vlc_player_PrepareNextMedia(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) +{ + if (input->started) + { + input->started = false; + /* Add this input to the stop list: it will be stopped by the + * destructor thread */ + 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); + } + else + { + /* 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)); + vlc_list_append(&input->node, &player->destructor.joinable_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) +{ + if (vlc_list_HasInput(&player->destructor.stopping_inputs, input)) + vlc_list_remove(&input->node); + + assert(!input->started); + vlc_player_destructor_AddInput(player, input); +} + +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); + + /* Terminate this thread when the player is deleting (vlc_player_Delete() + * was called) and when all input_thread_t all stopped and released. */ + while (!player->deleting + || !vlc_player_destructor_IsEmpty(player)) + { + /* Wait for an input to stop or close. No while loop here since we want + * to leave this code path when the player is deleting. */ + if (vlc_list_is_empty(&player->destructor.inputs) + && vlc_list_is_empty(&player->destructor.joinable_inputs)) + vlc_cond_wait(&player->destructor.wait, &player->lock); + + struct vlc_player_input *input; + vlc_list_foreach(input, &player->destructor.inputs, node) + { + vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING); + vlc_player_destructor_AddStoppingInput(player, input); + + input_Stop(input->thread); + } + + bool keep_sout = true; + const bool inputs_changed = + !vlc_list_is_empty(&player->destructor.joinable_inputs); + vlc_list_foreach(input, &player->destructor.joinable_inputs, node) + { + keep_sout = var_GetBool(input->thread, "sout-keep"); + + if (input->state == VLC_PLAYER_STATE_STOPPING) + vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPED); + + vlc_list_remove(&input->node); + vlc_player_input_Delete(input); + } + + if (inputs_changed) + { + const bool started = player->started; + vlc_player_Unlock(player); + if (!started) + input_resource_TerminateVout(player->resource); + if (!keep_sout) + input_resource_TerminateSout(player->resource); + vlc_player_Lock(player); + } + } + 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->titles) + { + vlc_player_title_list_Release(input->titles); + input->titles = NULL; + vlc_player_SendEvent(player, on_titles_changed, NULL); + } + + 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_PrepareNextMedia(player); + if (!player->next_media) + player->started = false; + } + send_event = !player->started; + 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; +} + +static inline void +vlc_player_vout_OSDProgram(vlc_player_t *player, const char *name) +{ + vlc_player_vout_OSDMessage(player, _("Program Service ID: %s"), name); +} + +void +vlc_player_SelectProgram(vlc_player_t *player, int id) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + + const struct vlc_player_program *prgm = + vlc_player_program_vector_FindById(&input->program_vector, + id, NULL); + if (prgm) + { + input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_PROGRAM, + &(vlc_value_t) { .i_int = id }); + vlc_player_vout_OSDProgram(player, prgm->name); + } +} + +static void +vlc_player_CycleProgram(vlc_player_t *player, bool next) +{ + size_t count = vlc_player_GetProgramCount(player); + if (!count) + return; + size_t index = 0; + bool selected = false; + for (size_t i = 0; i < count; ++i) + { + const struct vlc_player_program *prgm = + vlc_player_GetProgramAt(player, i); + if (prgm->selected) + { + /* Only one program can be selected at a time */ + assert(!selected); + index = i; + selected = true; + } + } + assert(selected); + if (next && index + 1 == count) /* First program */ + index = 0; + else if (!next && index == 0) /* Last program */ + index = count - 1; + else /* Next or Previous program */ + index = index + (next ? 1 : -1); + + const struct vlc_player_program *prgm = + vlc_player_GetProgramAt(player, index); + assert(prgm); + vlc_player_SelectProgram(player, prgm->group_id); +} + +void +vlc_player_SelectNextProgram(vlc_player_t *player) +{ + vlc_player_CycleProgram(player, true); +} + +void +vlc_player_SelectPrevProgram(vlc_player_t *player) +{ + vlc_player_CycleProgram(player, false); +} + +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); +} + +static inline const char * +es_format_category_to_string(enum es_format_category_e cat) +{ + switch (cat) + { + case VIDEO_ES: return "Video"; + case AUDIO_ES: return "Audio"; + case SPU_ES: return "Subtitle"; + default: return NULL; + } +} + +static void +vlc_player_vout_OSDTrack(vlc_player_t *player, vlc_es_id_t *id, bool select) +{ + enum es_format_category_e cat = vlc_es_id_GetCat(id); + const struct vlc_player_track *track = vlc_player_GetTrack(player, id); + if (!track && select) + return; + + const char *cat_name = es_format_category_to_string(cat); + assert(cat_name); + const char *track_name = select ? track->name : _("N/A"); + vlc_player_vout_OSDMessage(player, _("%s track: %s"), cat_name, track_name); +} + +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) + return; + + input_ControlPushEsHelper(input->thread, INPUT_CONTROL_SET_ES, id); + vlc_player_vout_OSDTrack(player, id, true); +} + +static void +vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat, + bool next) +{ + size_t count = vlc_player_GetTrackCount(player, cat); + if (!count) + return; + + size_t index; + bool selected = false; + 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) + { + if (selected) + { + /* Can't cycle through tracks if there are more than one + * selected */ + return; + } + index = i; + selected = true; + } + } + + if (!selected) + { + /* No track selected: select the first or the last track */ + index = next ? 0 : count - 1; + selected = true; + } + else + { + /* Unselect if we reach the end of the cycle */ + if ((next && index + 1 == count) || (!next && index == 0)) + selected = false; + else /* Switch to the next or previous track */ + index = index + (next ? 1 : -1); + } + + const struct vlc_player_track *track = + vlc_player_GetTrackAt(player, cat, index); + if (selected) + vlc_player_SelectTrack(player, track->es_id); + else + vlc_player_UnselectTrack(player, track->es_id); +} + +void +vlc_player_SelectNextTrack(vlc_player_t *player, + enum es_format_category_e cat) +{ + vlc_player_CycleTrack(player, cat, true); +} + +void +vlc_player_SelectPrevTrack(vlc_player_t *player, + enum es_format_category_e cat) +{ + vlc_player_CycleTrack(player, cat, false); +} + +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) + return; + + input_ControlPushEsHelper(input->thread, INPUT_CONTROL_UNSET_ES, id); + vlc_player_vout_OSDTrack(player, id, false); +} + +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); + + static const char osd_vars[][sizeof("deinterlace-mode")] = { + "aspect-ratio", "autoscale", "crop", "crop-bottom", + "crop-top", "crop-left", "crop-right", "deinterlace", + "deinterlace-mode", "sub-margin", "zoom" + }; + + vlc_player_t *player = input->player; + switch (ev->action) + { + case VLC_INPUT_EVENT_VOUT_ADDED: + vlc_player_SendEvent(player, on_vout_list_changed, + VLC_PLAYER_LIST_ADDED, ev->vout); + + /* Register vout callbacks after the vout list event */ + var_AddCallback(ev->vout, "fullscreen", + vlc_player_VoutCallback, player); + var_AddCallback(ev->vout, "video-wallpaper", + vlc_player_VoutCallback, player); + for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i) + var_AddCallback(ev->vout, osd_vars[i], + vlc_player_VoutOSDCallback, player); + break; + case VLC_INPUT_EVENT_VOUT_DELETED: + /* 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); + for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i) + var_DelCallback(ev->vout, osd_vars[i], + vlc_player_VoutOSDCallback, player); + + vlc_player_SendEvent(player, on_vout_list_changed, + VLC_PLAYER_LIST_REMOVED, ev->vout); + break; + default: + vlc_assert_unreachable(); + } +} + +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); + vlc_player_destructor_AddStoppingInput(input->player, input); + break; + case ERROR_S: + /* Don't send errors if the input is stopped by the user */ + if (input->started) + { + /* 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->time != VLC_TICK_INVALID + && input->abloop_state[0].time != VLC_TICK_INVALID + && input->abloop_state[1].time != VLC_TICK_INVALID) + { + if (input->time >= input->abloop_state[1].time) + vlc_player_SetTime(player, input->abloop_state[0].time); + } + else if (input->position >= 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->time != event->position.ms || + input->position != event->position.percentage) + { + input->time = event->position.ms; + input->position = event->position.percentage; + vlc_player_SendEvent(player, on_position_changed, + input->time, + input->position); + + 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: + if (input->started) /* Can happen with early input_thread fails */ + vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING); + 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; + + vlc_player_vout_OSDIcon(player, OSD_PLAY_ICON); + 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); + + vlc_player_InvalidateNextMedia(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); + + vlc_player_vout_OSDIcon(player, pause ? OSD_PAUSE_ICON : OSD_PLAY_ICON); +} + +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) + return; + input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_FRAME_NEXT, NULL); + vlc_player_vout_OSDMessage(player, _("Next frame")); +} + +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); + + vlc_player_vout_OSDMessage(player, ("Speed: %.2fx"), 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->time == VLC_TICK_INVALID) + return VLC_TICK_INVALID; + + return input->time; +} + +float +vlc_player_GetPosition(vlc_player_t *player) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + + return input ? input->position : -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; +} + +static inline void +vlc_player_vout_OSDPosition(vlc_player_t *player, + struct vlc_player_input *input, vlc_tick_t time, + float position, enum vlc_player_whence whence) +{ + if (input->length != VLC_TICK_INVALID) + { + if (time == VLC_TICK_INVALID) + time = position * input->length; + else + position = time / (float) input->length; + } + + size_t count; + vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count); + + if (time != VLC_TICK_INVALID) + { + if (whence == VLC_PLAYER_WHENCE_RELATIVE) + { + time += input->time; /* XXX: TOCTOU */ + if (time < 0) + time = 0; + } + + char time_text[MSTRTIME_MAX_SIZE]; + secstotimestr(time_text, SEC_FROM_VLC_TICK(time)); + if (input->length != VLC_TICK_INVALID) + { + char len_text[MSTRTIME_MAX_SIZE]; + secstotimestr(len_text, SEC_FROM_VLC_TICK(input->length)); + vouts_osd_Message(vouts, count, "%s / %s", time_text, len_text); + } + else + vouts_osd_Message(vouts, count, "%s", time_text); + } + + if (vlc_player_vout_IsFullscreen(player)) + { + if (whence == VLC_PLAYER_WHENCE_RELATIVE) + { + position += input->position; /* XXX: TOCTOU */ + if (position < 0.f) + position = 0.f; + } + vouts_osd_Slider(vouts, count, position * 100, OSD_HOR_SLIDER); + } + vlc_player_vout_OSDReleaseAll(player, vouts, count); +} + +void +vlc_player_DisplayPosition(vlc_player_t *player) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + vlc_player_vout_OSDPosition(player, input, input->time, input->position, + VLC_PLAYER_WHENCE_ABSOLUTE); +} + +void +vlc_player_SeekByPos(vlc_player_t *player, float position, + enum vlc_player_seek_speed speed, + enum vlc_player_whence whence) +{ + vlc_player_assert_seek_params(speed, whence); + + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + + const int type = + whence == VLC_PLAYER_WHENCE_ABSOLUTE ? INPUT_CONTROL_SET_POSITION + : INPUT_CONTROL_JUMP_POSITION; + input_ControlPush(input->thread, type, + &(input_control_param_t) { + .pos.f_val = position, + .pos.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST, + }); + + vlc_player_vout_OSDPosition(player, input, VLC_TICK_INVALID, position, + whence); +} + +void +vlc_player_SeekByTime(vlc_player_t *player, vlc_tick_t time, + enum vlc_player_seek_speed speed, + enum vlc_player_whence whence) +{ + vlc_player_assert_seek_params(speed, whence); + + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + + const int type = + whence == VLC_PLAYER_WHENCE_ABSOLUTE ? INPUT_CONTROL_SET_TIME + : INPUT_CONTROL_JUMP_TIME; + input_ControlPush(input->thread, type, + &(input_control_param_t) { + .time.i_val = time, + .time.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST, + }); + + vlc_player_vout_OSDPosition(player, input, time, -1, whence); +} + +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); +} + +void +vlc_player_UpdateViewpoint(vlc_player_t *player, + const vlc_viewpoint_t *viewpoint, + enum vlc_player_whence whence) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (input) + input_UpdateViewpoint(input->thread, viewpoint, + whence == VLC_PLAYER_WHENCE_ABSOLUTE); +} + +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 enable) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_RECORD_STATE, + &(vlc_value_t) { .b_bool = enable }); + + vlc_player_vout_OSDMessage(player, enable ? + _("Recording") : _("Recording done")); +} + +void +vlc_player_SetAudioDelay(vlc_player_t *player, vlc_tick_t delay, + enum vlc_player_whence whence) +{ + bool absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE; + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + + input_ControlPush(input->thread, INPUT_CONTROL_SET_AUDIO_DELAY, + &(input_control_param_t) { + .delay = { + .b_absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE, + .i_val = delay, + }, + }); + + if (!absolute) + delay += input->audio_delay; + vlc_player_vout_OSDMessage(player, _("Audio delay: %i ms"), + (int)MS_FROM_VLC_TICK(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; +} + +static void +vlc_player_SetSubtitleDelayInternal(vlc_player_t *player, vlc_tick_t delay, + enum vlc_player_whence whence) +{ + bool absolute = whence == VLC_PLAYER_WHENCE_ABSOLUTE; + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + + input_ControlPush(input->thread, INPUT_CONTROL_SET_SPU_DELAY, + &(input_control_param_t) { + .delay = { + .b_absolute = absolute, + .i_val = delay, + }, + }); +} + +void +vlc_player_SetSubtitleDelay(vlc_player_t *player, vlc_tick_t delay, + enum vlc_player_whence whence) +{ + vlc_player_SetSubtitleDelayInternal(player, delay, whence); + vlc_player_vout_OSDMessage(player, _("Subtitle delay: %s%i ms"), + whence == VLC_PLAYER_WHENCE_ABSOLUTE ? "" : "+", + (int)MS_FROM_VLC_TICK(delay)); +} + +static struct { + const char var[sizeof("video")]; + const char sout_var[sizeof("sout-video")]; +} cat2vars[] = { + [VIDEO_ES] = { "video", "sout-video" }, + [AUDIO_ES] = { "audio", "sout-audio" }, + [SPU_ES] = { "spu", "sout-spu" }, +}; + +void +vlc_player_SetTrackCategoryEnabled(vlc_player_t *player, + enum es_format_category_e cat, bool enabled) +{ + assert(cat >= UNKNOWN_ES && cat <= DATA_ES); + struct vlc_player_input *input = vlc_player_get_input_locked(player); + + var_SetBool(player, cat2vars[cat].var, enabled); + var_SetBool(player, cat2vars[cat].sout_var, enabled); + + if (input) + { + var_SetBool(input->thread, cat2vars[cat].var, enabled); + var_SetBool(input->thread, cat2vars[cat].sout_var, enabled); + + if (!enabled) + vlc_player_UnselectTrackCategory(player, cat); + } +} + +bool +vlc_player_IsTrackCategoryEnabled(vlc_player_t *player, + enum es_format_category_e cat) +{ + assert(cat >= UNKNOWN_ES && cat <= DATA_ES); + return var_GetBool(player, cat2vars[cat].var); +} + +void +vlc_player_SetSubtitleTextScale(vlc_player_t *player, unsigned scale) +{ + assert(scale >= 10 && scale <= 500); + var_SetInteger(player, "sub-text-scale", scale); +} + +unsigned +vlc_player_GetSubtitleTextScale(vlc_player_t *player) +{ + return var_GetInteger(player, "sub-text-scale"); +} + +static void +vlc_player_SubtitleSyncMarkAudio(vlc_player_t *player) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + input->subsync.audio_time = vlc_tick_now(); + vlc_player_vout_OSDMessage(player, _("Sub sync: bookmarked audio time")); +} + +static void +vlc_player_SubtitleSyncMarkSubtitle(vlc_player_t *player) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + input->subsync.subtitle_time = vlc_tick_now(); + vlc_player_vout_OSDMessage(player, _("Sub sync: bookmarked subtitle time")); +} + +static void +vlc_player_SubtitleSyncApply(vlc_player_t *player) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + if (input->subsync.audio_time == VLC_TICK_INVALID || + input->subsync.subtitle_time == VLC_TICK_INVALID) + { + vlc_player_vout_OSDMessage(player, _("Sub sync: set bookmarks first!")); + return; + } + vlc_tick_t delay = + input->subsync.audio_time - input->subsync.subtitle_time; + input->subsync.audio_time = VLC_TICK_INVALID; + input->subsync.subtitle_time = VLC_TICK_INVALID; + vlc_player_SetSubtitleDelayInternal(player, delay, + VLC_PLAYER_WHENCE_RELATIVE); + + long long delay_ms = MS_FROM_VLC_TICK(delay); + long long totdelay_ms = MS_FROM_VLC_TICK(input->subtitle_delay + delay); + vlc_player_vout_OSDMessage(player, _("Sub sync: corrected %"PRId64 + " ms (total delay = %"PRId64" ms)"), + delay_ms, totdelay_ms); +} + +static void +vlc_player_SubtitleSyncReset(vlc_player_t *player) +{ + struct vlc_player_input *input = vlc_player_get_input_locked(player); + if (!input) + return; + vlc_player_SetSubtitleDelayInternal(player, 0, VLC_PLAYER_WHENCE_ABSOLUTE); + input->subsync.audio_time = VLC_TICK_INVALID; + input->subsync.subtitle_time = VLC_TICK_INVALID; + vlc_player_vout_OSDMessage(player, _("Sub sync: delay reset")); +} + +void +vlc_player_SetSubtitleSync(vlc_player_t *player, + enum vlc_player_subtitle_sync sync) +{ + switch (sync) + { + case VLC_PLAYER_SUBTITLE_SYNC_RESET: + vlc_player_SubtitleSyncReset(player); + break; + case VLC_PLAYER_SUBTITLE_SYNC_MARK_AUDIO: + vlc_player_SubtitleSyncMarkAudio(player); + break; + case VLC_PLAYER_SUBTITLE_SYNC_MARK_SUBTITLE: + vlc_player_SubtitleSyncMarkSubtitle(player); + break; + case VLC_PLAYER_SUBTITLE_SYNC_APPLY: + vlc_player_SubtitleSyncApply(player); + break; + default: + vlc_assert_unreachable(); + } +} + +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) +{ + assert(quality && 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; +} + +void +vlc_player_SetPauseOnCork(vlc_player_t *player, bool enabled) +{ + vlc_player_assert_locked(player); + player->pause_on_cork = enabled; +} + +static int +vlc_player_CorkCallback(vlc_object_t *this, const char *var, + vlc_value_t oldval, vlc_value_t newval, void *data) +{ + vlc_player_t *player = data; + + if (oldval.i_int == newval.i_int ) + return VLC_SUCCESS; + + vlc_player_Lock(player); + + if (player->pause_on_cork) + { + if (newval.i_int) + { + player->corked = player->global_state == VLC_PLAYER_STATE_PLAYING + || player->global_state == VLC_PLAYER_STATE_STARTED; + if (player->corked) + vlc_player_Pause(player); + } + else + { + if (player->corked) + { + vlc_player_Resume(player); + player->corked = false; + } + } + } + else + vlc_player_SendEvent(player, on_cork_changed, newval.i_int); + + vlc_player_Unlock(player); + + return VLC_SUCCESS; + (void) this; (void) var; +} + +audio_output_t * +vlc_player_aout_Hold(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 void +vlc_player_vout_OSDVolume(vlc_player_t *player, bool mute_action) +{ + size_t count; + vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count); + + bool mute = vlc_player_aout_IsMuted(player); + int volume = lroundf(vlc_player_aout_GetVolume(player) * 100.f); + if (mute_action && mute) + vouts_osd_Icon(vouts, count, OSD_MUTE_ICON); + else + { + if (vlc_player_vout_IsFullscreen(player)) + vouts_osd_Slider(vouts, count, volume, OSD_VERT_SLIDER); + vouts_osd_Message(vouts, count, _("Volume: %ld%%"), volume); + } + + vlc_player_vout_OSDReleaseAll(player, vouts, count); +} + +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); + vlc_player_vout_OSDVolume(player, false); + } + } + else if (strcmp(var, "mute") == 0) + { + if (oldval.b_bool != newval.b_bool) + { + vlc_player_aout_SendEvent(player, on_mute_changed, newval.b_bool); + vlc_player_vout_OSDVolume(player, true); + } + } + else + vlc_assert_unreachable(); + + return VLC_SUCCESS; + (void) this; +} + +float +vlc_player_aout_GetVolume(vlc_player_t *player) +{ + audio_output_t *aout = vlc_player_aout_Hold(player); + if (!aout) + return -1.f; + float vol = aout_VolumeGet(aout); + vlc_object_release(aout); + + return vol; +} + +int +vlc_player_aout_SetVolume(vlc_player_t *player, float volume) +{ + audio_output_t *aout = vlc_player_aout_Hold(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, int steps, float *result) +{ + audio_output_t *aout = vlc_player_aout_Hold(player); + if (!aout) + return -1; + int ret = aout_VolumeUpdate(aout, steps, result); + vlc_object_release(aout); + + return ret; +} + +int +vlc_player_aout_IsMuted(vlc_player_t *player) +{ + audio_output_t *aout = vlc_player_aout_Hold(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_aout_Hold(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_aout_Hold(player); + if (!aout) + return -1; + aout_EnableFilter(aout, name, add); + vlc_object_release(aout); + + return 0; +} + +vout_thread_t * +vlc_player_vout_Hold(vlc_player_t *player) +{ + return input_resource_HoldVout(player->resource); +} + +vout_thread_t ** +vlc_player_vout_HoldAll(vlc_player_t *player, size_t *count) +{ + vout_thread_t **vouts; + input_resource_HoldVouts(player->resource, &vouts, count); + return vouts; +} + +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) +{ + 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 bool +vout_osd_PrintVariableText(vout_thread_t *vout, const char *varname, int vartype, + vlc_value_t varval, const char *osdfmt) +{ + bool found = false; + bool isvarstring = vartype == VLC_VAR_STRING; + size_t num_choices; + vlc_value_t *choices; + char **choices_text; + var_Change(vout, varname, VLC_VAR_GETCHOICES, + &num_choices, &choices, &choices_text); + for (size_t i = 0; i < num_choices; ++i) + { + if (!found) + if ((isvarstring && + strcmp(choices[i].psz_string, varval.psz_string) == 0) || + (!isvarstring && choices[i].f_float == varval.f_float)) + { + vouts_osd_Message(&vout, 1, osdfmt, choices_text[i]); + found = true; + } + if (isvarstring) + free(choices[i].psz_string); + free(choices_text[i]); + } + free(choices); + free(choices_text); + return found; +} + +static int +vlc_player_VoutOSDCallback(vlc_object_t *this, const char *var, + vlc_value_t oldval, vlc_value_t newval, void *data) +{ + VLC_UNUSED(oldval); + + vout_thread_t *vout = (vout_thread_t *)this; + + if (strcmp(var, "aspect-ratio") == 0) + vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING, + newval, _("Aspect ratio: %s")); + + else if (strcmp(var, "autoscale") == 0) + vouts_osd_Message(&vout, 1, newval.b_bool ? + _("Scaled to screen") : _("Original size")); + + else if (strcmp(var, "crop") == 0) + vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING, newval, + _("Crop: %s")); + + else if (strcmp(var, "crop-bottom") == 0) + vouts_osd_Message(&vout, 1, _("Bottom crop: %d px"), newval.i_int); + + else if (strcmp(var, "crop-top") == 0) + vouts_osd_Message(&vout, 1, _("Top crop: %d px"), newval.i_int); + + else if (strcmp(var, "crop-left") == 0) + vouts_osd_Message(&vout, 1, _("Left crop: %d px"), newval.i_int); + + else if (strcmp(var, "crop-right") == 0) + vouts_osd_Message(&vout, 1, _("Right crop: %d px"), newval.i_int); + + else if (strcmp(var, "deinterlace") == 0 || + strcmp(var, "deinterlace-mode") == 0) + { + bool varmode = strcmp(var, "deinterlace-mode") == 0; + int on = !varmode ? + newval.i_int : var_GetInteger(vout, "deinterlace"); + char *mode = varmode ? + newval.psz_string : var_GetString(vout, "deinterlace-mode"); + vouts_osd_Message(&vout, 1, _("Deinterlace %s (%s)"), + on == 1 ? _("On") : _("Off"), mode); + free(mode); + } + + else if (strcmp(var, "sub-margin") == 0) + vouts_osd_Message(&vout, 1, _("Subtitle position %d px"), newval.i_int); + + else if (strcmp(var, "sub-text-scale") == 0) + vouts_osd_Message(&vout, 1, _("Subtitle text scale %d%%"), newval.i_int); + + else if (strcmp(var, "zoom") == 0) + { + if (newval.f_float == 1.f) + vouts_osd_Message(&vout, 1, _("Zooming reset")); + else + { + char const *fmt = _("Zoom: x%f"); + bool found = vout_osd_PrintVariableText(vout, var, VLC_VAR_FLOAT, + newval, fmt); + if (!found) + vouts_osd_Message(&vout, 1, fmt, newval.f_float); + } + } + + (void) data; + return VLC_SUCCESS; +} + +static void +vlc_player_vout_SetVar(vlc_player_t *player, const char *name, int type, + vlc_value_t val) +{ + var_SetChecked(player, name, type, val); + + size_t count; + vout_thread_t **vouts = vlc_player_vout_HoldAll(player, &count); + for (size_t i = 0; i < count; i++) + { + var_SetChecked(vouts[i], name, type, val); + vlc_object_release(vouts[i]); + } + free(vouts); +} + + +static void +vlc_player_vout_TriggerOption(vlc_player_t *player, const char *option) +{ + size_t count; + vout_thread_t **vouts = vlc_player_vout_HoldAll(player, &count); + for (size_t i = 0; i < count; ++i) + { + var_TriggerCallback(vouts[i], option); + vlc_object_release(vouts[i]); + } + free(vouts); +} + +void +vlc_player_vout_SetFullscreen(vlc_player_t *player, bool enabled) +{ + vlc_player_vout_SetVar(player, "fullscreen", VLC_VAR_BOOL, + (vlc_value_t) { .b_bool = 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_vout_SetVar(player, "video-wallpaper", VLC_VAR_BOOL, + (vlc_value_t) { .b_bool = enabled }); + vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed, NULL, enabled); +} + +static const char * +vlc_vout_filter_type_to_varname(enum vlc_vout_filter_type type) +{ + switch (type) + { + case VLC_VOUT_FILTER_VIDEO_SPLITTER: + return "video-splitter"; + case VLC_VOUT_FILTER_VIDEO_FILTER: + return "video-filter"; + case VLC_VOUT_FILTER_SUB_SOURCE: + return "sub-source"; + case VLC_VOUT_FILTER_SUB_FILTER: + return "sub-filter"; + default: + vlc_assert_unreachable(); + } +} + +void +vlc_player_vout_SetFilter(vlc_player_t *player, enum vlc_vout_filter_type type, + const char *value) +{ + const char *varname = vlc_vout_filter_type_to_varname(type); + vlc_player_vout_SetVar(player, varname, VLC_VAR_STRING, + (vlc_value_t) { .psz_string = (char *) value }); +} + +char * +vlc_player_vout_GetFilter(vlc_player_t *player, enum vlc_vout_filter_type type) +{ + const char *varname = vlc_vout_filter_type_to_varname(type); + return var_GetString(player, varname); +} + +void +vlc_player_vout_Snapshot(vlc_player_t *player) +{ + vlc_player_vout_TriggerOption(player, "video-snapshot"); +} + +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_aout_Hold(player); + if (aout) + { + var_DelCallback(aout, "volume", vlc_player_AoutCallback, player); + var_DelCallback(aout, "mute", vlc_player_AoutCallback, player); + var_DelCallback(player, "corks", vlc_player_CorkCallback, NULL); + 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->pause_on_cork = false; + player->corked = 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) + + /* player variables */ + VAR_CREATE("rate", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT); + VAR_CREATE("sub-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT); + VAR_CREATE("sub-text-scale", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT); + + /* vout variables */ + VAR_CREATE("video-splitter", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + VAR_CREATE("video-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + VAR_CREATE("sub-source", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + VAR_CREATE("sub-filter", VLC_VAR_STRING | 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); + + /* aout variables */ + VAR_CREATE("mute", VLC_VAR_BOOL); + VAR_CREATE("corks", VLC_VAR_INTEGER); + + /* es_out variables */ + VAR_CREATE("video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT); + VAR_CREATE("sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT); + VAR_CREATE("audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT); + VAR_CREATE("sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT); + VAR_CREATE("spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT); + VAR_CREATE("sout-spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT); + + /* 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); + var_AddCallback(player, "corks", vlc_player_CorkCallback, NULL); + 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); + var_DelCallback(player, "corks", vlc_player_AoutCallback, NULL); + } + 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 0000000000000000000000000000000000000000..cc10236a98a3537fed5bbf93d0d8adacc073583a --- /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 4beb6c0e02dc83543c8e56a6d3b1312356400eaf..3fea6e122f20b3dc0d432eaace2cc32d8deffc79 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -793,3 +793,113 @@ vlc_thumbnailer_RequestByTime vlc_thumbnailer_RequestByPos vlc_thumbnailer_Cancel vlc_thumbnailer_Release +vlc_player_AddAssociatedMedia +vlc_player_AddListener +vlc_player_aout_AddListener +vlc_player_aout_EnableFilter +vlc_player_aout_GetVolume +vlc_player_aout_Hold +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_DisplayPosition +vlc_player_DecrementRate +vlc_player_Delete +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_HasTeletextMenu +vlc_player_IncrementRate +vlc_player_InvalidateNextMedia +vlc_player_IsRecording +vlc_player_IsTeletextEnabled +vlc_player_IsTeletextTransparent +vlc_player_IsTrackCategoryEnabled +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_SelectNextTrack +vlc_player_SelectNextProgram +vlc_player_SelectPrevChapter +vlc_player_SelectPrevTitle +vlc_player_SelectPrevTrack +vlc_player_SelectPrevProgram +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_SetSubtitleSync +vlc_player_SetTeletextEnabled +vlc_player_SetTeletextTransparency +vlc_player_SetTrackCategoryEnabled +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_UpdateViewpoint +vlc_player_vout_AddListener +vlc_player_vout_GetFilter +vlc_player_vout_Hold +vlc_player_vout_HoldAll +vlc_player_vout_IsFullscreen +vlc_player_vout_IsWallpaperModeEnabled +vlc_player_vout_OSDMessage +vlc_player_vout_RemoveListener +vlc_player_vout_SetFilter +vlc_player_vout_SetFullscreen +vlc_player_vout_SetWallpaperModeEnabled +vlc_player_vout_Snapshot