/*****************************************************************************
 * 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 vlc_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
 */

/**
 * @defgroup vlc_player__instance Player instance
 * @{
 */

/**
 * Player opaque structure.
 */
typedef struct vlc_player_t vlc_player_t;

/**
 * Player lock type (normal or reentrant)
 */
enum vlc_player_lock_type
{
    /**
     * Normal lock
     *
     * If the player is already locked, subsequent calls to vlc_player_Lock()
     * will deadlock.
     */
    VLC_PLAYER_LOCK_NORMAL,

    /**
     * Reentrant lock
     *
     * If the player is already locked, subsequent calls to vlc_player_Lock()
     * will still succeed. To unlock the player, one call to
     * vlc_player_Unlock() per vlc_player_Lock() is necessary.
     */
    VLC_PLAYER_LOCK_REENTRANT,
};

/**
 * Create a new player instance
 *
 * @param parent parent VLC object
 * @param lock_type whether the player lock is reentrant or not
 * @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, enum vlc_player_lock_type lock_type);

/**
 * 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);

/**
 * Ask to start 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);

/**
 * Pause when reaching EOF
 *
 * @param player locked player instance
 * @param play_and_pause true to pause the player when reaching EOF
 */
VLC_API void
vlc_player_SetPlayAndPause(vlc_player_t *player, bool play_and_pause);

/**
 * Repeat playback when reaching EOF
 *
 * @param player locked player instance
 * @param repeat_count number of time to restart the same media
 */
VLC_API void
vlc_player_SetRepeatCount(vlc_player_t *player, unsigned repeat_count);

/**
 * 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);

/** @} vlc_player__instance */

/**
 * @defgroup vlc_player__playback Playback control
 * @{
 */

/**
 * 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_SetCategoryDelay()
 */
enum vlc_player_whence
{
    /** Given time/position */
    VLC_PLAYER_WHENCE_ABSOLUTE,
    /** The current position +/- the given time/position */
    VLC_PLAYER_WHENCE_RELATIVE,
};

/**
 * 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,
};

/**
 * A to B loop state
 */
enum vlc_player_abloop
{
    VLC_PLAYER_ABLOOP_NONE,
    VLC_PLAYER_ABLOOP_A,
    VLC_PLAYER_ABLOOP_B,
};

/**
 * Reason why the current media is stopping
 */
enum vlc_player_media_stopping_reason
{
    /** The media is stopping because of an error (default) */
    VLC_PLAYER_MEDIA_STOPPING_ERROR,
    /** The media reached the end of stream */
    VLC_PLAYER_MEDIA_STOPPING_EOS,
    /** The media is stopping because of a user request */
    VLC_PLAYER_MEDIA_STOPPING_USER,
};

/** 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)

enum vlc_player_restore_playback_pos
{
    VLC_PLAYER_RESTORE_PLAYBACK_POS_NEVER,
    VLC_PLAYER_RESTORE_PLAYBACK_POS_ASK,
    VLC_PLAYER_RESTORE_PLAYBACK_POS_ALWAYS,
};

/**
 * 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.
 *
 * @note The function will open the media, without starting it, allowing the
 * user to send controls (like seek requests) before Starting the player.
 *
 * @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);

/**
 * Set the next media
 *
 * This function replaces the next media to be played.
 * The user should set the next media from the
 * vlc_player_cbs.current_media_changed callback or anytime before the current
 * media is stopped.
 *
 * @note The media won't be opened directly by this function. If there is no
 * current media, the next media will be opened from vlc_player_Start(). If
 * there is a current playing media, the next media will be opened and played
 * automatically.
 *
 * @param player locked player instance
 * @param media next media to play (will be held by the player)
 */
VLC_API void
vlc_player_SetNextMedia(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);

/**
 * Get the next played media.
 *
 * This function return the media set by vlc_player_SetNextMedia()
 *
 * @param player locked player instance
 * @return a valid media or NULL (if no next media is set)
 */
VLC_API input_item_t *
vlc_player_GetNextMedia(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;
}

/**
 * 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. In case of success, the user should wait
 * for the STOPPED state event to know when the stop is finished.
 *
 * @param player locked player instance
 * @return VLC_SUCCESS if the player is being stopped, VLC_EGENERIC otherwise
 * (no-op)
 */
VLC_API int
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
 *
 * @note Works only on streams that can pause..
 *
 * @note listen to the vlc_player_cbs.on_next_frame_status to be notified when
 * the next frame is displayed.
 *
 * @param player locked player instance
 */
VLC_API void
vlc_player_NextVideoFrame(vlc_player_t *player);

/**
 * Pause and display the previous video frame
 *
 * @note Works only on streams that can pause, seek and pace.
 *
 * @note listen to the vlc_player_cbs.on_prev_frame_status to be notified when
 * the previous frame is displayed.
 *
 * @param player locked player instance
 */
VLC_API void
vlc_player_PreviousVideoFrame(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_player_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.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 double
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, double 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, double 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, double 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, double 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 uses and stores the
 * current time/position when called. The B time must be higher than the
 * A time.
 *
 * @param player locked player instance
 * @param abloop select which A/B cursor to set
 * @return VLC_SUCCESS or a VLC error code
 */
VLC_API int
vlc_player_SetAtoBLoop(vlc_player_t *player, enum vlc_player_abloop abloop);

/**
 * Enable A to B loop of the current media by setting start and end time
 *
 * The B time must be higher than the A time.
 *
 * @param player locked player instance
 * @param a_time start time for the loop
 * @param b_time end time for the loop
 * @return VLC_SUCCESS or a VLC error code
 */
VLC_API int
vlc_player_SetAtoBLoopTime(vlc_player_t *player, vlc_tick_t a_time, vlc_tick_t b_time);

/**
 * Enable A to B loop of the current media by setting start and end position
 *
 * The B position must be higher than the A position.
 *
 * @param player locked player instance
 * @param a_pos start position for the loop
 * @param b_pos end position for the loop
 * @return VLC_SUCCESS or a VLC error code
 */
VLC_API int
vlc_player_SetAtoBLoopPosition(vlc_player_t *player, double a_pos, double b_pos);

/**
 * Reset/remove the A to B loop of the current media
 *
 * @param player locked player instance
 * @return VLC_SUCCESS or a VLC error code
 */
VLC_API int
vlc_player_ResetAtoBLoop(vlc_player_t *player);

/**
 * 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, double *a_pos,
                       vlc_tick_t *b_time, double *b_pos);

/**
 * 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
  */
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
 * @param dir_path path of the recording directory or NULL (use default path),
 * has only an effect when first enabling recording.
 */
VLC_API void
vlc_player_SetRecordingEnabled(vlc_player_t *player, bool enabled,
                               const char *dir_path);

/**
 * Helper to toggle the recording state
 */
static inline void
vlc_player_ToggleRecording(vlc_player_t *player)
{
    vlc_player_SetRecordingEnabled(player, !vlc_player_IsRecording(player), NULL);
}

/**
 * Add an associated (or external) media to the current media
 *
 * @param player locked player instance
 * @param cat SPU_ES or UNKNOWN_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);

/**
 * Get the signal quality and strength of the current media
 *
 * @param player locked player instance
 * @param quality a pointer that will be assigned with the signal quality
 * @param strength a pointer that will be assigned with the signal strength
 * @retval VLC_SUCCESS when quality and strength have been assigned
 * @retval VLC_EGENERIC in case of error (strength and quality are not assigned)
 */
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);

/**
 * Restore the previous playback position of the current media
 */
VLC_API void
vlc_player_RestorePlaybackPos(vlc_player_t *player);

/**
 * Get the V4L2 object used to do controls
 *
 * @param player locked player instance
 * @return the V4L2 object or NULL if not any. This object must be used with
 * the player lock held.
 */
VLC_API vlc_object_t *
vlc_player_GetV4l2Object(vlc_player_t *player) VLC_DEPRECATED;

/** @} vlc_player__playback */

/**
 * @defgroup vlc_player__titles Title and chapter control
 * @{
 */

/**
 * 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
/** vlc_player_title.flags: The title is the main one. */
#define VLC_PLAYER_TITLE_MAIN         0x04

/** 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;

/**
 * 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 titles a valid title list
 * @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);

/**
 * 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_chapter_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);

/** @} vlc_player__titles */

/**
 * @defgroup vlc_player__programs Program control
 * @{
 */

/**
 * 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;
};

/**
 * 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);

/**
 * 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
 *
 * This function can be used to pre-select a program by its id before starting
 * the player. It has only effect for the current media. It can also be used
 * when the player is already started.
 *
 * @note Selecting a non-existing program will cause the player to no select
 * any programs. Therefore, all tracks will be disabled.
 *
 * @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);

/**
 * Helper to get the current selected program
 */
static inline const struct vlc_player_program *
vlc_player_GetSelectedProgram(vlc_player_t *player)
{
    size_t count = vlc_player_GetProgramCount(player);
    for (size_t i = 0; i < count; ++i)
    {
        const struct vlc_player_program *program =
            vlc_player_GetProgramAt(player, i);
        assert(program);
        if (program->selected)
            return program;
    }
    return NULL;
}

/** @} vlc_player__programs */

/**
 * @defgroup vlc_player__tracks Tracks control
 * @{
 */

/**
 * Player selection policy
 *
 * @see vlc_player_SelectEsId()
 */
enum vlc_player_select_policy
{
    /**
     * Only one track per category is selected. Selecting a track with this
     * policy will disable all other tracks for the same category.
     */
    VLC_PLAYER_SELECT_EXCLUSIVE,
    /**
     * Select multiple tracks for one category.
     *
     * Only one audio track can be selected at a time.
     * Two subtitle tracks can be selected simultaneously.
     * Multiple video tracks can be selected simultaneously.
     */
    VLC_PLAYER_SELECT_SIMULTANEOUS,
};

/**
 * Player track structure.
 *
 * A track is a representation of an ES identifier at a given time. Once the
 * player is unlocked, all content except the es_id pointer can be updated.
 *
 * @see vlc_player_cbs.on_track_list_changed
 * @see vlc_player_GetTrack
 */
struct vlc_player_track
{
    /** Id used for any player actions, like vlc_player_SelectEsId() */
    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;
};

/**
 * 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.
 *
 * @warning The returned track won't be updated if the original one is modified
 * by the player.
 *
 * @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);

/**
 * 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 es_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);

/**
 * Get and the video output used by a ES identifier
 *
 * @warning A same vout can be associated with multiple ES during the lifetime
 * of the player. The information returned by this function becomes invalid
 * when the player is unlocked. The returned vout doesn't need to be released,
 * but must be held with vout_Hold() if it is accessed after the player is
 * unlocked.
 *
 * @param player locked player instance
 * @param es_id an ES ID (retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 * @param order if not null, the order of the vout
 * @return a valid vout or NULL (if the track is disabled, it it's not a video
 * or spu track, or if the vout failed to start)
 */
VLC_API vout_thread_t *
vlc_player_GetEsIdVout(vlc_player_t *player, vlc_es_id_t *es_id,
                       enum vlc_vout_order *order);

/**
 * Get the ES identifier of a video output
 *
 * @warning A same vout can be associated with multiple ES during the lifetime
 * of the player. The information returned by this function becomes invalid
 * when the player is unlocked. The returned es_id doesn't need to be released,
 * but must be held with vlc_es_id_Hold() if it accessed after the player is
 * unlocked.
 *
 * @param player locked player instance
 * @param vout vout (can't be NULL)
 * @return a valid ES identifier or NULL (if the vout is stopped)
 */
VLC_API vlc_es_id_t *
vlc_player_GetEsIdFromVout(vlc_player_t *player, vout_thread_t *vout);

/**
 * 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 tracks by their string identifier
 *
 * This function can be used to pre-select a list of tracks before starting the
 * player. It has only effect for the current media. It can also be used when
 * the player is already started.

 * 'str_ids' can contain more than one track id, delimited with ','. "" or any
 * invalid track id will cause the player to unselect all tracks of that
 * category. NULL will disable the preference for newer tracks without
 * unselecting any current tracks.
 *
 * Example:
 * - (VIDEO_ES, "video/1,video/2") will select these 2 video tracks. If there
 * is only one video track with the id "video/0", no tracks will be selected.
 * - (SPU_ES, "${slave_url_md5sum}/spu/0) will select one spu added by an input
 * slave with the corresponding url.
 *
 * @note The string identifier of a track can be found via vlc_es_id_GetStrId().
 *
 * @param player locked player instance
 * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
 * @param str_ids list of string identifier or NULL
 */
VLC_API void
vlc_player_SelectTracksByStringIds(vlc_player_t *player,
                                   enum es_format_category_e cat,
                                   const char *str_ids);

/**
 * 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 es_id an ES ID (retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 * @param policy exclusive or simultaneous
 * @return the number of track selected for es_id category
 */
VLC_API unsigned
vlc_player_SelectEsId(vlc_player_t *player, vlc_es_id_t *es_id,
                      enum vlc_player_select_policy policy);


/**
 * Helper to select a track
 */
static inline unsigned
vlc_player_SelectTrack(vlc_player_t *player,
                       const struct vlc_player_track *track,
                       enum vlc_player_select_policy policy)
{
    return vlc_player_SelectEsId(player, track->es_id, policy);
}

/**
 * Select multiple tracks from a list of ES identifiers.
 *
 * Any tracks of the category, not referenced in the list will be unselected.
 *
 * @warning there is no guarantee all requested tracks will be selected. The
 * behaviour is undefined if the list is not null-terminated.
 *
 * @note A successful call will trigger the
 * vlc_player_cbs.on_track_selection_changed event for each track that has
 * its selection state changed.
 *
 * @see VLC_PLAYER_SELECT_SIMULTANEOUS
 *
 * @param player locked player instance
 * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
 * @param es_id_list a null-terminated list of ES identifiers. es_ids not
 * corresponding to the category will be ignored.
 * (ES IDs can be retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 * @return the number of track selected for that category
 */
VLC_API unsigned
vlc_player_SelectEsIdList(vlc_player_t *player,
                          enum es_format_category_e cat,
                          vlc_es_id_t *const es_id_list[]);


/**
 * Cycle through the tracks
 *
 * 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.
 * Unless called on the PRIMARY with a SECONDARY selected, it will cycle through
 *
 * @param player locked player instance
 * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
 * @param next the cycle order
 */
VLC_API void
vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat,
                      enum vlc_vout_order vout_order, bool next);

/**
 * Helper to 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.
 * Unless called on the PRIMARY with a SECONDARY selected, it will cycle through
 *
 * @param player locked player instance
 * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
 */
static inline void
vlc_player_SelectNextTrack(vlc_player_t *player, enum es_format_category_e cat,
                           enum vlc_vout_order vout_order)
{
    vlc_player_CycleTrack(player, cat, vout_order, true);
}

/**
 * Helper to 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.
 * Unless called on the PRIMARY with a SECONDARY selected, it will cycle through
 *
 * @param player locked player instance
 * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
 */
static inline void
vlc_player_SelectPrevTrack(vlc_player_t *player, enum es_format_category_e cat,
                           enum vlc_vout_order vout_order)
{
    vlc_player_CycleTrack(player, cat, vout_order, false);
}

/**
 * Unselect a track from an ES identifier
 *
 * @warning Other tracks of the same category won't be touched.
 *
 * @note A successful call will trigger the
 * vlc_player_cbs.on_track_selection_changed event.
 *
 * @param player locked player instance
 * @param es_id an ES ID (retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 */
VLC_API void
vlc_player_UnselectEsId(vlc_player_t *player, vlc_es_id_t *es_id);

/**
 * Helper to unselect a track
 */
static inline void
vlc_player_UnselectTrack(vlc_player_t *player,
                         const struct vlc_player_track *track)
{
    vlc_player_UnselectEsId(player, track->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);
    }
}

/**
 * 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 es_id an ES ID (retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 */
VLC_API void
vlc_player_RestartEsId(vlc_player_t *player, vlc_es_id_t *es_id);

/**
 * Helper to restart a track
 */
static inline void
vlc_player_RestartTrack(vlc_player_t *player,
                        const struct vlc_player_track *track)
{
    vlc_player_RestartEsId(player, track->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);
    }
}

/**
 * Select the language for an ES category
 *
 * @warning The language will only be set for all future played media.
 *
 * @param player locked player instance
 * @param cat AUDIO_ES or SPU_ES
 * @param lang comma separated, two or three letters country code, 'any' as a
 * fallback or NULL to reset the default state
 */
VLC_API void
vlc_player_SelectCategoryLanguage(vlc_player_t *player,
                                  enum es_format_category_e cat,
                                  const char *lang);

/**
 * Get the language of an ES category
 *
 * @warning This only reflects the change made by
 * vlc_player_SelectCategoryLanguage(). The current playing track doesn't
 * necessarily correspond to the returned language.
 *
 * @see vlc_player_SelectCategoryLanguage
 *
 * @param player locked player instance
 * @param cat AUDIO_ES or SPU_ES
 * @return valid language or NULL, need to be freed
 */
VLC_API char *
vlc_player_GetCategoryLanguage(vlc_player_t *player,
                               enum es_format_category_e cat);

/**
 * Helper to select the audio language
 */
static inline void
vlc_player_SelectAudioLanguage(vlc_player_t *player, const char *lang)
{
    vlc_player_SelectCategoryLanguage(player, AUDIO_ES, lang);
}

/**
 * Helper to select the subtitle language
 */
static inline void
vlc_player_SelectSubtitleLanguage(vlc_player_t *player, const char *lang)
{
    vlc_player_SelectCategoryLanguage(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);
}

/**
 * Helper to toggle subtitles
 */
static inline void
vlc_player_ToggleSubtitle(vlc_player_t *player)
{
    bool enabled = !vlc_player_IsSubtitleEnabled(player);
    vlc_player_SetSubtitleEnabled(player, enabled);
}

/**
 * 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);

/** @} vlc_player__tracks */

/**
 * @defgroup vlc_player__tracks_sync Tracks synchronisation (delay)
 * @{
 */

/**
 * Get the delay of an ES category for the current media
 *
 * @see vlc_player_cbs.on_category_delay_changed
 *
 * @param player locked player instance
 * @param cat AUDIO_ES or SPU_ES (VIDEO_ES not supported yet)
 * @return a valid delay or 0
 */
VLC_API vlc_tick_t
vlc_player_GetCategoryDelay(vlc_player_t *player, enum es_format_category_e cat);

/**
 * Set the delay of one category for the current media
 *
 * @note A successful call will trigger the
 * vlc_player_cbs.on_category_delay_changed event.
 *
 * @warning This has no effect on tracks where the delay was set by
 * vlc_player_SetEsIdDelay()
 *
 * @param player locked player instance
 * @param cat AUDIO_ES or SPU_ES (VIDEO_ES not supported yet)
 * @param delay a valid time
 * @param whence absolute or relative
 * @return VLC_SUCCESS or VLC_EGENERIC if the category is not handled
 */
VLC_API int
vlc_player_SetCategoryDelay(vlc_player_t *player, enum es_format_category_e cat,
                            vlc_tick_t delay, enum vlc_player_whence whence);

/**
 * Get the delay of a track
 *
 * @see vlc_player_cbs.on_track_delay_changed
 *
 * @param player locked player instance
 * @param es_id an ES ID (retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 * @return a valid delay or INT64_MAX is no delay is set for this track
 */
VLC_API vlc_tick_t
vlc_player_GetEsIdDelay(vlc_player_t *player, vlc_es_id_t *es_id);

/**
 * Set the delay of one track
 *
 * @note A successful call will trigger the
 * vlc_player_cbs.on_track_delay_changed event.
 *
 * @warning Setting the delay of one specific track will override previous and
 * future changes of delay made by vlc_player_SetCategoryDelay()
 *
 * @param player locked player instance
 * @param es_id an ES ID (retrieved from vlc_player_cbs.on_track_list_changed or
 * vlc_player_GetTrackAt())
 * @param delay a valid time or INT64_MAX to use default category delay
 * @param whence absolute or relative
 * @return VLC_SUCCESS or VLC_EGENERIC if the category of the es_id is not
 * handled (VIDEO_ES not supported yet)
 */
VLC_API int
vlc_player_SetEsIdDelay(vlc_player_t *player, vlc_es_id_t *es_id,
                        vlc_tick_t delay, enum vlc_player_whence whence);

/**
 * Helper to get the audio delay
 */
static inline vlc_tick_t
vlc_player_GetAudioDelay(vlc_player_t *player)
{
    return vlc_player_GetCategoryDelay(player, AUDIO_ES);
}

/**
 * Helper to set the audio delay
 */
static inline void
vlc_player_SetAudioDelay(vlc_player_t *player, vlc_tick_t delay,
                         enum vlc_player_whence whence)

{
    vlc_player_SetCategoryDelay(player, AUDIO_ES, delay, whence);
}

/**
 * Helper to get the audio delay
 */
static inline vlc_tick_t
vlc_player_GetVideoDelay(vlc_player_t *player)
{
    return vlc_player_GetCategoryDelay(player, VIDEO_ES);
}

/**
 * Helper to set the audio delay
 */
static inline void
vlc_player_SetVideoDelay(vlc_player_t *player, vlc_tick_t delay,
                         enum vlc_player_whence whence)

{
    vlc_player_SetCategoryDelay(player, VIDEO_ES, delay, whence);
}

/**
 * Helper to get the subtitle delay
 */
static inline vlc_tick_t
vlc_player_GetSubtitleDelay(vlc_player_t *player)
{
    return vlc_player_GetCategoryDelay(player, SPU_ES);
}

/**
 * Helper to set the subtitle delay
 */
static inline void
vlc_player_SetSubtitleDelay(vlc_player_t *player, vlc_tick_t delay,
                            enum vlc_player_whence whence)
{
    vlc_player_SetCategoryDelay(player, SPU_ES, delay, whence);
}

/**
 * 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);

/** @} vlc_player__tracks_sync */

/**
 * @defgroup vlc_player__teletext Teletext control
 * @{
 */

/**
 * 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);

/** @} vlc_player__teletext */

/**
 * @defgroup vlc_player__renderer External renderer control
 * @{
 */

/**
 * 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);

/** @} vlc_player__renderer */

/**
 * @defgroup vlc_player__metadata Metadata callbacks
 * @{
 */

/**
 * Player metadata listener opaque structure.
 *
 * This opaque structure is returned by vlc_player_AddMetadataListener() and
 * can be used to remove the listener via
 * vlc_player_RemoveMetadataListener().
 */
typedef struct vlc_player_metadata_listener_id vlc_player_metadata_listener_id;

/**
 * Player metadata option
 */
enum vlc_player_metadata_option
{
    /**
     * Ask for momentary loudness measurement
     *
     * Very low CPU usage.
     * @see vlc_player_metadata_cbs.on_momentary_loudness_changed
     */
    VLC_PLAYER_METADATA_LOUDNESS_MOMENTARY,

    /**
     * Ask for all loudness measurements
     *
     * High CPU usage.
     * @see vlc_player_metadata_cbs.on_loudness_changed
     */
    VLC_PLAYER_METADATA_LOUDNESS_FULL,
};

/**
 * Player metadata callbacks
 *
 * Can be registered with vlc_player_AddMetadataListener().
 *
 * @warning To avoid deadlocks, users should never call vlc_player_t functions
 * from these callbacks.
 */
union vlc_player_metadata_cbs
{
    /**
     * Called when the momentary loudness measurement have changed
     *
     * @see VLC_PLAYER_METADATA_LOUDNESS_MOMEMTARY
     *
     * Only sent when audio is playing, approximately every 400ms (but can be
     * higher, depending on the input sample size).
     *
     * @param date Absolute date of the measurement. It is most likely in the
     * future (0 to 2seconds) depending on the audio output buffer size.
     * @param momentary_loudness Momentary loudness
     * @param data opaque pointer set by vlc_player_AddMetadataListener()
     */
    void (*on_momentary_loudness_changed)(vlc_tick_t date,
                                          double momentary_loudness,
                                          void *data);

    /**
     * Called when loudness measurements have changed
     *
     * @see VLC_PLAYER_METADATA_LOUDNESS_FULL
     *
     * Only sent when audio is playing, approximately every 400ms (but can be
     * higher, depending on the input sample size).
     *
     * @param date Absolute date of the measurement. It is most likely in the
     * future (0 to 2seconds) depending on the audio output buffer size.
     * @param loudness loudness measurement
     * @param data opaque pointer set by vlc_player_AddMetadataListener()
     */
    void (*on_loudness_changed)(vlc_tick_t date,
                                const struct vlc_audio_loudness *loudness,
                                void *data);
};

/**
 * Add a metadata listener
 *
 * @note Every registered loudness meter need to be removed by the caller with
 * vlc_player_RemoveMetadataListener().
 *
 * @param player locked player instance
 * @param option select which metadata to listen
 * @param cbs pointer to a vlc_player_metadata_cbs union, 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 error (plugin missing)
 */
VLC_API vlc_player_metadata_listener_id *
vlc_player_AddMetadataListener(vlc_player_t *player,
                               enum vlc_player_metadata_option option,
                               const union vlc_player_metadata_cbs *cbs,
                               void *cbs_data);

/**
 * Remove a metadata listener
 *
 * @param player player instance
 * @param listener_id listener id returned by vlc_player_AddMetadataListener()
 */
VLC_API void
vlc_player_RemoveMetadataListener(vlc_player_t *player,
                                  vlc_player_metadata_listener_id *listener_id);


/** @} vlc_player__metadata */

/**
 * @defgroup vlc_player__aout Audio output control
 * @{
 */

/**
 * 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 aout callbacks
 *
 * Can be registered with vlc_player_aout_AddListener().
 *
 * @warning To avoid deadlocks, users should never call audio_output_t and
 * vlc_player_t functions from these callbacks.
 */
struct vlc_player_aout_cbs
{
    /**
     * Called when the volume has changed
     *
     * @see vlc_player_aout_SetVolume()
     *
     * @param aout the main aout of the player
     * @param new_volume volume in the range [0;2.f]
     * @param data opaque pointer set by vlc_player_aout_AddListener()
     */
    void (*on_volume_changed)(audio_output_t *aout, float new_volume,
        void *data);

    /**
     * Called when the mute state has changed
     *
     * @see vlc_player_aout_Mute()
     *
     * @param aout the main aout of the player
     * @param new_mute true if muted
     * @param data opaque pointer set by vlc_player_aout_AddListener()
     */
    void (*on_mute_changed)(audio_output_t *aout, bool new_muted,
        void *data);

    /**
     * Called when the audio device has changed
     *
     * @param aout the main aout of the player
     * @param device the device name
     * @param data opaque pointer set by vlc_player_aout_AddListener()
     */
    void (*on_device_changed)(audio_output_t *aout, const char *device,
        void *data);
};

/**
 * Get the audio output
 *
 * @warning The returned pointer must be released with aout_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);

/**
 * Reset the main audio output
 *
 * @warning The main aout can only by reset if it is not currently used by any
 * decoders (before any play).
 *
 * @param player player instance
 */
VLC_API void
vlc_player_aout_Reset(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);

/**
 * Helper to toggle the mute state
 */
static inline int
vlc_player_aout_ToggleMute(vlc_player_t *player)
{
    return vlc_player_aout_Mute(player,
                                !vlc_player_aout_IsMuted(player));
}

/**
 * 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);

/** @} vlc_player__aout */

/**
 * @defgroup vlc_player__vout Video output control
 * @{
 */

/**
 * 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;

/**
 * action of vlc_player_cbs.on_vout_changed callback
 */
enum vlc_player_vout_action
{
    VLC_PLAYER_VOUT_STARTED,
    VLC_PLAYER_VOUT_STOPPED,
};

/**
 * Player vout callbacks
 *
 * Can be registered with vlc_player_vout_AddListener().
 *
 * @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 and
 * vlc_player_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 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)(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 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)(vout_thread_t *vout, bool enabled,
        void *data);
};


/**
 * Get and hold the main video output
 *
 * @warning the returned vout_thread_t * must be released with vout_Release().
 * @see vlc_player_cbs.on_vout_changed
 *
 * @note The player is guaranteed to always hold one valid vout. Only vout
 * variables can be changed from this instance. The vout returned before
 * playback is not necessarily the same one that will be used for playback.
 *
 * @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
 * vout_Release(). The returned array must be freed.
 *
 * @see vlc_player_cbs.on_vout_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));
}

/**
 * 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_osd_Message(vlc_player_t *player, const char *fmt, ...);

/** @} vlc_player__vout */

/**
 * @defgroup vlc_player__events Player events
 * @{
 */

/**
 * 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;

/**
 * Action of vlc_player_cbs.on_track_list_changed,
 * vlc_player_cbs.on_program_list_changed callbacks
 */
enum vlc_player_list_action
{
    VLC_PLAYER_LIST_ADDED,
    VLC_PLAYER_LIST_REMOVED,
    VLC_PLAYER_LIST_UPDATED,
};

/**
 * 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).
     *
     * The user could set the next media via vlc_player_SetNextMedia() from
     * the current callback or anytime before the current media is stopped.

     * @see vlc_player_SetCurrentMedia()
     *
     * @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 or stopping.
     * Can be called during playback.
     *
     * @param player locked player instance
     * @param old_caps old player capabilities
     * @param new_caps new player capabilities
     * @param data opaque pointer set by vlc_player_AddListener()
     */
    void (*on_capabilities_changed)(vlc_player_t *player,
        int old_caps, 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, double 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 track delay has changed
     *
     * @param player locked player instance
     * @param es_id valid track id
     * @param delay a valid delay or INT64_MAX if the delay of this track is
     * canceled
     */
    void (*on_track_delay_changed)(vlc_player_t *player,
        vlc_es_id_t *es_id, vlc_tick_t delay, 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 category delay has changed for the current media
     *
     * @see vlc_player_SetCategoryDelay()
     *
     * @param player locked player instance
     * @param cat AUDIO_ES or SPU_ES
     * @param new_delay audio delay
     * @param data opaque pointer set by vlc_player_AddListener()
     */
    void (*on_category_delay_changed)(vlc_player_t *player,
         enum es_format_category_e cat, 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, double pos,
        void *data);

    /**
     * Called when the media meta and/or info 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, const input_item_node_t *new_subitems, void *data);

    /**
     * Called when new attachments are added to the media
     *
     * @note It can be called several times for one parse request. The array
     * contains only new elements after a second call.
     *
     * @param player locked player instance
     * @param media current media
     * @param array valid array containing new elements, should only be used
     * within the callback. One and all elements can be held and stored on a
     * new variable or new array.
     * @param count number of elements in the array
     * @param data opaque pointer set by vlc_player_AddListener()
     */
    void (*on_media_attachments_added)(vlc_player_t *player,
        input_item_t *media, input_attachment_t *const *array, size_t count,
        void *data);

    /**
     * Called when a vout is started or stopped
     *
     * @note In case, several media with only one video track are played
     * successively, the same vout instance will be started and stopped several
     * time.
     *
     * @param player locked player instance
     * @param action started or stopped
     * @param vout vout (can't be NULL)
     * @param order vout order
     * @param es_id the ES id associated with this vout
     * @param data opaque pointer set by vlc_player_AddListener()
     */
    void (*on_vout_changed)(vlc_player_t *player,
        enum vlc_player_vout_action action, vout_thread_t *vout,
        enum vlc_vout_order order, vlc_es_id_t *es_id, 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);

    /**
     * Called to query the user about restoring the previous playback position
     *
     * If this callback isn't provided, the user won't be asked to restore
     * the previous playback position, effectively causing
     * VLC_PLAYER_RESTORE_PLAYBACK_POS_ASK to be handled as
     * VLC_PLAYER_RESTORE_PLAYBACK_POS_NEVER
     *
     * The implementation can react to this callback by calling
     * vlc_player_RestorePlaybackPos(), or by discarding the event.
     *
     * @param player locked player instance
     * @param data opaque pointer set by vlc_player_AddListener()
     */
    void (*on_playback_restore_queried)(vlc_player_t *player, void *data);

    /**
     * Called when the player will stop the current media.
     *
     * @note This can be called from the PLAYING state, before the
     * player requests the next media, or from the STOPPING state, ie.
     * when the player is stopping, or by an internal transition
     * (e.g., when the media reaches the end of file or errors out).
     *
     * @see vlc_player_SetCurrentMedia()
     * @see vlc_player_Stop()
     *
     * @param player locked player instance
     * @param current_media media currently stopping
     * @param stopping_reason reason why the media is stopping
     * @param data opaque pointer set by vlc_player_AddListener()
     */
    void (*on_stopping_current_media)(vlc_player_t *player, input_item_t *current_media,
                                      enum vlc_player_media_stopping_reason stopping_reason,
                                      void *data);

    /**
     * Called when the next frame, following a call to
     * `vlc_player_NextVideoFrame()`, is displayed.
     *
     * @see vlc_player_NextVideoFrame()
     *
     * @param player locked player instance
     * @param status 0 in case of success, -EAGAIN on first call (paused),
     * -EBUSY in case of video error, -ENOTSUP if can't pause,
     * -EINVAL in case of invalid state
     */
    void (*on_next_frame_status)(vlc_player_t *player, int status, void *data);

    /**
    * Called when the previous frame, following a call to
    * `vlc_player_PreviousVideoFrame()`, is displayed.
    *
    * @see vlc_player_PreviousVideoFrame()
    *
    * @param player locked player instance
    * @param status 0 in case of success,
    * -EAGAIN on first call (paused) or on first frame,
    * -EBUSY in case of video error,
    * -ENOTSUP if can't pause/seek/pace,
    * -EINVAL in case of invalid state,
    * -ERANGE if the player could not seek back
    */
    void (*on_prev_frame_status)(vlc_player_t *player, int status, void *data);
};

/**
 * 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);

/** @} vlc_player__events */

/**
 * @defgroup vlc_player__timer Player timer
 * @{
 */

/**
 * Player timer opaque structure.
 */
typedef struct vlc_player_timer_id vlc_player_timer_id;

/**
 * Player timer point
 *
 * @see vlc_player_timer_cbs.on_update
 */
struct vlc_player_timer_point
{
    /** Position in the range [0.0f;1.0] */
    double position;
    /** Rate of the player */
    double rate;
    /** Valid time >= VLC_TICK_0 or VLC_TICK_INVALID, subtract this time with
     * VLC_TICK_0 to get the original value. */
    vlc_tick_t ts;
    /** Valid length >= VLC_TICK_0 or VLC_TICK_INVALID */
    vlc_tick_t length;
    /** if true, length represents the seek range and position is the position
     * within that seek_range */
    bool live;
    /** System date of this record (always valid), this date can be in the
     * future or in the past. The special value of INT64_MAX mean that the
     * clock was paused when this point was updated. In that case,
     * vlc_player_timer_point_Interpolate() will return the current ts/pos of
     * this point (there is nothing to interpolate). */
    vlc_tick_t system_date;
};

/**
 * Player smpte timecode
 *
 * @see vlc_player_timer_smpte_cbs
 */
struct vlc_player_timer_smpte_timecode
{
    /** Hours [0;n] */
    unsigned hours;
    /** Minutes [0;59] */
    unsigned minutes;
    /** Seconds [0;59] */
    unsigned seconds;
    /** Frame number [0;n] */
    unsigned frames;
    /** Maximum number of digits needed to display the frame number */
    unsigned frame_resolution;
    /** True if the source is NTSC 29.97fps or 59.94fps DF */
    bool drop_frame;
};

/**
 * Player timer callbacks
 *
 * @see vlc_player_AddTimer
 */
struct vlc_player_timer_cbs
{
    /**
     * Called when the state or the time changed (mandatory).
     *
     * Get notified when the time is updated by the input or output source. The
     * input source is the 'demux' or the 'access_demux'. The output source are
     * audio and video outputs: an update is received each time a video frame
     * is displayed or an audio sample is written. The delay between each
     * updates may depend on the input and source type (it can be every 5ms,
     * 30ms, 1s or 10s...). The user of this timer may need to update the
     * position at a higher frequency from its own mainloop via
     * vlc_player_timer_point_Interpolate().
     *
     * @warning The player is not locked from this callback. It is forbidden
     * to call any player functions from here.
     *
     * @param value always valid, the time corresponding to the state
     * @param data opaque pointer set by vlc_player_AddTimer()
     */
    void (*on_update)(const struct vlc_player_timer_point *value, void *data);

    /**
     * The player timer is paused (can be NULL).
     *
     * This event is sent when the player is paused or stopping. The player
     * user should stop its "interpolate" timer.
     *
     * @note on_update() can be called when paused for those 2 reasons:
     * - playback is resumed (vlc_player_timer_point.system_date is valid)
     * - a track, likely video (next-frame) is outputted when paused
     *   (vlc_player_timer_point.system_date = INT64_MAX)
     *
     * @warning The player is not locked from this callback. It is forbidden
     * to call any player functions from here.
     *
     * @param system_date system date of this event, not valid when stopped. It
     * can be used to interpolate the last updated point to this date in order
     * to get the last paused ts/position.
     * @param data opaque pointer set by vlc_player_AddTimer()
     */
    void (*on_paused)(vlc_tick_t system_date, void *data);

    /**
     * Called when the player is seeking or finished seeking (can be NULL).
     *
     * @warning The player is not locked from this callback. It is forbidden
     * to call any player functions from here.
     *
     * @note on_update() can be called when seeking. It corresponds to tracks
     * updating their points prior to receiving the asynchronous seek event.
     * The user could discard them manually.
     *
     * @param value point of the seek request or NULL when seeking is finished
     * @param data opaque pointer set by vlc_player_AddTimer()
     */
    void (*on_seek)(const struct vlc_player_timer_point *value, void *data);
};

/**
 * Player smpte timer callbacks
 *
 * @see vlc_player_AddSmpteTimer
 */
struct vlc_player_timer_smpte_cbs
{
    /**
     * Called when a new frame is displayed
     *
     * @warning The player is not locked from this callback. It is forbidden
     * to call any player functions from here.
     *
     * @param tc always valid, the timecode corresponding to the frame just
     * displayed
     * @param data opaque pointer set by vlc_player_AddTimer()
     */
    void (*on_update)(const struct vlc_player_timer_smpte_timecode *tc,
                      void *data);

    /**
     * The player timer is paused (can be NULL).
     *
     * @see vlc_player_timer_cbs.on_paused
     */
    void (*on_paused)(vlc_tick_t system_date, void *data);

    /**
     * Called when the player is seeking or finished seeking (can be NULL).
     *
     * @warning The player is not locked from this callback. It is forbidden
     * to call any player functions from here.
     *
     * @warning the value is parameter is a timestamp point and not a timecode,
     * as this is an approximation.
     *
     * @note on_update() can be called when seeking. It corresponds to tracks
     * updating their points prior to receiving the asynchronous seek event.
     * The user could discard them manually.
     *
     * @param value point of the seek request or NULL when seeking is finished
     * @param data opaque pointer set by vlc_player_AddTimer()
     */
    void (*on_seek)(const struct vlc_player_timer_point *value, void *data);
};

/**
 * Add a timer in order to get times updates
 *
 * @param player player instance (locked or not)
 * @param min_period corresponds to the minimum period between each updates,
 * use it to avoid flood from too many source updates, set it to
 * VLC_TICK_INVALID to receive all updates.
 * @param cbs pointer to a vlc_player_timer_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 vlc_player_timer_id or NULL in case of memory allocation
 * error
 */
VLC_API vlc_player_timer_id *
vlc_player_AddTimer(vlc_player_t *player, vlc_tick_t min_period,
                    const struct vlc_player_timer_cbs *cbs,
                    void *cbs_data);

/**
 * Add a smpte timer in order to get accurate video frame updates
 *
 * @param player player instance (locked or not)
 * @param cbs pointer to a vlc_player_timer_smpte_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 vlc_player_timer_id or NULL in case of memory allocation
 * error
 */
VLC_API vlc_player_timer_id *
vlc_player_AddSmpteTimer(vlc_player_t *player,
                         const struct vlc_player_timer_smpte_cbs *cbs,
                         void *cbs_data);

/**
 * Remove a player timer
 *
 * @param player player instance (locked or not)
 * @param timer timer created by vlc_player_AddTimer()
 */
VLC_API void
vlc_player_RemoveTimer(vlc_player_t *player, vlc_player_timer_id *timer);

/**
 * Interpolate the last timer value to now
 *
 * @param point time update obtained via the vlc_player_timer_cbs.on_update()
 * callback
 * @param system_now current system date
 * @param out_ts pointer where to set the interpolated ts, subtract this time
 * with VLC_TICK_0 to get the original value.
 * @param out_pos pointer where to set the interpolated position
 * @return VLC_SUCCESS in case of success, an error in the interpolated ts is
 * negative (could happen during the buffering step)
 */
VLC_API int
vlc_player_timer_point_Interpolate(const struct vlc_player_timer_point *point,
                                   vlc_tick_t system_now,
                                   vlc_tick_t *out_ts, double *out_pos);

/**
 * Get the date of the next interval
 *
 * Can be used to setup an UI timer in order to update some widgets at specific
 * interval. A next_interval of VLC_TICK_FROM_SEC(1) can be used to update a
 * time widget when the media reaches a new second.
 *
 * @note The media time doesn't necessarily correspond to the system time, that
 * is why this function is needed and use the rate of the current point.
 *
 * @param point time update obtained via the vlc_player_timer_cbs.on_update()
 * @param system_now current system date
 * @param interpolated_ts ts returned by vlc_player_timer_point_Interpolate()
 * with the same system now
 * @param next_interval next interval
 * @return the absolute system date of the next interval
 */
VLC_API vlc_tick_t
vlc_player_timer_point_GetNextIntervalDate(const struct vlc_player_timer_point *point,
                                           vlc_tick_t system_now,
                                           vlc_tick_t interpolated_ts,
                                           vlc_tick_t next_interval);

/** @} vlc_player__timer */

/** @} vlc_player */

#endif
