[vlc-devel] [PATCH 2/2] player: split implementation into several files
Thomas Guillem
thomas at gllm.fr
Tue Aug 13 16:02:54 CEST 2019
player.c was starting getting huge (~4000 lines), and therefore harder to
navigate.
RFC: What do you prefer between src/input/player_foo.c and
src/input/player/foo.c ?
---
src/Makefile.am | 6 +
src/input/player.c | 1992 +-------------------------------------
src/input/player.h | 339 ++++++-
src/input/player_aout.c | 219 +++++
src/input/player_input.c | 771 +++++++++++++++
src/input/player_osd.c | 308 ++++++
src/input/player_title.c | 174 ++++
src/input/player_track.c | 207 ++++
src/input/player_vout.c | 203 ++++
9 files changed, 2272 insertions(+), 1947 deletions(-)
create mode 100644 src/input/player_aout.c
create mode 100644 src/input/player_input.c
create mode 100644 src/input/player_osd.c
create mode 100644 src/input/player_title.c
create mode 100644 src/input/player_track.c
create mode 100644 src/input/player_vout.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 8c56c0e165..f0ee136291 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -261,6 +261,12 @@ libvlccore_la_SOURCES = \
input/es_out_timeshift.c \
input/input.c \
input/player.c \
+ input/player_input.c \
+ input/player_track.c \
+ input/player_title.c \
+ input/player_aout.c \
+ input/player_vout.c \
+ input/player_osd.c \
input/player.h \
input/info.h \
input/meta.c \
diff --git a/src/input/player.c b/src/input/player.c
index 9ab8926f09..7e43e853b1 100644
--- a/src/input/player.c
+++ b/src/input/player.c
@@ -1,7 +1,7 @@
/*****************************************************************************
* player.c: Player interface
*****************************************************************************
- * Copyright © 2018 VLC authors and VideoLAN
+ * Copyright © 2018-2019 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
@@ -27,23 +27,15 @@
#include <vlc_common.h>
#include "player.h"
#include <vlc_aout.h>
-#include <vlc_interface.h>
#include <vlc_renderer_discovery.h>
-#include <vlc_list.h>
-#include <vlc_vector.h>
-#include <vlc_atomic.h>
#include <vlc_tick.h>
#include <vlc_decoder.h>
#include <vlc_memstream.h>
#include "libvlc.h"
-#include "input_internal.h"
#include "resource.h"
#include "../audio_output/aout_internal.h"
-#define RETRY_TIMEOUT_BASE VLC_TICK_FROM_MS(100)
-#define RETRY_TIMEOUT_MAX VLC_TICK_FROM_MS(3200)
-
static_assert(VLC_PLAYER_CAP_SEEK == VLC_INPUT_CAPABILITIES_SEEKABLE &&
VLC_PLAYER_CAP_PAUSE == VLC_INPUT_CAPABILITIES_PAUSEABLE &&
VLC_PLAYER_CAP_CHANGE_RATE == VLC_INPUT_CAPABILITIES_CHANGE_RATE &&
@@ -54,745 +46,10 @@ static_assert(VLC_PLAYER_TITLE_MENU == INPUT_TITLE_MENU &&
VLC_PLAYER_TITLE_INTERACTIVE == INPUT_TITLE_INTERACTIVE,
"player/input title flag mismatch");
-struct vlc_player_track_priv
-{
- struct vlc_player_track t;
- vout_thread_t *vout; /* weak reference */
- vlc_tick_t delay;
- /* only valid if selected and if category is VIDEO_ES or SPU_ES */
- enum vlc_vout_order vout_order;
-};
-
-typedef struct VLC_VECTOR(struct vlc_player_program *)
- vlc_player_program_vector;
-
-typedef struct VLC_VECTOR(struct vlc_player_track_priv *)
- vlc_player_track_vector;
-
-struct vlc_player_listener_id
-{
- const struct vlc_player_cbs *cbs;
- void *cbs_data;
- struct vlc_list node;
-};
-
-struct vlc_player_vout_listener_id
-{
- const struct vlc_player_vout_cbs *cbs;
- void *cbs_data;
- struct vlc_list node;
-};
-
-struct vlc_player_aout_listener_id
-{
- const struct vlc_player_aout_cbs *cbs;
- void *cbs_data;
- struct vlc_list node;
-};
-
-struct vlc_player_title_list
-{
- vlc_atomic_rc_t rc;
- size_t count;
- struct vlc_player_title array[];
-};
-
-struct vlc_player_input
-{
- input_thread_t *thread;
- vlc_player_t *player;
- bool started;
-
- enum vlc_player_state state;
- enum vlc_player_error error;
- float rate;
- int capabilities;
- vlc_tick_t length;
-
- vlc_tick_t time;
- float position;
-
- bool recording;
-
- float signal_quality;
- float signal_strength;
- float cache;
-
- struct input_stats_t stats;
-
- vlc_tick_t cat_delays[DATA_ES];
-
- vlc_player_program_vector program_vector;
- vlc_player_track_vector video_track_vector;
- vlc_player_track_vector audio_track_vector;
- vlc_player_track_vector spu_track_vector;
- struct vlc_player_track_priv *teletext_menu;
-
- struct vlc_player_title_list *titles;
-
- size_t title_selected;
- size_t chapter_selected;
-
- struct vlc_list node;
-
- bool teletext_enabled;
- bool teletext_transparent;
- unsigned teletext_page;
-
- struct
- {
- vlc_tick_t time;
- float pos;
- bool set;
- } abloop_state[2];
-};
-
-struct vlc_player_t
-{
- struct vlc_object_t obj;
- vlc_mutex_t lock;
- vlc_mutex_t aout_listeners_lock;
- vlc_mutex_t vout_listeners_lock;
- vlc_cond_t start_delay_cond;
-
- enum vlc_player_media_stopped_action media_stopped_action;
- bool start_paused;
-
- const struct vlc_player_media_provider *media_provider;
- void *media_provider_data;
-
- bool pause_on_cork;
- bool corked;
-
- struct vlc_list listeners;
- struct vlc_list aout_listeners;
- struct vlc_list vout_listeners;
-
- input_resource_t *resource;
- vlc_renderer_item_t *renderer;
-
- input_item_t *media;
- struct vlc_player_input *input;
-
- bool releasing_media;
- bool next_media_requested;
- input_item_t *next_media;
-
- enum vlc_player_state global_state;
- bool started;
-
- unsigned error_count;
-
- bool deleting;
- struct
- {
- vlc_thread_t thread;
- vlc_cond_t wait;
- vlc_cond_t notify;
- struct vlc_list inputs;
- struct vlc_list stopping_inputs;
- struct vlc_list joinable_inputs;
- } destructor;
-};
-
-#define vlc_player_SendEvent(player, event, ...) do { \
- vlc_player_listener_id *listener; \
- vlc_list_foreach(listener, &player->listeners, node) \
- { \
- if (listener->cbs->event) \
- listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
- } \
-} while(0)
-
-#define vlc_player_aout_SendEvent(player, event, ...) do { \
- vlc_mutex_lock(&player->aout_listeners_lock); \
- vlc_player_aout_listener_id *listener; \
- vlc_list_foreach(listener, &player->aout_listeners, node) \
- { \
- if (listener->cbs->event) \
- listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
- } \
- vlc_mutex_unlock(&player->aout_listeners_lock); \
-} while(0)
-
-#define vlc_player_vout_SendEvent(player, event, ...) do { \
- vlc_mutex_lock(&player->vout_listeners_lock); \
- vlc_player_vout_listener_id *listener; \
- vlc_list_foreach(listener, &player->vout_listeners, node) \
- { \
- if (listener->cbs->event) \
- listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
- } \
- vlc_mutex_unlock(&player->vout_listeners_lock); \
-} while(0)
-
#define vlc_player_foreach_inputs(it) \
for (struct vlc_player_input *it = player->input; it != NULL; it = NULL)
-static void
-input_thread_Events(input_thread_t *, const struct vlc_input_event *, void *);
-static void
-vlc_player_input_HandleState(struct vlc_player_input *, enum vlc_player_state,
- vlc_tick_t);
-static int
-vlc_player_VoutCallback(vlc_object_t *this, const char *var,
- vlc_value_t oldval, vlc_value_t newval, void *data);
-static int
-vlc_player_VoutOSDCallback(vlc_object_t *this, const char *var,
- vlc_value_t oldval, vlc_value_t newval, void *data);
-
-void
-vlc_player_assert_locked(vlc_player_t *player)
-{
- assert(player);
- vlc_mutex_assert(&player->lock);
-}
-
-static inline struct vlc_player_input *
-vlc_player_get_input_locked(vlc_player_t *player)
-{
- vlc_player_assert_locked(player);
- return player->input;
-}
-
-static vout_thread_t **
-vlc_player_vout_OSDHoldAll(vlc_player_t *player, size_t *count)
-{
- vout_thread_t **vouts;
- input_resource_HoldVouts(player->resource, &vouts, count);
-
- for (size_t i = 0; i < *count; ++i)
- {
- vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD);
- vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER);
- vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER);
- }
- return vouts;
-}
-
-static void
-vlc_player_vout_OSDReleaseAll(vlc_player_t *player, vout_thread_t **vouts,
- size_t count)
-{
- for (size_t i = 0; i < count; ++i)
- vout_Release(vouts[i]);
- free(vouts);
- (void) player;
-}
-
-static inline void
-vouts_osd_Message(vout_thread_t **vouts, size_t count, const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- for (size_t i = 0; i < count; ++i)
- {
- va_list acpy;
- va_copy(acpy, args);
- vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, acpy);
- va_end(acpy);
- }
- va_end(args);
-}
-
-static inline void
-vouts_osd_Icon(vout_thread_t **vouts, size_t count, short type)
-{
- for (size_t i = 0; i < count; ++i)
- vout_OSDIcon(vouts[i], VOUT_SPU_CHANNEL_OSD, type);
-}
-
-static inline void
-vouts_osd_Slider(vout_thread_t **vouts, size_t count, int position, short type)
-{
- int channel = type == OSD_HOR_SLIDER ?
- VOUT_SPU_CHANNEL_OSD_HSLIDER : VOUT_SPU_CHANNEL_OSD_VSLIDER;
- for (size_t i = 0; i < count; ++i)
- vout_OSDSlider(vouts[i], channel, position, type);
-}
-
-void
-vlc_player_osd_Message(vlc_player_t *player, const char *fmt, ...)
-{
- size_t count;
- vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
-
- va_list args;
- va_start(args, fmt);
- for (size_t i = 0; i < count; ++i)
- {
- va_list acpy;
- va_copy(acpy, args);
- vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, acpy);
- va_end(acpy);
- }
- va_end(args);
-
- vlc_player_vout_OSDReleaseAll(player, vouts, count);
-}
-
-static void
-vlc_player_vout_OSDIcon(vlc_player_t *player, short type)
-{
- size_t count;
- vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
-
- vouts_osd_Icon(vouts, count, type);
-
- vlc_player_vout_OSDReleaseAll(player, vouts, count);
-}
-
-static char *
-vlc_player_program_DupTitle(int id, const char *title)
-{
- char *dup;
- if (title)
- dup = strdup(title);
- else if (asprintf(&dup, "%d", id) == -1)
- dup = NULL;
- return dup;
-}
-
-static struct vlc_player_program *
-vlc_player_program_New(int id, const char *name)
-{
- struct vlc_player_program *prgm = malloc(sizeof(*prgm));
- if (!prgm)
- return NULL;
- prgm->name = vlc_player_program_DupTitle(id, name);
- if (!prgm->name)
- {
- free(prgm);
- return NULL;
- }
- prgm->group_id = id;
- prgm->selected = prgm->scrambled = false;
-
- return prgm;
-}
-
-static int
-vlc_player_program_Update(struct vlc_player_program *prgm, int id,
- const char *name)
-{
- free((char *)prgm->name);
- prgm->name = vlc_player_program_DupTitle(id, name);
- return prgm->name != NULL ? VLC_SUCCESS : VLC_ENOMEM;
-}
-
-struct vlc_player_program *
-vlc_player_program_Dup(const struct vlc_player_program *src)
-{
- struct vlc_player_program *dup =
- vlc_player_program_New(src->group_id, src->name);
-
- if (!dup)
- return NULL;
- dup->selected = src->selected;
- dup->scrambled = src->scrambled;
- return dup;
-}
-
-void
-vlc_player_program_Delete(struct vlc_player_program *prgm)
-{
- free((char *)prgm->name);
- free(prgm);
-}
-
-static struct vlc_player_program *
-vlc_player_program_vector_FindById(vlc_player_program_vector *vec, int id,
- size_t *idx)
-{
- for (size_t i = 0; i < vec->size; ++i)
- {
- struct vlc_player_program *prgm = vec->data[i];
- if (prgm->group_id == id)
- {
- if (idx)
- *idx = i;
- return prgm;
- }
- }
- return NULL;
-}
-
-static struct vlc_player_track_priv *
-vlc_player_track_New(vlc_es_id_t *id, const char *name, const es_format_t *fmt)
-{
- struct vlc_player_track_priv *trackpriv = malloc(sizeof(*trackpriv));
- if (!trackpriv)
- return NULL;
- struct vlc_player_track *track = &trackpriv->t;
-
- trackpriv->delay = INT64_MAX;
- trackpriv->vout = NULL;
- trackpriv->vout_order = VLC_VOUT_ORDER_NONE;
-
- track->name = strdup(name);
- if (!track->name)
- {
- free(track);
- return NULL;
- }
-
- int ret = es_format_Copy(&track->fmt, fmt);
- if (ret != VLC_SUCCESS)
- {
- free((char *)track->name);
- free(track);
- return NULL;
- }
- track->es_id = vlc_es_id_Hold(id);
- track->selected = false;
-
- return trackpriv;
-}
-
-struct vlc_player_track *
-vlc_player_track_Dup(const struct vlc_player_track *src)
-{
- struct vlc_player_track_priv *duppriv =
- vlc_player_track_New(src->es_id, src->name, &src->fmt);
-
- if (!duppriv)
- return NULL;
- duppriv->t.selected = src->selected;
- return &duppriv->t;
-}
-
-static void
-vlc_player_track_priv_Delete(struct vlc_player_track_priv *trackpriv)
-{
- struct vlc_player_track *track = &trackpriv->t;
- es_format_Clean(&track->fmt);
- free((char *)track->name);
- vlc_es_id_Release(track->es_id);
- free(trackpriv);
-}
-
-void
-vlc_player_track_Delete(struct vlc_player_track *track)
-{
- struct vlc_player_track_priv *trackpriv =
- container_of(track, struct vlc_player_track_priv, t);
- vlc_player_track_priv_Delete(trackpriv);
-}
-
-static int
-vlc_player_track_priv_Update(struct vlc_player_track_priv *trackpriv,
- const char *name, const es_format_t *fmt)
-{
- struct vlc_player_track *track = &trackpriv->t;
-
- if (strcmp(name, track->name) != 0)
- {
- char *dup = strdup(name);
- if (!dup)
- return VLC_ENOMEM;
- free((char *)track->name);
- track->name = dup;
- }
-
- es_format_t fmtdup;
- int ret = es_format_Copy(&fmtdup, fmt);
- if (ret != VLC_SUCCESS)
- return ret;
-
- es_format_Clean(&track->fmt);
- track->fmt = fmtdup;
- return VLC_SUCCESS;
-}
-
-struct vlc_player_title_list *
-vlc_player_title_list_Hold(struct vlc_player_title_list *titles)
-{
- vlc_atomic_rc_inc(&titles->rc);
- return titles;
-}
-
void
-vlc_player_title_list_Release(struct vlc_player_title_list *titles)
-{
- if (!vlc_atomic_rc_dec(&titles->rc))
- return;
- for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
- {
- struct vlc_player_title *title = &titles->array[title_idx];
- free((char *)title->name);
- for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
- ++chapter_idx)
- {
- const struct vlc_player_chapter *chapter =
- &title->chapters[chapter_idx];
- free((char *)chapter->name);
- }
- free((void *)title->chapters);
- }
- free(titles);
-}
-
-static char *
-input_title_GetName(const struct input_title_t *input_title, int idx,
- int title_offset)
-{
- int ret;
- char length_str[MSTRTIME_MAX_SIZE + sizeof(" []")];
-
- if (input_title->i_length > 0)
- {
- strcpy(length_str, " [");
- secstotimestr(&length_str[2], SEC_FROM_VLC_TICK(input_title->i_length));
- strcat(length_str, "]");
- }
- else
- length_str[0] = '\0';
-
- char *dup;
- if (input_title->psz_name && input_title->psz_name[0] != '\0')
- ret = asprintf(&dup, "%s%s", input_title->psz_name, length_str);
- else
- ret = asprintf(&dup, _("Title %i%s"), idx + title_offset, length_str);
- if (ret == -1)
- return NULL;
- return dup;
-}
-
-static char *
-seekpoint_GetName(seekpoint_t *seekpoint, int idx, int chapter_offset)
-{
- if (seekpoint->psz_name && seekpoint->psz_name[0] != '\0' )
- return strdup(seekpoint->psz_name);
-
- char *dup;
- int ret = asprintf(&dup, _("Chapter %i"), idx + chapter_offset);
- if (ret == -1)
- return NULL;
- return dup;
-}
-
-static struct vlc_player_title_list *
-vlc_player_title_list_Create(input_title_t *const *array, size_t count,
- int title_offset, int chapter_offset)
-{
- if (count == 0)
- return NULL;
-
- /* Allocate the struct + the whole list */
- size_t size;
- if (mul_overflow(count, sizeof(struct vlc_player_title), &size))
- return NULL;
- if (add_overflow(size, sizeof(struct vlc_player_title_list), &size))
- return NULL;
- struct vlc_player_title_list *titles = malloc(size);
- if (!titles)
- return NULL;
-
- vlc_atomic_rc_init(&titles->rc);
- titles->count = count;
-
- for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
- {
- const struct input_title_t *input_title = array[title_idx];
- struct vlc_player_title *title = &titles->array[title_idx];
-
- title->name = input_title_GetName(input_title, title_idx, title_offset);
- title->length = input_title->i_length;
- title->flags = input_title->i_flags;
- const size_t seekpoint_count = input_title->i_seekpoint > 0 ?
- input_title->i_seekpoint : 0;
- title->chapter_count = seekpoint_count;
-
- struct vlc_player_chapter *chapters = title->chapter_count == 0 ? NULL :
- vlc_alloc(title->chapter_count, sizeof(*chapters));
-
- if (chapters)
- {
- for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
- ++chapter_idx)
- {
- struct vlc_player_chapter *chapter = &chapters[chapter_idx];
- seekpoint_t *seekpoint = input_title->seekpoint[chapter_idx];
-
- chapter->name = seekpoint_GetName(seekpoint, chapter_idx,
- chapter_offset);
- chapter->time = seekpoint->i_time_offset;
- if (!chapter->name) /* Will trigger the error path */
- title->chapter_count = chapter_idx;
- }
- }
- else if (seekpoint_count > 0) /* Will trigger the error path */
- title->chapter_count = 0;
-
- title->chapters = chapters;
-
- if (!title->name || seekpoint_count != title->chapter_count)
- {
- /* Release titles up to title_idx */
- titles->count = title_idx;
- vlc_player_title_list_Release(titles);
- return NULL;
- }
- }
- return titles;
-}
-
-const struct vlc_player_title *
-vlc_player_title_list_GetAt(struct vlc_player_title_list *titles, size_t idx)
-{
- assert(idx < titles->count);
- return &titles->array[idx];
-}
-
-size_t
-vlc_player_title_list_GetCount(struct vlc_player_title_list *titles)
-{
- return titles->count;
-}
-
-static struct vlc_player_input *
-vlc_player_input_New(vlc_player_t *player, input_item_t *item)
-{
- struct vlc_player_input *input = malloc(sizeof(*input));
- if (!input)
- return NULL;
-
- input->player = player;
- input->started = false;
-
- input->state = VLC_PLAYER_STATE_STOPPED;
- input->error = VLC_PLAYER_ERROR_NONE;
- input->rate = 1.f;
- input->capabilities = 0;
- input->length = input->time = VLC_TICK_INVALID;
- input->position = 0.f;
-
- input->recording = false;
-
- input->cache = 0.f;
- input->signal_quality = input->signal_strength = -1.f;
-
- memset(&input->stats, 0, sizeof(input->stats));
-
- vlc_vector_init(&input->program_vector);
- vlc_vector_init(&input->video_track_vector);
- vlc_vector_init(&input->audio_track_vector);
- vlc_vector_init(&input->spu_track_vector);
- input->teletext_menu = NULL;
-
- input->titles = NULL;
- input->title_selected = input->chapter_selected = 0;
-
- input->teletext_enabled = input->teletext_transparent = false;
- input->teletext_page = 0;
-
- input->abloop_state[0].set = input->abloop_state[1].set = false;
-
- input->thread = input_Create(player, input_thread_Events, input, item,
- player->resource, player->renderer);
- if (!input->thread)
- {
- free(input);
- return NULL;
- }
-
- /* Initial sub/audio delay */
- const vlc_tick_t cat_delays[DATA_ES] = {
- [AUDIO_ES] =
- VLC_TICK_FROM_MS(var_InheritInteger(player, "audio-desync")),
- [SPU_ES] =
- vlc_tick_from_samples(var_InheritInteger(player, "sub-delay"), 10),
- };
-
- for (enum es_format_category_e i = UNKNOWN_ES; i < DATA_ES; ++i)
- {
- input->cat_delays[i] = cat_delays[i];
- if (cat_delays[i] != 0)
- {
- const input_control_param_t param = {
- .cat_delay = { i, cat_delays[i] }
- };
- input_ControlPush(input->thread, INPUT_CONTROL_SET_CATEGORY_DELAY,
- ¶m);
- vlc_player_SendEvent(player, on_category_delay_changed, i,
- cat_delays[i]);
- }
- }
- return input;
-}
-
-static void
-vlc_player_input_Delete(struct vlc_player_input *input)
-{
- assert(input->titles == NULL);
- assert(input->program_vector.size == 0);
- assert(input->video_track_vector.size == 0);
- assert(input->audio_track_vector.size == 0);
- assert(input->spu_track_vector.size == 0);
- assert(input->teletext_menu == NULL);
-
- vlc_vector_destroy(&input->program_vector);
- vlc_vector_destroy(&input->video_track_vector);
- vlc_vector_destroy(&input->audio_track_vector);
- vlc_vector_destroy(&input->spu_track_vector);
-
- input_Close(input->thread);
- free(input);
-}
-
-static void
-vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, vlc_tick_t time,
- float pos)
-{
- vlc_player_t *player = input->player;
-
- if (player->input != input)
- return;
-
- assert(input->abloop_state[0].set && input->abloop_state[1].set);
-
- if (time != VLC_TICK_INVALID
- && input->abloop_state[0].time != VLC_TICK_INVALID
- && input->abloop_state[1].time != VLC_TICK_INVALID)
- {
- if (time >= input->abloop_state[1].time)
- vlc_player_SetTime(player, input->abloop_state[0].time);
- }
- else if (pos >= input->abloop_state[1].pos)
- vlc_player_SetPosition(player, input->abloop_state[0].pos);
-}
-
-static inline vlc_tick_t
-vlc_player_input_GetTime(struct vlc_player_input *input)
-{
- return input->time;
-}
-
-static inline float
-vlc_player_input_GetPos(struct vlc_player_input *input)
-{
- return input->position;
-}
-
-static void
-vlc_player_input_UpdateTime(struct vlc_player_input *input)
-{
- if (input->abloop_state[0].set && input->abloop_state[1].set)
- vlc_player_input_HandleAtoBLoop(input, vlc_player_input_GetTime(input),
- vlc_player_input_GetPos(input));
-}
-
-static int
-vlc_player_input_Start(struct vlc_player_input *input)
-{
- int ret = input_Start(input->thread);
- if (ret != VLC_SUCCESS)
- return ret;
- input->started = true;
- return ret;
-}
-
-static void
vlc_player_PrepareNextMedia(vlc_player_t *player)
{
vlc_player_assert_locked(player);
@@ -808,7 +65,7 @@ vlc_player_PrepareNextMedia(vlc_player_t *player)
player->next_media_requested = true;
}
-static int
+int
vlc_player_OpenNextMedia(vlc_player_t *player)
{
assert(player->input == NULL);
@@ -833,8 +90,9 @@ vlc_player_OpenNextMedia(vlc_player_t *player)
player->media = player->next_media;
player->next_media = NULL;
- player->input = vlc_player_input_New(player, player->media);
- if (!player->input)
+ struct vlc_player_input *input = player->input =
+ vlc_player_input_New(player, player->media);
+ if (!input)
{
input_item_Release(player->media);
player->media = NULL;
@@ -892,7 +150,7 @@ vlc_player_destructor_AddInput(vlc_player_t *player,
vlc_cond_signal(&input->player->destructor.wait);
}
-static void
+void
vlc_player_destructor_AddStoppingInput(vlc_player_t *player,
struct vlc_player_input *input)
{
@@ -906,7 +164,7 @@ vlc_player_destructor_AddStoppingInput(vlc_player_t *player,
}
}
-static void
+void
vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
struct vlc_player_input *input)
{
@@ -982,121 +240,6 @@ vlc_player_destructor_Thread(void *data)
return NULL;
}
-static bool
-vlc_player_WaitRetryDelay(vlc_player_t *player)
-{
- if (player->error_count)
- {
- /* Delay the next opening in case of error to avoid busy loops */
- vlc_tick_t delay = RETRY_TIMEOUT_BASE;
- for (unsigned i = 1; i < player->error_count
- && delay < RETRY_TIMEOUT_MAX; ++i)
- delay *= 2; /* Wait 100, 200, 400, 800, 1600 and finally 3200ms */
- delay += vlc_tick_now();
-
- while (player->error_count > 0
- && vlc_cond_timedwait(&player->start_delay_cond, &player->lock,
- delay) == 0);
- if (player->error_count == 0)
- return false; /* canceled */
- }
- return true;
-}
-
-static void
-vlc_player_input_HandleState(struct vlc_player_input *input,
- enum vlc_player_state state, vlc_tick_t state_date)
-{
- vlc_player_t *player = input->player;
-
- /* The STOPPING state can be set earlier by the player. In that case,
- * ignore all future events except the STOPPED one */
- if (input->state == VLC_PLAYER_STATE_STOPPING
- && state != VLC_PLAYER_STATE_STOPPED)
- return;
-
- input->state = state;
-
- /* Override the global state if the player is still playing and has a next
- * media to play */
- bool send_event = player->global_state != state;
- switch (input->state)
- {
- case VLC_PLAYER_STATE_STOPPED:
- assert(!input->started);
- assert(input != player->input);
-
- if (input->titles)
- {
- vlc_player_title_list_Release(input->titles);
- input->titles = NULL;
- vlc_player_SendEvent(player, on_titles_changed, NULL);
- }
-
- if (input->error != VLC_PLAYER_ERROR_NONE)
- player->error_count++;
- else
- player->error_count = 0;
-
- vlc_player_WaitRetryDelay(player);
-
- if (!player->deleting)
- vlc_player_OpenNextMedia(player);
- if (!player->input)
- player->started = false;
-
- switch (player->media_stopped_action)
- {
- case VLC_PLAYER_MEDIA_STOPPED_EXIT:
- if (player->input && player->started)
- vlc_player_input_Start(player->input);
- else
- libvlc_Quit(vlc_object_instance(player));
- break;
- case VLC_PLAYER_MEDIA_STOPPED_CONTINUE:
- if (player->input && player->started)
- vlc_player_input_Start(player->input);
- break;
- default:
- break;
- }
-
- send_event = !player->started;
- break;
- case VLC_PLAYER_STATE_STOPPING:
- input->started = false;
- if (input == player->input)
- player->input = NULL;
-
- if (player->started)
- {
- vlc_player_PrepareNextMedia(player);
- if (!player->next_media)
- player->started = false;
- }
- send_event = !player->started;
- break;
- case VLC_PLAYER_STATE_STARTED:
- case VLC_PLAYER_STATE_PLAYING:
- if (player->started &&
- player->global_state == VLC_PLAYER_STATE_PLAYING)
- send_event = false;
- break;
-
- case VLC_PLAYER_STATE_PAUSED:
- assert(player->started && input->started);
- break;
- default:
- vlc_assert_unreachable();
- }
-
- if (send_event)
- {
- player->global_state = input->state;
- vlc_player_SendEvent(player, on_state_changed, player->global_state);
- }
-}
-
size_t
vlc_player_GetProgramCount(vlc_player_t *player)
{
@@ -1130,12 +273,6 @@ vlc_player_GetProgram(vlc_player_t *player, int id)
return prgm;
}
-static inline void
-vlc_player_vout_OSDProgram(vlc_player_t *player, const char *name)
-{
- vlc_player_osd_Message(player, _("Program Service ID: %s"), name);
-}
-
void
vlc_player_SelectProgram(vlc_player_t *player, int id)
{
@@ -1150,7 +287,7 @@ vlc_player_SelectProgram(vlc_player_t *player, int id)
{
input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_PROGRAM,
&(vlc_value_t) { .i_int = id });
- vlc_player_vout_OSDProgram(player, prgm->name);
+ vlc_player_osd_Program(player, prgm->name);
}
}
@@ -1200,134 +337,6 @@ vlc_player_SelectPrevProgram(vlc_player_t *player)
vlc_player_CycleProgram(player, false);
}
-static void
-vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
- const struct vlc_input_event_program *ev)
-{
- vlc_player_t *player = input->player;
- struct vlc_player_program *prgm;
- vlc_player_program_vector *vec = &input->program_vector;
-
- switch (ev->action)
- {
- case VLC_INPUT_PROGRAM_ADDED:
- prgm = vlc_player_program_New(ev->id, ev->title);
- if (!prgm)
- break;
-
- if (!vlc_vector_push(vec, prgm))
- {
- vlc_player_program_Delete(prgm);
- break;
- }
- vlc_player_SendEvent(player, on_program_list_changed,
- VLC_PLAYER_LIST_ADDED, prgm);
- break;
- case VLC_INPUT_PROGRAM_DELETED:
- {
- size_t idx;
- prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx);
- if (prgm)
- {
- vlc_player_SendEvent(player, on_program_list_changed,
- VLC_PLAYER_LIST_REMOVED, prgm);
- vlc_vector_remove(vec, idx);
- vlc_player_program_Delete(prgm);
- }
- break;
- }
- case VLC_INPUT_PROGRAM_UPDATED:
- case VLC_INPUT_PROGRAM_SCRAMBLED:
- prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL);
- if (!prgm)
- break;
- if (ev->action == VLC_INPUT_PROGRAM_UPDATED)
- {
- if (vlc_player_program_Update(prgm, ev->id, ev->title) != 0)
- break;
- }
- else
- prgm->scrambled = ev->scrambled;
- vlc_player_SendEvent(player, on_program_list_changed,
- VLC_PLAYER_LIST_UPDATED, prgm);
- break;
- case VLC_INPUT_PROGRAM_SELECTED:
- {
- int unselected_id = -1, selected_id = -1;
- vlc_vector_foreach(prgm, vec)
- {
- if (prgm->group_id == ev->id)
- {
- if (!prgm->selected)
- {
- assert(selected_id == -1);
- prgm->selected = true;
- selected_id = prgm->group_id;
- }
- }
- else
- {
- if (prgm->selected)
- {
- assert(unselected_id == -1);
- prgm->selected = false;
- unselected_id = prgm->group_id;
- }
- }
- }
- if (unselected_id != -1 || selected_id != -1)
- vlc_player_SendEvent(player, on_program_selection_changed,
- unselected_id, selected_id);
- break;
- }
- default:
- vlc_assert_unreachable();
- }
-}
-
-static inline vlc_player_track_vector *
-vlc_player_input_GetTrackVector(struct vlc_player_input *input,
- enum es_format_category_e cat)
-{
- switch (cat)
- {
- case VIDEO_ES:
- return &input->video_track_vector;
- case AUDIO_ES:
- return &input->audio_track_vector;
- case SPU_ES:
- return &input->spu_track_vector;
- default:
- return NULL;
- }
-}
-
-static struct vlc_player_track_priv *
-vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t *id,
- size_t *idx)
-{
- for (size_t i = 0; i < vec->size; ++i)
- {
- struct vlc_player_track_priv *trackpriv = vec->data[i];
- if (trackpriv->t.es_id == id)
- {
- if (idx)
- *idx = i;
- return trackpriv;
- }
- }
- return NULL;
-}
-
-static struct vlc_player_track_priv *
-vlc_player_input_FindTrackById(struct vlc_player_input *input, vlc_es_id_t *id,
- size_t *idx)
-{
- vlc_player_track_vector *vec =
- vlc_player_input_GetTrackVector(input, vlc_es_id_GetCat(id));
- return vec ? vlc_player_track_vector_FindById(vec, id, idx) : NULL;
-}
-
size_t
vlc_player_GetTrackCount(vlc_player_t *player, enum es_format_category_e cat)
{
@@ -1420,32 +429,6 @@ vlc_player_GetEsIdFromVout(vlc_player_t *player, vout_thread_t *vout)
return NULL;
}
-static inline const char *
-es_format_category_to_string(enum es_format_category_e cat)
-{
- switch (cat)
- {
- case VIDEO_ES: return "Video";
- case AUDIO_ES: return "Audio";
- case SPU_ES: return "Subtitle";
- default: return NULL;
- }
-}
-
-static void
-vlc_player_vout_OSDTrack(vlc_player_t *player, vlc_es_id_t *id, bool select)
-{
- enum es_format_category_e cat = vlc_es_id_GetCat(id);
- const struct vlc_player_track *track = vlc_player_GetTrack(player, id);
- if (!track && select)
- return;
-
- const char *cat_name = es_format_category_to_string(cat);
- assert(cat_name);
- const char *track_name = select ? track->name : _("N/A");
- vlc_player_osd_Message(player, _("%s track: %s"), cat_name, track_name);
-}
-
unsigned
vlc_player_SelectEsIdList(vlc_player_t *player,
enum es_format_category_e cat,
@@ -1507,7 +490,7 @@ vlc_player_SelectEsIdList(vlc_player_t *player,
if (track_count == 0)
vlc_player_osd_Message(player, _("%s track: %s"), cat_name, _("N/A"));
else if (track_count == 1)
- vlc_player_vout_OSDTrack(player, es_id_list[0], true);
+ vlc_player_osd_Track(player, es_id_list[0], true);
else
{
struct vlc_memstream stream;
@@ -1545,7 +528,7 @@ vlc_player_SelectEsId(vlc_player_t *player, vlc_es_id_t *id,
if (policy == VLC_PLAYER_SELECT_EXCLUSIVE)
{
input_ControlPushEsHelper(input->thread, INPUT_CONTROL_SET_ES, id);
- vlc_player_vout_OSDTrack(player, id, true);
+ vlc_player_osd_Track(player, id, true);
return 1;
}
@@ -1568,7 +551,7 @@ vlc_player_SelectEsId(vlc_player_t *player, vlc_es_id_t *id,
if (selected_track_count == 1)
{
input_ControlPushEsHelper(input->thread, INPUT_CONTROL_SET_ES, id);
- vlc_player_vout_OSDTrack(player, id, true);
+ vlc_player_osd_Track(player, id, true);
return 1;
}
@@ -1668,7 +651,7 @@ vlc_player_UnselectEsId(vlc_player_t *player, vlc_es_id_t *id)
return;
input_ControlPushEsHelper(input->thread, INPUT_CONTROL_UNSET_ES, id);
- vlc_player_vout_OSDTrack(player, id, false);
+ vlc_player_osd_Track(player, id, false);
}
void
@@ -1715,55 +698,6 @@ vlc_player_GetCategoryLanguage(vlc_player_t *player,
}
}
-static void
-vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input,
- const struct vlc_input_event_es *ev)
-{
- vlc_player_t *player = input->player;
- switch (ev->action)
- {
- case VLC_INPUT_ES_ADDED:
- if (input->teletext_menu)
- {
- msg_Warn(player, "Can't handle more than one teletext menu "
- "track. Using the last one.");
- vlc_player_track_priv_Delete(input->teletext_menu);
- }
- input->teletext_menu = vlc_player_track_New(ev->id, ev->title,
- ev->fmt);
- if (!input->teletext_menu)
- return;
-
- vlc_player_SendEvent(player, on_teletext_menu_changed, true);
- break;
- case VLC_INPUT_ES_DELETED:
- {
- if (input->teletext_menu && input->teletext_menu->t.es_id == ev->id)
- {
- assert(!input->teletext_enabled);
-
- vlc_player_track_priv_Delete(input->teletext_menu);
- input->teletext_menu = NULL;
- vlc_player_SendEvent(player, on_teletext_menu_changed, false);
- }
- break;
- }
- case VLC_INPUT_ES_UPDATED:
- break;
- case VLC_INPUT_ES_SELECTED:
- case VLC_INPUT_ES_UNSELECTED:
- if (input->teletext_menu->t.es_id == ev->id)
- {
- input->teletext_enabled = ev->action == VLC_INPUT_ES_SELECTED;
- vlc_player_SendEvent(player, on_teletext_enabled_changed,
- input->teletext_enabled);
- }
- break;
- default:
- vlc_assert_unreachable();
- }
-}
-
void
vlc_player_SetTeletextEnabled(vlc_player_t *player, bool enabled)
{
@@ -1838,148 +772,6 @@ vlc_player_IsTeletextTransparent(vlc_player_t *player)
return vlc_player_IsTeletextEnabled(player) && input->teletext_transparent;
}
-static void
-vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
- const struct vlc_input_event_es *ev)
-{
- assert(ev->id && ev->title && ev->fmt);
-
- if (ev->fmt->i_cat == SPU_ES && ev->fmt->i_codec == VLC_CODEC_TELETEXT
- && (ev->fmt->subs.teletext.i_magazine == 1
- || ev->fmt->subs.teletext.i_magazine > 8))
- {
- vlc_player_input_HandleTeletextMenu(input, ev);
- return;
- }
-
- vlc_player_track_vector *vec =
- vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
- if (!vec)
- return; /* UNKNOWN_ES or DATA_ES not handled */
-
- vlc_player_t *player = input->player;
- struct vlc_player_track_priv *trackpriv;
- switch (ev->action)
- {
- case VLC_INPUT_ES_ADDED:
- trackpriv = vlc_player_track_New(ev->id, ev->title, ev->fmt);
- if (!trackpriv)
- break;
-
- if (!vlc_vector_push(vec, trackpriv))
- {
- vlc_player_track_priv_Delete(trackpriv);
- break;
- }
- vlc_player_SendEvent(player, on_track_list_changed,
- VLC_PLAYER_LIST_ADDED, &trackpriv->t);
- break;
- case VLC_INPUT_ES_DELETED:
- {
- size_t idx;
- trackpriv = vlc_player_track_vector_FindById(vec, ev->id, &idx);
- if (trackpriv)
- {
- vlc_player_SendEvent(player, on_track_list_changed,
- VLC_PLAYER_LIST_REMOVED, &trackpriv->t);
- vlc_vector_remove(vec, idx);
- vlc_player_track_priv_Delete(trackpriv);
- }
- break;
- }
- case VLC_INPUT_ES_UPDATED:
- trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
- if (!trackpriv)
- break;
- if (vlc_player_track_priv_Update(trackpriv, ev->title, ev->fmt) != 0)
- break;
- vlc_player_SendEvent(player, on_track_list_changed,
- VLC_PLAYER_LIST_UPDATED, &trackpriv->t);
- break;
- case VLC_INPUT_ES_SELECTED:
- trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
- if (trackpriv)
- {
- trackpriv->t.selected = true;
- vlc_player_SendEvent(player, on_track_selection_changed,
- NULL, trackpriv->t.es_id);
- }
- break;
- case VLC_INPUT_ES_UNSELECTED:
- trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
- if (trackpriv)
- {
- trackpriv->t.selected = false;
- vlc_player_SendEvent(player, on_track_selection_changed,
- trackpriv->t.es_id, NULL);
- }
- break;
- default:
- vlc_assert_unreachable();
- }
-}
-
-static void
-vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
- const struct vlc_input_event_title *ev)
-{
- vlc_player_t *player = input->player;
- switch (ev->action)
- {
- case VLC_INPUT_TITLE_NEW_LIST:
- {
- input_thread_private_t *input_th = input_priv(input->thread);
- const int title_offset = input_th->i_title_offset;
- const int chapter_offset = input_th->i_seekpoint_offset;
-
- if (input->titles)
- vlc_player_title_list_Release(input->titles);
- input->title_selected = input->chapter_selected = 0;
- input->titles =
- vlc_player_title_list_Create(ev->list.array, ev->list.count,
- title_offset, chapter_offset);
- vlc_player_SendEvent(player, on_titles_changed, input->titles);
- if (input->titles)
- vlc_player_SendEvent(player, on_title_selection_changed,
- &input->titles->array[0], 0);
- break;
- }
- case VLC_INPUT_TITLE_SELECTED:
- if (!input->titles)
- return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
- assert(ev->selected_idx < input->titles->count);
- input->title_selected = ev->selected_idx;
- vlc_player_SendEvent(player, on_title_selection_changed,
- &input->titles->array[input->title_selected],
- input->title_selected);
- break;
- default:
- vlc_assert_unreachable();
- }
-}
-
-static void
-vlc_player_input_HandleChapterEvent(struct vlc_player_input *input,
- const struct vlc_input_event_chapter *ev)
-{
- vlc_player_t *player = input->player;
- if (!input->titles || ev->title < 0 || ev->seekpoint < 0)
- return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
-
- assert((size_t)ev->title < input->titles->count);
- const struct vlc_player_title *title = &input->titles->array[ev->title];
- if (!title->chapter_count)
- return;
-
- assert(ev->seekpoint < (int)title->chapter_count);
- input->title_selected = ev->title;
- input->chapter_selected = ev->seekpoint;
-
- const struct vlc_player_chapter *chapter = &title->chapters[ev->seekpoint];
- vlc_player_SendEvent(player, on_chapter_selection_changed, title, ev->title,
- chapter, ev->seekpoint);
-}
-
struct vlc_player_title_list *
vlc_player_GetTitleList(vlc_player_t *player)
{
@@ -2071,248 +863,31 @@ void
vlc_player_SelectChapterIdx(vlc_player_t *player, size_t index)
{
struct vlc_player_input *input = vlc_player_get_input_locked(player);
- if (!input)
- return;
- input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_SEEKPOINT,
- &(vlc_value_t){ .i_int = index });
- vlc_player_osd_Message(player, _("Chapter %ld"), index);
-}
-
-void
-vlc_player_SelectNextChapter(vlc_player_t *player)
-{
- struct vlc_player_input *input = vlc_player_get_input_locked(player);
- if (!input)
- return;
- input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_NEXT, NULL);
- vlc_player_osd_Message(player, _("Next chapter"));
-}
-
-void
-vlc_player_SelectPrevChapter(vlc_player_t *player)
-{
- struct vlc_player_input *input = vlc_player_get_input_locked(player);
- if (!input)
- return;
- input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_PREV, NULL);
- vlc_player_osd_Message(player, _("Previous chapter"));
-}
-
-static void
-vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
- const struct vlc_input_event_vout *ev)
-{
- assert(ev->vout);
- assert(ev->id);
-
- static const char osd_vars[][sizeof("secondary-sub-margin")] = {
- "aspect-ratio", "autoscale", "crop", "crop-bottom",
- "crop-top", "crop-left", "crop-right", "deinterlace",
- "deinterlace-mode", "sub-margin", "secondary-sub-margin", "zoom"
- };
-
- vlc_player_t *player = input->player;
-
- struct vlc_player_track_priv *trackpriv =
- vlc_player_input_FindTrackById(input, ev->id, NULL);
- if (!trackpriv)
- return;
-
- const bool is_video_es = trackpriv->t.fmt.i_cat == VIDEO_ES;
-
- switch (ev->action)
- {
- case VLC_INPUT_EVENT_VOUT_ADDED:
- trackpriv->vout = ev->vout;
- vlc_player_SendEvent(player, on_vout_changed,
- VLC_PLAYER_VOUT_STARTED, ev->vout,
- ev->order, ev->id);
-
- if (is_video_es)
- {
- /* Register vout callbacks after the vout list event */
- var_AddCallback(ev->vout, "fullscreen",
- vlc_player_VoutCallback, player);
- var_AddCallback(ev->vout, "video-wallpaper",
- vlc_player_VoutCallback, player);
- for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i)
- var_AddCallback(ev->vout, osd_vars[i],
- vlc_player_VoutOSDCallback, player);
- }
- break;
- case VLC_INPUT_EVENT_VOUT_DELETED:
- if (is_video_es)
- {
- /* Un-register vout callbacks before the vout list event */
- var_DelCallback(ev->vout, "fullscreen",
- vlc_player_VoutCallback, player);
- var_DelCallback(ev->vout, "video-wallpaper",
- vlc_player_VoutCallback, player);
- for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i)
- var_DelCallback(ev->vout, osd_vars[i],
- vlc_player_VoutOSDCallback, player);
- }
-
- trackpriv->vout = NULL;
- vlc_player_SendEvent(player, on_vout_changed,
- VLC_PLAYER_VOUT_STOPPED, ev->vout,
- VLC_VOUT_ORDER_NONE, ev->id);
- break;
- default:
- vlc_assert_unreachable();
- }
-}
-
-static void
-vlc_player_input_HandleStateEvent(struct vlc_player_input *input,
- input_state_e state, vlc_tick_t state_date)
-{
- switch (state)
- {
- case OPENING_S:
- vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED,
- VLC_TICK_INVALID);
- break;
- case PLAYING_S:
- vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING,
- state_date);
- break;
- case PAUSE_S:
- vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED,
- state_date);
- break;
- case END_S:
- vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
- VLC_TICK_INVALID);
- vlc_player_destructor_AddStoppingInput(input->player, input);
- break;
- case ERROR_S:
- /* Don't send errors if the input is stopped by the user */
- if (input->started)
- {
- /* Contrary to the input_thead_t, an error is not a state */
- input->error = VLC_PLAYER_ERROR_GENERIC;
- vlc_player_SendEvent(input->player, on_error_changed, input->error);
- }
- break;
- default:
- vlc_assert_unreachable();
- }
-}
-
-static void
-input_thread_Events(input_thread_t *input_thread,
- const struct vlc_input_event *event, void *user_data)
-{
- struct vlc_player_input *input = user_data;
- vlc_player_t *player = input->player;
-
- assert(input_thread == input->thread);
-
- vlc_mutex_lock(&player->lock);
-
- switch (event->type)
- {
- case INPUT_EVENT_STATE:
- vlc_player_input_HandleStateEvent(input, event->state.value,
- event->state.date);
- break;
- case INPUT_EVENT_RATE:
- input->rate = event->rate;
- vlc_player_SendEvent(player, on_rate_changed, input->rate);
- break;
- case INPUT_EVENT_CAPABILITIES:
- {
- int old_caps = input->capabilities;
- input->capabilities = event->capabilities;
- vlc_player_SendEvent(player, on_capabilities_changed,
- old_caps, input->capabilities);
- break;
- }
- case INPUT_EVENT_TIMES:
- if (event->times.ms != VLC_TICK_INVALID
- && (input->time != event->times.ms
- || input->position != event->times.percentage))
- {
- input->time = event->times.ms;
- input->position = event->times.percentage;
- vlc_player_SendEvent(player, on_position_changed,
- input->time, input->position);
-
- vlc_player_input_UpdateTime(input);
- }
- if (input->length != event->times.length)
- {
- input->length = event->times.length;
- vlc_player_SendEvent(player, on_length_changed, input->length);
- }
- break;
- case INPUT_EVENT_PROGRAM:
- vlc_player_input_HandleProgramEvent(input, &event->program);
- break;
- case INPUT_EVENT_ES:
- vlc_player_input_HandleEsEvent(input, &event->es);
- break;
- case INPUT_EVENT_TITLE:
- vlc_player_input_HandleTitleEvent(input, &event->title);
- break;
- case INPUT_EVENT_CHAPTER:
- vlc_player_input_HandleChapterEvent(input, &event->chapter);
- break;
- case INPUT_EVENT_RECORD:
- input->recording = event->record;
- vlc_player_SendEvent(player, on_recording_changed, input->recording);
- break;
- case INPUT_EVENT_STATISTICS:
- input->stats = *event->stats;
- vlc_player_SendEvent(player, on_statistics_changed, &input->stats);
- break;
- case INPUT_EVENT_SIGNAL:
- input->signal_quality = event->signal.quality;
- input->signal_strength = event->signal.strength;
- vlc_player_SendEvent(player, on_signal_changed,
- input->signal_quality, input->signal_strength);
- break;
- case INPUT_EVENT_CACHE:
- input->cache = event->cache;
- vlc_player_SendEvent(player, on_buffering_changed, event->cache);
- break;
- case INPUT_EVENT_VOUT:
- vlc_player_input_HandleVoutEvent(input, &event->vout);
- break;
- case INPUT_EVENT_ITEM_META:
- vlc_player_SendEvent(player, on_media_meta_changed,
- input_GetItem(input->thread));
- break;
- case INPUT_EVENT_ITEM_EPG:
- vlc_player_SendEvent(player, on_media_epg_changed,
- input_GetItem(input->thread));
- break;
- case INPUT_EVENT_SUBITEMS:
- vlc_player_SendEvent(player, on_media_subitems_changed,
- input_GetItem(input->thread), event->subitems);
- break;
- case INPUT_EVENT_DEAD:
- if (input->started) /* Can happen with early input_thread fails */
- vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
- VLC_TICK_INVALID);
- vlc_player_destructor_AddJoinableInput(player, input);
- break;
- case INPUT_EVENT_VBI_PAGE:
- input->teletext_page = event->vbi_page < 999 ? event->vbi_page : 100;
- vlc_player_SendEvent(player, on_teletext_page_changed,
- input->teletext_page);
- break;
- case INPUT_EVENT_VBI_TRANSPARENCY:
- input->teletext_transparent = event->vbi_transparent;
- vlc_player_SendEvent(player, on_teletext_transparency_changed,
- input->teletext_transparent);
- break;
- default:
- break;
- }
+ if (!input)
+ return;
+ input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_SEEKPOINT,
+ &(vlc_value_t){ .i_int = index });
+ vlc_player_osd_Message(player, _("Chapter %ld"), index);
+}
- vlc_mutex_unlock(&player->lock);
+void
+vlc_player_SelectNextChapter(vlc_player_t *player)
+{
+ struct vlc_player_input *input = vlc_player_get_input_locked(player);
+ if (!input)
+ return;
+ input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_NEXT, NULL);
+ vlc_player_osd_Message(player, _("Next chapter"));
+}
+
+void
+vlc_player_SelectPrevChapter(vlc_player_t *player)
+{
+ struct vlc_player_input *input = vlc_player_get_input_locked(player);
+ if (!input)
+ return;
+ input_ControlPush(input->thread, INPUT_CONTROL_SET_SEEKPOINT_PREV, NULL);
+ vlc_player_osd_Message(player, _("Previous chapter"));
}
void
@@ -2542,7 +1117,7 @@ vlc_player_Start(vlc_player_t *player)
if (ret == VLC_SUCCESS)
{
player->started = true;
- vlc_player_vout_OSDIcon(player, OSD_PLAY_ICON);
+ vlc_player_osd_Icon(player, OSD_PLAY_ICON);
}
return ret;
}
@@ -2594,7 +1169,7 @@ vlc_player_SetPause(vlc_player_t *player, bool pause)
vlc_value_t val = { .i_int = pause ? PAUSE_S : PLAYING_S };
input_ControlPushHelper(input->thread, INPUT_CONTROL_SET_STATE, &val);
- vlc_player_vout_OSDIcon(player, pause ? OSD_PAUSE_ICON : OSD_PLAY_ICON);
+ vlc_player_osd_Icon(player, pause ? OSD_PAUSE_ICON : OSD_PLAY_ICON);
}
void
@@ -2745,63 +1320,13 @@ vlc_player_assert_seek_params(enum vlc_player_seek_speed speed,
(void) speed; (void) whence;
}
-static void
-vlc_player_vout_OSDPosition(vlc_player_t *player,
- struct vlc_player_input *input, vlc_tick_t time,
- float position, enum vlc_player_whence whence)
-{
- if (input->length != VLC_TICK_INVALID)
- {
- if (time == VLC_TICK_INVALID)
- time = position * input->length;
- else
- position = time / (float) input->length;
- }
-
- size_t count;
- vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
-
- if (time != VLC_TICK_INVALID)
- {
- if (whence == VLC_PLAYER_WHENCE_RELATIVE)
- {
- time += vlc_player_input_GetTime(input); /* XXX: TOCTOU */
- if (time < 0)
- time = 0;
- }
-
- char time_text[MSTRTIME_MAX_SIZE];
- secstotimestr(time_text, SEC_FROM_VLC_TICK(time));
- if (input->length != VLC_TICK_INVALID)
- {
- char len_text[MSTRTIME_MAX_SIZE];
- secstotimestr(len_text, SEC_FROM_VLC_TICK(input->length));
- vouts_osd_Message(vouts, count, "%s / %s", time_text, len_text);
- }
- else
- vouts_osd_Message(vouts, count, "%s", time_text);
- }
-
- if (vlc_player_vout_IsFullscreen(player))
- {
- if (whence == VLC_PLAYER_WHENCE_RELATIVE)
- {
- position += vlc_player_input_GetPos(input); /* XXX: TOCTOU */
- if (position < 0.f)
- position = 0.f;
- }
- vouts_osd_Slider(vouts, count, position * 100, OSD_HOR_SLIDER);
- }
- vlc_player_vout_OSDReleaseAll(player, vouts, count);
-}
-
void
vlc_player_DisplayPosition(vlc_player_t *player)
{
struct vlc_player_input *input = vlc_player_get_input_locked(player);
if (!input)
return;
- vlc_player_vout_OSDPosition(player, input,
+ vlc_player_osd_Position(player, input,
vlc_player_input_GetTime(input),
vlc_player_input_GetPos(input),
VLC_PLAYER_WHENCE_ABSOLUTE);
@@ -2827,7 +1352,7 @@ vlc_player_SeekByPos(vlc_player_t *player, float position,
.pos.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
});
- vlc_player_vout_OSDPosition(player, input, VLC_TICK_INVALID, position,
+ vlc_player_osd_Position(player, input, VLC_TICK_INVALID, position,
whence);
}
@@ -2851,7 +1376,7 @@ vlc_player_SeekByTime(vlc_player_t *player, vlc_tick_t time,
.time.b_fast_seek = speed == VLC_PLAYER_SEEK_FAST,
});
- vlc_player_vout_OSDPosition(player, input, time, -1, whence);
+ vlc_player_osd_Position(player, input, time, -1, whence);
}
void
@@ -3247,386 +1772,6 @@ vlc_player_CorkCallback(vlc_object_t *this, const char *var,
(void) this; (void) var;
}
-audio_output_t *
-vlc_player_aout_Hold(vlc_player_t *player)
-{
- return input_resource_HoldAout(player->resource);
-}
-
-vlc_player_aout_listener_id *
-vlc_player_aout_AddListener(vlc_player_t *player,
- const struct vlc_player_aout_cbs *cbs,
- void *cbs_data)
-{
- assert(cbs);
-
- vlc_player_aout_listener_id *listener = malloc(sizeof(*listener));
- if (!listener)
- return NULL;
-
- listener->cbs = cbs;
- listener->cbs_data = cbs_data;
-
- vlc_mutex_lock(&player->aout_listeners_lock);
- vlc_list_append(&listener->node, &player->aout_listeners);
- vlc_mutex_unlock(&player->aout_listeners_lock);
-
- return listener;
-}
-
-void
-vlc_player_aout_RemoveListener(vlc_player_t *player,
- vlc_player_aout_listener_id *id)
-{
- assert(id);
-
- vlc_mutex_lock(&player->aout_listeners_lock);
- vlc_list_remove(&id->node);
- vlc_mutex_unlock(&player->aout_listeners_lock);
- free(id);
-}
-
-static void
-vlc_player_vout_OSDVolume(vlc_player_t *player, bool mute_action)
-{
- size_t count;
- vout_thread_t **vouts = vlc_player_vout_OSDHoldAll(player, &count);
-
- bool mute = vlc_player_aout_IsMuted(player);
- int volume = lroundf(vlc_player_aout_GetVolume(player) * 100.f);
- if (mute_action && mute)
- vouts_osd_Icon(vouts, count, OSD_MUTE_ICON);
- else
- {
- if (vlc_player_vout_IsFullscreen(player))
- vouts_osd_Slider(vouts, count, volume, OSD_VERT_SLIDER);
- vouts_osd_Message(vouts, count, _("Volume: %ld%%"), volume);
- }
-
- vlc_player_vout_OSDReleaseAll(player, vouts, count);
-}
-
-static int
-vlc_player_AoutCallback(vlc_object_t *this, const char *var,
- vlc_value_t oldval, vlc_value_t newval, void *data)
-{
- vlc_player_t *player = data;
-
- if (strcmp(var, "volume") == 0)
- {
- if (oldval.f_float != newval.f_float)
- {
- vlc_player_aout_SendEvent(player, on_volume_changed, newval.f_float);
- vlc_player_vout_OSDVolume(player, false);
- }
- }
- else if (strcmp(var, "mute") == 0)
- {
- if (oldval.b_bool != newval.b_bool)
- {
- vlc_player_aout_SendEvent(player, on_mute_changed, newval.b_bool);
- vlc_player_vout_OSDVolume(player, true);
- }
- }
- else if (strcmp(var, "device") == 0)
- {
- const char *old = oldval.psz_string;
- const char *new = newval.psz_string;
- /* support NULL values for string comparison */
- if (old != new && (!old || !new || strcmp(old, new)))
- vlc_player_aout_SendEvent(player, on_device_changed,
- newval.psz_string);
- }
- else
- vlc_assert_unreachable();
-
- return VLC_SUCCESS;
- (void) this;
-}
-
-float
-vlc_player_aout_GetVolume(vlc_player_t *player)
-{
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (!aout)
- return -1.f;
- float vol = aout_VolumeGet(aout);
- aout_Release(aout);
-
- return vol;
-}
-
-int
-vlc_player_aout_SetVolume(vlc_player_t *player, float volume)
-{
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (!aout)
- return -1;
- int ret = aout_VolumeSet(aout, volume);
- aout_Release(aout);
-
- return ret;
-}
-
-int
-vlc_player_aout_IncrementVolume(vlc_player_t *player, int steps, float *result)
-{
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (!aout)
- return -1;
- int ret = aout_VolumeUpdate(aout, steps, result);
- aout_Release(aout);
-
- return ret;
-}
-
-int
-vlc_player_aout_IsMuted(vlc_player_t *player)
-{
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (!aout)
- return -1;
- int ret = aout_MuteGet(aout);
- aout_Release(aout);
-
- return ret;
-}
-
-int
-vlc_player_aout_Mute(vlc_player_t *player, bool mute)
-{
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (!aout)
- return -1;
- int ret = aout_MuteSet (aout, mute);
- aout_Release(aout);
-
- return ret;
-}
-
-
-int
-vlc_player_aout_EnableFilter(vlc_player_t *player, const char *name, bool add)
-{
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (!aout)
- return -1;
- aout_EnableFilter(aout, name, add);
- aout_Release(aout);
-
- return 0;
-}
-
-vout_thread_t *
-vlc_player_vout_Hold(vlc_player_t *player)
-{
- vout_thread_t *vout = input_resource_HoldVout(player->resource);
- return vout ? vout : input_resource_HoldDummyVout(player->resource);
-}
-
-vout_thread_t **
-vlc_player_vout_HoldAll(vlc_player_t *player, size_t *count)
-{
- vout_thread_t **vouts;
- input_resource_HoldVouts(player->resource, &vouts, count);
-
- if (*count == 0)
- {
- vouts = vlc_alloc(1, sizeof(*vouts));
- if (vouts)
- {
- *count = 1;
- vouts[0] = input_resource_HoldDummyVout(player->resource);
- }
- }
- return vouts;
-}
-
-vlc_player_vout_listener_id *
-vlc_player_vout_AddListener(vlc_player_t *player,
- const struct vlc_player_vout_cbs *cbs,
- void *cbs_data)
-{
- assert(cbs);
-
- vlc_player_vout_listener_id *listener = malloc(sizeof(*listener));
- if (!listener)
- return NULL;
-
- listener->cbs = cbs;
- listener->cbs_data = cbs_data;
-
- vlc_mutex_lock(&player->vout_listeners_lock);
- vlc_list_append(&listener->node, &player->vout_listeners);
- vlc_mutex_unlock(&player->vout_listeners_lock);
-
- return listener;
-}
-
-void
-vlc_player_vout_RemoveListener(vlc_player_t *player,
- vlc_player_vout_listener_id *id)
-{
- assert(id);
-
- vlc_mutex_lock(&player->vout_listeners_lock);
- vlc_list_remove(&id->node);
- vlc_mutex_unlock(&player->vout_listeners_lock);
- free(id);
-}
-
-bool
-vlc_player_vout_IsFullscreen(vlc_player_t *player)
-{
- return var_GetBool(player, "fullscreen");
-}
-
-static int
-vlc_player_VoutCallback(vlc_object_t *this, const char *var,
- vlc_value_t oldval, vlc_value_t newval, void *data)
-{
- vlc_player_t *player = data;
-
- if (strcmp(var, "fullscreen") == 0)
- {
- if (oldval.b_bool != newval.b_bool )
- vlc_player_vout_SendEvent(player, on_fullscreen_changed,
- (vout_thread_t *)this, newval.b_bool);
- }
- else if (strcmp(var, "video-wallpaper") == 0)
- {
- if (oldval.b_bool != newval.b_bool )
- vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed,
- (vout_thread_t *)this, newval.b_bool);
- }
- else
- vlc_assert_unreachable();
-
- return VLC_SUCCESS;
-}
-
-static bool
-vout_osd_PrintVariableText(vout_thread_t *vout, const char *varname, int vartype,
- vlc_value_t varval, const char *osdfmt)
-{
- bool found = false;
- bool isvarstring = vartype == VLC_VAR_STRING;
- size_t num_choices;
- vlc_value_t *choices;
- char **choices_text;
- var_Change(vout, varname, VLC_VAR_GETCHOICES,
- &num_choices, &choices, &choices_text);
- for (size_t i = 0; i < num_choices; ++i)
- {
- if (!found)
- if ((isvarstring &&
- strcmp(choices[i].psz_string, varval.psz_string) == 0) ||
- (!isvarstring && choices[i].f_float == varval.f_float))
- {
- vouts_osd_Message(&vout, 1, osdfmt, choices_text[i]);
- found = true;
- }
- if (isvarstring)
- free(choices[i].psz_string);
- free(choices_text[i]);
- }
- free(choices);
- free(choices_text);
- return found;
-}
-
-static int
-vlc_player_VoutOSDCallback(vlc_object_t *this, const char *var,
- vlc_value_t oldval, vlc_value_t newval, void *data)
-{
- VLC_UNUSED(oldval);
-
- vout_thread_t *vout = (vout_thread_t *)this;
-
- if (strcmp(var, "aspect-ratio") == 0)
- vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING,
- newval, _("Aspect ratio: %s"));
-
- else if (strcmp(var, "autoscale") == 0)
- vouts_osd_Message(&vout, 1, newval.b_bool ?
- _("Scaled to screen") : _("Original size"));
-
- else if (strcmp(var, "crop") == 0)
- vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING, newval,
- _("Crop: %s"));
-
- else if (strcmp(var, "crop-bottom") == 0)
- vouts_osd_Message(&vout, 1, _("Bottom crop: %d px"), newval.i_int);
-
- else if (strcmp(var, "crop-top") == 0)
- vouts_osd_Message(&vout, 1, _("Top crop: %d px"), newval.i_int);
-
- else if (strcmp(var, "crop-left") == 0)
- vouts_osd_Message(&vout, 1, _("Left crop: %d px"), newval.i_int);
-
- else if (strcmp(var, "crop-right") == 0)
- vouts_osd_Message(&vout, 1, _("Right crop: %d px"), newval.i_int);
-
- else if (strcmp(var, "deinterlace") == 0 ||
- strcmp(var, "deinterlace-mode") == 0)
- {
- bool varmode = strcmp(var, "deinterlace-mode") == 0;
- int on = !varmode ?
- newval.i_int : var_GetInteger(vout, "deinterlace");
- char *mode = varmode ?
- newval.psz_string : var_GetString(vout, "deinterlace-mode");
- vouts_osd_Message(&vout, 1, _("Deinterlace %s (%s)"),
- on == 1 ? _("On") : _("Off"), mode);
- if (!varmode)
- free(mode);
- }
-
- else if (strcmp(var, "sub-margin") == 0)
- vouts_osd_Message(&vout, 1, _("Subtitle position %d px"), newval.i_int);
-
- else if (strcmp(var, "secondary-sub-margin") == 0)
- vouts_osd_Message(&vout, 1, _("Secondary subtitle position %d px"), newval.i_int);
-
- else if (strcmp(var, "sub-text-scale") == 0)
- vouts_osd_Message(&vout, 1, _("Subtitle text scale %d%%"), newval.i_int);
-
- else if (strcmp(var, "zoom") == 0)
- {
- if (newval.f_float == 1.f)
- vouts_osd_Message(&vout, 1, _("Zooming reset"));
- else
- {
- bool found = vout_osd_PrintVariableText(vout, var, VLC_VAR_FLOAT,
- newval, _("Zoom mode: %s"));
- if (!found)
- vouts_osd_Message(&vout, 1, _("Zoom: x%f"), newval.f_float);
- }
- }
-
- (void) data;
- return VLC_SUCCESS;
-}
-
-static void
-vlc_player_vout_SetVar(vlc_player_t *player, const char *name, int type,
- vlc_value_t val)
-{
- vout_thread_t *vout = vlc_player_vout_Hold(player);
- var_SetChecked(vout, name, type, val);
- vout_Release(vout);
-}
-
-
-static void
-vlc_player_vout_TriggerOption(vlc_player_t *player, const char *option)
-{
- /* Don't use vlc_player_vout_Hold() since there is nothing to trigger if it
- * returns a dummy vout */
- vout_thread_t *vout = input_resource_HoldVout(player->resource);
- var_TriggerCallback(vout, option);
- vout_Release(vout);
-}
-
vlc_object_t *
vlc_player_GetV4l2Object(vlc_player_t *player)
{
@@ -3635,34 +1780,6 @@ vlc_player_GetV4l2Object(vlc_player_t *player)
(vlc_object_t*) input->thread : NULL;
}
-void
-vlc_player_vout_SetFullscreen(vlc_player_t *player, bool enabled)
-{
- vlc_player_vout_SetVar(player, "fullscreen", VLC_VAR_BOOL,
- (vlc_value_t) { .b_bool = enabled });
- vlc_player_vout_SendEvent(player, on_fullscreen_changed, NULL, enabled);
-}
-
-bool
-vlc_player_vout_IsWallpaperModeEnabled(vlc_player_t *player)
-{
- return var_GetBool(player, "video-wallpaper");
-}
-
-void
-vlc_player_vout_SetWallpaperModeEnabled(vlc_player_t *player, bool enabled)
-{
- vlc_player_vout_SetVar(player, "video-wallpaper", VLC_VAR_BOOL,
- (vlc_value_t) { .b_bool = enabled });
- vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed, NULL, enabled);
-}
-
-void
-vlc_player_vout_Snapshot(vlc_player_t *player)
-{
- vlc_player_vout_TriggerOption(player, "video-snapshot");
-}
-
static void
vlc_player_InitLocks(vlc_player_t *player, enum vlc_player_lock_type lock_type)
{
@@ -3713,14 +1830,9 @@ vlc_player_Delete(vlc_player_t *player)
vlc_player_DestroyLocks(player);
- audio_output_t *aout = vlc_player_aout_Hold(player);
- if (aout)
- {
- var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
- var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
- var_DelCallback(player, "corks", vlc_player_CorkCallback, NULL);
- aout_Release(aout);
- }
+ vlc_player_aout_DelCallbacks(player);
+ var_DelCallback(player, "corks", vlc_player_CorkCallback, NULL);
+
input_resource_Release(player->resource);
if (player->renderer)
vlc_renderer_item_release(player->renderer);
@@ -3808,16 +1920,14 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type,
if (!player->resource)
goto error;
-
+ /* Ensure the player has a valid aout */
aout = input_resource_GetAout(player->resource);
if (aout != NULL)
{
- var_AddCallback(aout, "volume", vlc_player_AoutCallback, player);
- var_AddCallback(aout, "mute", vlc_player_AoutCallback, player);
- var_AddCallback(aout, "device", vlc_player_AoutCallback, player);
- var_AddCallback(player, "corks", vlc_player_CorkCallback, NULL);
+ vlc_player_aout_AddCallbacks(player);
input_resource_PutAout(player->resource, aout);
}
+ var_AddCallback(player, "corks", vlc_player_CorkCallback, NULL);
player->deleting = false;
vlc_player_InitLocks(player, lock_type);
@@ -3833,12 +1943,8 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type,
error:
if (aout)
- {
- var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
- var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
- var_DelCallback(aout, "device", vlc_player_AoutCallback, player);
- var_DelCallback(player, "corks", vlc_player_AoutCallback, NULL);
- }
+ vlc_player_aout_DelCallbacks(player);
+ var_DelCallback(player, "corks", vlc_player_CorkCallback, NULL);
if (player->resource)
input_resource_Release(player->resource);
diff --git a/src/input/player.h b/src/input/player.h
index f65be833ba..5ad169fd28 100644
--- a/src/input/player.h
+++ b/src/input/player.h
@@ -1,7 +1,7 @@
/*****************************************************************************
* player.h: Player internal interface
*****************************************************************************
- * Copyright © 2018 VLC authors and VideoLAN
+ * Copyright © 2018-2019 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
@@ -22,17 +22,348 @@
#define VLC_PLAYER_INTERNAL_H
#include <vlc_player.h>
+#include <vlc_list.h>
+#include <vlc_vector.h>
+#include <vlc_atomic.h>
-/**
+#include "input_internal.h"
+
+struct vlc_player_track_priv
+{
+ struct vlc_player_track t;
+ vout_thread_t *vout; /* weak reference */
+ vlc_tick_t delay;
+ /* only valid if selected and if category is VIDEO_ES or SPU_ES */
+ enum vlc_vout_order vout_order;
+};
+
+typedef struct VLC_VECTOR(struct vlc_player_program *)
+ vlc_player_program_vector;
+
+typedef struct VLC_VECTOR(struct vlc_player_track_priv *)
+ vlc_player_track_vector;
+
+struct vlc_player_title_list
+{
+ vlc_atomic_rc_t rc;
+ size_t count;
+ struct vlc_player_title array[];
+};
+
+struct vlc_player_input
+{
+ input_thread_t *thread;
+ vlc_player_t *player;
+ bool started;
+
+ enum vlc_player_state state;
+ enum vlc_player_error error;
+ float rate;
+ double output_rate;
+ int capabilities;
+ vlc_tick_t length;
+
+ float position;
+ vlc_tick_t time;
+ vlc_tick_t input_time_date;
+
+ vlc_tick_t output_time;
+ vlc_tick_t output_time_date;
+
+ vlc_tick_t pause_date;
+
+ bool recording;
+
+ float signal_quality;
+ float signal_strength;
+ float cache;
+
+ struct input_stats_t stats;
+
+ vlc_tick_t cat_delays[DATA_ES];
+
+ vlc_player_program_vector program_vector;
+ vlc_player_track_vector video_track_vector;
+ vlc_player_track_vector audio_track_vector;
+ vlc_player_track_vector spu_track_vector;
+ struct vlc_player_track_priv *teletext_menu;
+
+ struct vlc_player_title_list *titles;
+
+ size_t title_selected;
+ size_t chapter_selected;
+
+ struct vlc_list node;
+
+ bool teletext_enabled;
+ bool teletext_transparent;
+ unsigned teletext_page;
+
+ struct
+ {
+ vlc_tick_t time;
+ float pos;
+ bool set;
+ } abloop_state[2];
+};
+
+struct vlc_player_listener_id
+{
+ const struct vlc_player_cbs *cbs;
+ void *cbs_data;
+ struct vlc_list node;
+};
+
+struct vlc_player_vout_listener_id
+{
+ const struct vlc_player_vout_cbs *cbs;
+ void *cbs_data;
+ struct vlc_list node;
+};
+
+struct vlc_player_aout_listener_id
+{
+ const struct vlc_player_aout_cbs *cbs;
+ void *cbs_data;
+ struct vlc_list node;
+};
+
+struct vlc_player_t
+{
+ struct vlc_object_t obj;
+ vlc_mutex_t lock;
+ vlc_mutex_t aout_listeners_lock;
+ vlc_mutex_t vout_listeners_lock;
+ vlc_cond_t start_delay_cond;
+
+ enum vlc_player_media_stopped_action media_stopped_action;
+ bool start_paused;
+
+ const struct vlc_player_media_provider *media_provider;
+ void *media_provider_data;
+
+ bool pause_on_cork;
+ bool corked;
+
+ struct vlc_list listeners;
+ struct vlc_list aout_listeners;
+ struct vlc_list vout_listeners;
+
+ input_resource_t *resource;
+ vlc_renderer_item_t *renderer;
+
+ input_item_t *media;
+ struct vlc_player_input *input;
+
+ bool releasing_media;
+ bool next_media_requested;
+ input_item_t *next_media;
+
+ enum vlc_player_state global_state;
+ bool started;
+
+ unsigned error_count;
+
+ bool deleting;
+ struct
+ {
+ vlc_thread_t thread;
+ vlc_cond_t wait;
+ vlc_cond_t notify;
+ struct vlc_list inputs;
+ struct vlc_list stopping_inputs;
+ struct vlc_list joinable_inputs;
+ } destructor;
+};
+
+/*
* Assert that the player mutex is locked.
*
* This is exposed in this internal header because the playlist and its
* associated player share the lock to avoid lock-order inversion issues.
*/
-void
-vlc_player_assert_locked(vlc_player_t *player);
+static inline void
+vlc_player_assert_locked(vlc_player_t *player)
+{
+ assert(player);
+ vlc_mutex_assert(&player->lock);
+}
+
+static inline struct vlc_player_input *
+vlc_player_get_input_locked(vlc_player_t *player)
+{
+ vlc_player_assert_locked(player);
+ return player->input;
+}
+
+#define vlc_player_SendEvent(player, event, ...) do { \
+ vlc_player_listener_id *listener; \
+ vlc_list_foreach(listener, &player->listeners, node) \
+ { \
+ if (listener->cbs->event) \
+ listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
+ } \
+} while(0)
+
+static inline const char *
+es_format_category_to_string(enum es_format_category_e cat)
+{
+ switch (cat)
+ {
+ case VIDEO_ES: return "Video";
+ case AUDIO_ES: return "Audio";
+ case SPU_ES: return "Subtitle";
+ default: return NULL;
+ }
+}
+
+/*
+ * player.c
+ */
vlc_object_t *
vlc_player_GetObject(vlc_player_t *player);
+int
+vlc_player_OpenNextMedia(vlc_player_t *player);
+
+void
+vlc_player_PrepareNextMedia(vlc_player_t *player);
+
+void
+vlc_player_destructor_AddStoppingInput(vlc_player_t *player,
+ struct vlc_player_input *input);
+
+void
+vlc_player_destructor_AddJoinableInput(vlc_player_t *player,
+ struct vlc_player_input *input);
+
+/*
+ * player_track.c
+ */
+
+struct vlc_player_program *
+vlc_player_program_New(int id, const char *name);
+
+int
+vlc_player_program_Update(struct vlc_player_program *prgm, int id,
+ const char *name);
+
+struct vlc_player_program *
+vlc_player_program_vector_FindById(vlc_player_program_vector *vec, int id,
+ size_t *idx);
+
+struct vlc_player_track_priv *
+vlc_player_track_priv_New(vlc_es_id_t *id, const char *name, const es_format_t *fmt);
+
+void
+vlc_player_track_priv_Delete(struct vlc_player_track_priv *trackpriv);
+
+int
+vlc_player_track_priv_Update(struct vlc_player_track_priv *trackpriv,
+ const char *name, const es_format_t *fmt);
+
+struct vlc_player_track_priv *
+vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t *id,
+ size_t *idx);
+
+/*
+ * player_title.c
+ */
+
+struct vlc_player_title_list *
+vlc_player_title_list_Create(input_title_t *const *array, size_t count,
+ int title_offset, int chapter_offset);
+
+/*
+ * player_input.c
+ */
+
+static inline vlc_player_track_vector *
+vlc_player_input_GetTrackVector(struct vlc_player_input *input,
+ enum es_format_category_e cat)
+{
+ switch (cat)
+ {
+ case VIDEO_ES:
+ return &input->video_track_vector;
+ case AUDIO_ES:
+ return &input->audio_track_vector;
+ case SPU_ES:
+ return &input->spu_track_vector;
+ default:
+ return NULL;
+ }
+}
+
+struct vlc_player_track_priv *
+vlc_player_input_FindTrackById(struct vlc_player_input *input, vlc_es_id_t *id,
+ size_t *idx);
+
+struct vlc_player_input *
+vlc_player_input_New(vlc_player_t *player, input_item_t *item);
+
+void
+vlc_player_input_Delete(struct vlc_player_input *input);
+
+vlc_tick_t
+vlc_player_input_GetTime(struct vlc_player_input *input);
+
+float
+vlc_player_input_GetPos(struct vlc_player_input *input);
+
+int
+vlc_player_input_Start(struct vlc_player_input *input);
+
+void
+vlc_player_input_HandleState(struct vlc_player_input *, enum vlc_player_state,
+ vlc_tick_t);
+
+/*
+ * player_vout.c
+ */
+
+void
+vlc_player_vout_AddCallbacks(vlc_player_t *player, vout_thread_t *vout);
+
+void
+vlc_player_vout_DelCallbacks(vlc_player_t *player, vout_thread_t *vout);
+
+/*
+ * player_aout.c
+ */
+
+void
+vlc_player_aout_AddCallbacks(vlc_player_t *player);
+
+void
+vlc_player_aout_DelCallbacks(vlc_player_t *player);
+
+/*
+ * player_osd.c
+ */
+
+void
+vlc_player_osd_Message(vlc_player_t *player, const char *fmt, ...);
+
+void
+vlc_player_osd_Icon(vlc_player_t *player, short type);
+
+void
+vlc_player_osd_Position(vlc_player_t *player,
+ struct vlc_player_input *input, vlc_tick_t time,
+ float position, enum vlc_player_whence whence);
+void
+vlc_player_osd_Volume(vlc_player_t *player, bool mute_action);
+
+int
+vlc_player_vout_OSDCallback(vlc_object_t *this, const char *var,
+ vlc_value_t oldval, vlc_value_t newval, void *data);
+
+void
+vlc_player_osd_Track(vlc_player_t *player, vlc_es_id_t *id, bool select);
+
+void
+vlc_player_osd_Program(vlc_player_t *player, const char *name);
+
#endif
diff --git a/src/input/player_aout.c b/src/input/player_aout.c
new file mode 100644
index 0000000000..9c4cf84dd5
--- /dev/null
+++ b/src/input/player_aout.c
@@ -0,0 +1,219 @@
+/*****************************************************************************
+ * player.c: Player interface
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include <vlc_decoder.h>
+#include "player.h"
+#include "resource.h"
+
+#define vlc_player_aout_SendEvent(player, event, ...) do { \
+ vlc_mutex_lock(&player->aout_listeners_lock); \
+ vlc_player_aout_listener_id *listener; \
+ vlc_list_foreach(listener, &player->aout_listeners, node) \
+ { \
+ if (listener->cbs->event) \
+ listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
+ } \
+ vlc_mutex_unlock(&player->aout_listeners_lock); \
+} while(0)
+
+audio_output_t *
+vlc_player_aout_Hold(vlc_player_t *player)
+{
+ return input_resource_HoldAout(player->resource);
+}
+
+vlc_player_aout_listener_id *
+vlc_player_aout_AddListener(vlc_player_t *player,
+ const struct vlc_player_aout_cbs *cbs,
+ void *cbs_data)
+{
+ assert(cbs);
+
+ vlc_player_aout_listener_id *listener = malloc(sizeof(*listener));
+ if (!listener)
+ return NULL;
+
+ listener->cbs = cbs;
+ listener->cbs_data = cbs_data;
+
+ vlc_mutex_lock(&player->aout_listeners_lock);
+ vlc_list_append(&listener->node, &player->aout_listeners);
+ vlc_mutex_unlock(&player->aout_listeners_lock);
+
+ return listener;
+}
+
+void
+vlc_player_aout_RemoveListener(vlc_player_t *player,
+ vlc_player_aout_listener_id *id)
+{
+ assert(id);
+
+ vlc_mutex_lock(&player->aout_listeners_lock);
+ vlc_list_remove(&id->node);
+ vlc_mutex_unlock(&player->aout_listeners_lock);
+ free(id);
+}
+
+static int
+vlc_player_AoutCallback(vlc_object_t *this, const char *var,
+ vlc_value_t oldval, vlc_value_t newval, void *data)
+{
+ vlc_player_t *player = data;
+
+ if (strcmp(var, "volume") == 0)
+ {
+ if (oldval.f_float != newval.f_float)
+ {
+ vlc_player_aout_SendEvent(player, on_volume_changed, newval.f_float);
+ vlc_player_osd_Volume(player, false);
+ }
+ }
+ else if (strcmp(var, "mute") == 0)
+ {
+ if (oldval.b_bool != newval.b_bool)
+ {
+ vlc_player_aout_SendEvent(player, on_mute_changed, newval.b_bool);
+ vlc_player_osd_Volume(player, true);
+ }
+ }
+ else if (strcmp(var, "device") == 0)
+ {
+ const char *old = oldval.psz_string;
+ const char *new = newval.psz_string;
+ /* support NULL values for string comparison */
+ if (old != new && (!old || !new || strcmp(old, new)))
+ vlc_player_aout_SendEvent(player, on_device_changed,
+ newval.psz_string);
+ }
+ else
+ vlc_assert_unreachable();
+
+ return VLC_SUCCESS;
+ (void) this;
+}
+
+float
+vlc_player_aout_GetVolume(vlc_player_t *player)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return -1.f;
+ float vol = aout_VolumeGet(aout);
+ aout_Release(aout);
+
+ return vol;
+}
+
+int
+vlc_player_aout_SetVolume(vlc_player_t *player, float volume)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return -1;
+ int ret = aout_VolumeSet(aout, volume);
+ aout_Release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_IncrementVolume(vlc_player_t *player, int steps, float *result)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return -1;
+ int ret = aout_VolumeUpdate(aout, steps, result);
+ aout_Release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_IsMuted(vlc_player_t *player)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return -1;
+ int ret = aout_MuteGet(aout);
+ aout_Release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_Mute(vlc_player_t *player, bool mute)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return -1;
+ int ret = aout_MuteSet (aout, mute);
+ aout_Release(aout);
+
+ return ret;
+}
+
+int
+vlc_player_aout_EnableFilter(vlc_player_t *player, const char *name, bool add)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return -1;
+ aout_EnableFilter(aout, name, add);
+ aout_Release(aout);
+
+ return 0;
+}
+
+
+void
+vlc_player_aout_AddCallbacks(vlc_player_t *player)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return;
+
+ var_AddCallback(aout, "volume", vlc_player_AoutCallback, player);
+ var_AddCallback(aout, "mute", vlc_player_AoutCallback, player);
+ var_AddCallback(aout, "device", vlc_player_AoutCallback, player);
+
+ aout_Release(aout);
+}
+
+void
+vlc_player_aout_DelCallbacks(vlc_player_t *player)
+{
+ audio_output_t *aout = vlc_player_aout_Hold(player);
+ if (!aout)
+ return;
+
+ var_DelCallback(aout, "volume", vlc_player_AoutCallback, player);
+ var_DelCallback(aout, "mute", vlc_player_AoutCallback, player);
+ var_DelCallback(aout, "device", vlc_player_AoutCallback, player);
+
+ aout_Release(aout);
+}
diff --git a/src/input/player_input.c b/src/input/player_input.c
new file mode 100644
index 0000000000..73ececb6df
--- /dev/null
+++ b/src/input/player_input.c
@@ -0,0 +1,771 @@
+/*****************************************************************************
+ * player.c: Player interface
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_interface.h>
+#include "player.h"
+
+struct vlc_player_track_priv *
+vlc_player_input_FindTrackById(struct vlc_player_input *input, vlc_es_id_t *id,
+ size_t *idx)
+{
+ vlc_player_track_vector *vec =
+ vlc_player_input_GetTrackVector(input, vlc_es_id_GetCat(id));
+ return vec ? vlc_player_track_vector_FindById(vec, id, idx) : NULL;
+}
+
+static void
+vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, vlc_tick_t time,
+ float pos)
+{
+ vlc_player_t *player = input->player;
+
+ if (player->input != input)
+ return;
+
+ assert(input->abloop_state[0].set && input->abloop_state[1].set);
+
+ if (time != VLC_TICK_INVALID
+ && input->abloop_state[0].time != VLC_TICK_INVALID
+ && input->abloop_state[1].time != VLC_TICK_INVALID)
+ {
+ if (time >= input->abloop_state[1].time)
+ vlc_player_SetTime(player, input->abloop_state[0].time);
+ }
+ else if (pos >= input->abloop_state[1].pos)
+ vlc_player_SetPosition(player, input->abloop_state[0].pos);
+}
+
+vlc_tick_t
+vlc_player_input_GetTime(struct vlc_player_input *input)
+{
+ return input->time;
+}
+
+float
+vlc_player_input_GetPos(struct vlc_player_input *input)
+{
+ return input->position;
+}
+
+static void
+vlc_player_input_UpdateTime(struct vlc_player_input *input)
+{
+ if (input->abloop_state[0].set && input->abloop_state[1].set)
+ vlc_player_input_HandleAtoBLoop(input, vlc_player_input_GetTime(input),
+ vlc_player_input_GetPos(input));
+}
+
+int
+vlc_player_input_Start(struct vlc_player_input *input)
+{
+ int ret = input_Start(input->thread);
+ if (ret != VLC_SUCCESS)
+ return ret;
+ input->started = true;
+ return ret;
+}
+
+static bool
+vlc_player_WaitRetryDelay(vlc_player_t *player)
+{
+#define RETRY_TIMEOUT_BASE VLC_TICK_FROM_MS(100)
+#define RETRY_TIMEOUT_MAX VLC_TICK_FROM_MS(3200)
+ if (player->error_count)
+ {
+ /* Delay the next opening in case of error to avoid busy loops */
+ vlc_tick_t delay = RETRY_TIMEOUT_BASE;
+ for (unsigned i = 1; i < player->error_count
+ && delay < RETRY_TIMEOUT_MAX; ++i)
+ delay *= 2; /* Wait 100, 200, 400, 800, 1600 and finally 3200ms */
+ delay += vlc_tick_now();
+
+ while (player->error_count > 0
+ && vlc_cond_timedwait(&player->start_delay_cond, &player->lock,
+ delay) == 0);
+ if (player->error_count == 0)
+ return false; /* canceled */
+ }
+ return true;
+}
+
+void
+vlc_player_input_HandleState(struct vlc_player_input *input,
+ enum vlc_player_state state, vlc_tick_t state_date)
+{
+ vlc_player_t *player = input->player;
+
+ /* The STOPPING state can be set earlier by the player. In that case,
+ * ignore all future events except the STOPPED one */
+ if (input->state == VLC_PLAYER_STATE_STOPPING
+ && state != VLC_PLAYER_STATE_STOPPED)
+ return;
+
+ input->state = state;
+
+ /* Override the global state if the player is still playing and has a next
+ * media to play */
+ bool send_event = player->global_state != state;
+ switch (input->state)
+ {
+ case VLC_PLAYER_STATE_STOPPED:
+ assert(!input->started);
+ assert(input != player->input);
+
+ if (input->titles)
+ {
+ vlc_player_title_list_Release(input->titles);
+ input->titles = NULL;
+ vlc_player_SendEvent(player, on_titles_changed, NULL);
+ }
+
+ if (input->error != VLC_PLAYER_ERROR_NONE)
+ player->error_count++;
+ else
+ player->error_count = 0;
+
+ vlc_player_WaitRetryDelay(player);
+
+ if (!player->deleting)
+ vlc_player_OpenNextMedia(player);
+ if (!player->input)
+ player->started = false;
+
+ switch (player->media_stopped_action)
+ {
+ case VLC_PLAYER_MEDIA_STOPPED_EXIT:
+ if (player->input && player->started)
+ vlc_player_input_Start(player->input);
+ else
+ libvlc_Quit(vlc_object_instance(player));
+ break;
+ case VLC_PLAYER_MEDIA_STOPPED_CONTINUE:
+ if (player->input && player->started)
+ vlc_player_input_Start(player->input);
+ break;
+ default:
+ break;
+ }
+
+ send_event = !player->started;
+ break;
+ case VLC_PLAYER_STATE_STOPPING:
+ input->started = false;
+ if (input == player->input)
+ player->input = NULL;
+
+ if (player->started)
+ {
+ vlc_player_PrepareNextMedia(player);
+ if (!player->next_media)
+ player->started = false;
+ }
+ send_event = !player->started;
+ break;
+ case VLC_PLAYER_STATE_STARTED:
+ case VLC_PLAYER_STATE_PLAYING:
+ if (player->started &&
+ player->global_state == VLC_PLAYER_STATE_PLAYING)
+ send_event = false;
+ break;
+
+ case VLC_PLAYER_STATE_PAUSED:
+ assert(player->started && input->started);
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+
+ if (send_event)
+ {
+ player->global_state = input->state;
+ vlc_player_SendEvent(player, on_state_changed, player->global_state);
+ }
+}
+
+static void
+vlc_player_input_HandleStateEvent(struct vlc_player_input *input,
+ input_state_e state, vlc_tick_t state_date)
+{
+ switch (state)
+ {
+ case OPENING_S:
+ vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED,
+ VLC_TICK_INVALID);
+ break;
+ case PLAYING_S:
+ vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING,
+ state_date);
+ break;
+ case PAUSE_S:
+ vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED,
+ state_date);
+ break;
+ case END_S:
+ vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
+ VLC_TICK_INVALID);
+ vlc_player_destructor_AddStoppingInput(input->player, input);
+ break;
+ case ERROR_S:
+ /* Don't send errors if the input is stopped by the user */
+ if (input->started)
+ {
+ /* Contrary to the input_thead_t, an error is not a state */
+ input->error = VLC_PLAYER_ERROR_GENERIC;
+ vlc_player_SendEvent(input->player, on_error_changed, input->error);
+ }
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+static void
+vlc_player_input_HandleProgramEvent(struct vlc_player_input *input,
+ const struct vlc_input_event_program *ev)
+{
+ vlc_player_t *player = input->player;
+ struct vlc_player_program *prgm;
+ vlc_player_program_vector *vec = &input->program_vector;
+
+ switch (ev->action)
+ {
+ case VLC_INPUT_PROGRAM_ADDED:
+ prgm = vlc_player_program_New(ev->id, ev->title);
+ if (!prgm)
+ break;
+
+ if (!vlc_vector_push(vec, prgm))
+ {
+ vlc_player_program_Delete(prgm);
+ break;
+ }
+ vlc_player_SendEvent(player, on_program_list_changed,
+ VLC_PLAYER_LIST_ADDED, prgm);
+ break;
+ case VLC_INPUT_PROGRAM_DELETED:
+ {
+ size_t idx;
+ prgm = vlc_player_program_vector_FindById(vec, ev->id, &idx);
+ if (prgm)
+ {
+ vlc_player_SendEvent(player, on_program_list_changed,
+ VLC_PLAYER_LIST_REMOVED, prgm);
+ vlc_vector_remove(vec, idx);
+ vlc_player_program_Delete(prgm);
+ }
+ break;
+ }
+ case VLC_INPUT_PROGRAM_UPDATED:
+ case VLC_INPUT_PROGRAM_SCRAMBLED:
+ prgm = vlc_player_program_vector_FindById(vec, ev->id, NULL);
+ if (!prgm)
+ break;
+ if (ev->action == VLC_INPUT_PROGRAM_UPDATED)
+ {
+ if (vlc_player_program_Update(prgm, ev->id, ev->title) != 0)
+ break;
+ }
+ else
+ prgm->scrambled = ev->scrambled;
+ vlc_player_SendEvent(player, on_program_list_changed,
+ VLC_PLAYER_LIST_UPDATED, prgm);
+ break;
+ case VLC_INPUT_PROGRAM_SELECTED:
+ {
+ int unselected_id = -1, selected_id = -1;
+ vlc_vector_foreach(prgm, vec)
+ {
+ if (prgm->group_id == ev->id)
+ {
+ if (!prgm->selected)
+ {
+ assert(selected_id == -1);
+ prgm->selected = true;
+ selected_id = prgm->group_id;
+ }
+ }
+ else
+ {
+ if (prgm->selected)
+ {
+ assert(unselected_id == -1);
+ prgm->selected = false;
+ unselected_id = prgm->group_id;
+ }
+ }
+ }
+ if (unselected_id != -1 || selected_id != -1)
+ vlc_player_SendEvent(player, on_program_selection_changed,
+ unselected_id, selected_id);
+ break;
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+static void
+vlc_player_input_HandleTeletextMenu(struct vlc_player_input *input,
+ const struct vlc_input_event_es *ev)
+{
+ vlc_player_t *player = input->player;
+ switch (ev->action)
+ {
+ case VLC_INPUT_ES_ADDED:
+ if (input->teletext_menu)
+ {
+ msg_Warn(player, "Can't handle more than one teletext menu "
+ "track. Using the last one.");
+ vlc_player_track_priv_Delete(input->teletext_menu);
+ }
+ input->teletext_menu = vlc_player_track_priv_New(ev->id, ev->title,
+ ev->fmt);
+ if (!input->teletext_menu)
+ return;
+
+ vlc_player_SendEvent(player, on_teletext_menu_changed, true);
+ break;
+ case VLC_INPUT_ES_DELETED:
+ {
+ if (input->teletext_menu && input->teletext_menu->t.es_id == ev->id)
+ {
+ assert(!input->teletext_enabled);
+
+ vlc_player_track_priv_Delete(input->teletext_menu);
+ input->teletext_menu = NULL;
+ vlc_player_SendEvent(player, on_teletext_menu_changed, false);
+ }
+ break;
+ }
+ case VLC_INPUT_ES_UPDATED:
+ break;
+ case VLC_INPUT_ES_SELECTED:
+ case VLC_INPUT_ES_UNSELECTED:
+ if (input->teletext_menu->t.es_id == ev->id)
+ {
+ input->teletext_enabled = ev->action == VLC_INPUT_ES_SELECTED;
+ vlc_player_SendEvent(player, on_teletext_enabled_changed,
+ input->teletext_enabled);
+ }
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+static void
+vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
+ const struct vlc_input_event_es *ev)
+{
+ assert(ev->id && ev->title && ev->fmt);
+
+ if (ev->fmt->i_cat == SPU_ES && ev->fmt->i_codec == VLC_CODEC_TELETEXT
+ && (ev->fmt->subs.teletext.i_magazine == 1
+ || ev->fmt->subs.teletext.i_magazine > 8))
+ {
+ vlc_player_input_HandleTeletextMenu(input, ev);
+ return;
+ }
+
+ vlc_player_track_vector *vec =
+ vlc_player_input_GetTrackVector(input, ev->fmt->i_cat);
+ if (!vec)
+ return; /* UNKNOWN_ES or DATA_ES not handled */
+
+ vlc_player_t *player = input->player;
+ struct vlc_player_track_priv *trackpriv;
+ switch (ev->action)
+ {
+ case VLC_INPUT_ES_ADDED:
+ trackpriv = vlc_player_track_priv_New(ev->id, ev->title, ev->fmt);
+ if (!trackpriv)
+ break;
+
+ if (!vlc_vector_push(vec, trackpriv))
+ {
+ vlc_player_track_priv_Delete(trackpriv);
+ break;
+ }
+ vlc_player_SendEvent(player, on_track_list_changed,
+ VLC_PLAYER_LIST_ADDED, &trackpriv->t);
+ break;
+ case VLC_INPUT_ES_DELETED:
+ {
+ size_t idx;
+ trackpriv = vlc_player_track_vector_FindById(vec, ev->id, &idx);
+ if (trackpriv)
+ {
+ vlc_player_SendEvent(player, on_track_list_changed,
+ VLC_PLAYER_LIST_REMOVED, &trackpriv->t);
+ vlc_vector_remove(vec, idx);
+ vlc_player_track_priv_Delete(trackpriv);
+ }
+ break;
+ }
+ case VLC_INPUT_ES_UPDATED:
+ trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
+ if (!trackpriv)
+ break;
+ if (vlc_player_track_priv_Update(trackpriv, ev->title, ev->fmt) != 0)
+ break;
+ vlc_player_SendEvent(player, on_track_list_changed,
+ VLC_PLAYER_LIST_UPDATED, &trackpriv->t);
+ break;
+ case VLC_INPUT_ES_SELECTED:
+ trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
+ if (trackpriv)
+ {
+ trackpriv->t.selected = true;
+ vlc_player_SendEvent(player, on_track_selection_changed,
+ NULL, trackpriv->t.es_id);
+ }
+ break;
+ case VLC_INPUT_ES_UNSELECTED:
+ trackpriv = vlc_player_track_vector_FindById(vec, ev->id, NULL);
+ if (trackpriv)
+ {
+ trackpriv->t.selected = false;
+ vlc_player_SendEvent(player, on_track_selection_changed,
+ trackpriv->t.es_id, NULL);
+ }
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+static void
+vlc_player_input_HandleTitleEvent(struct vlc_player_input *input,
+ const struct vlc_input_event_title *ev)
+{
+ vlc_player_t *player = input->player;
+ switch (ev->action)
+ {
+ case VLC_INPUT_TITLE_NEW_LIST:
+ {
+ input_thread_private_t *input_th = input_priv(input->thread);
+ const int title_offset = input_th->i_title_offset;
+ const int chapter_offset = input_th->i_seekpoint_offset;
+
+ if (input->titles)
+ vlc_player_title_list_Release(input->titles);
+ input->title_selected = input->chapter_selected = 0;
+ input->titles =
+ vlc_player_title_list_Create(ev->list.array, ev->list.count,
+ title_offset, chapter_offset);
+ vlc_player_SendEvent(player, on_titles_changed, input->titles);
+ if (input->titles)
+ vlc_player_SendEvent(player, on_title_selection_changed,
+ &input->titles->array[0], 0);
+ break;
+ }
+ case VLC_INPUT_TITLE_SELECTED:
+ if (!input->titles)
+ return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
+ assert(ev->selected_idx < input->titles->count);
+ input->title_selected = ev->selected_idx;
+ vlc_player_SendEvent(player, on_title_selection_changed,
+ &input->titles->array[input->title_selected],
+ input->title_selected);
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+static void
+vlc_player_input_HandleChapterEvent(struct vlc_player_input *input,
+ const struct vlc_input_event_chapter *ev)
+{
+ vlc_player_t *player = input->player;
+ if (!input->titles || ev->title < 0 || ev->seekpoint < 0)
+ return; /* a previous VLC_INPUT_TITLE_NEW_LIST failed */
+
+ assert((size_t)ev->title < input->titles->count);
+ const struct vlc_player_title *title = &input->titles->array[ev->title];
+ if (!title->chapter_count)
+ return;
+
+ assert(ev->seekpoint < (int)title->chapter_count);
+ input->title_selected = ev->title;
+ input->chapter_selected = ev->seekpoint;
+
+ const struct vlc_player_chapter *chapter = &title->chapters[ev->seekpoint];
+ vlc_player_SendEvent(player, on_chapter_selection_changed, title, ev->title,
+ chapter, ev->seekpoint);
+}
+
+static void
+vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
+ const struct vlc_input_event_vout *ev)
+{
+ assert(ev->vout);
+ assert(ev->id);
+
+ vlc_player_t *player = input->player;
+
+ struct vlc_player_track_priv *trackpriv =
+ vlc_player_input_FindTrackById(input, ev->id, NULL);
+ if (!trackpriv)
+ return;
+
+ const bool is_video_es = trackpriv->t.fmt.i_cat == VIDEO_ES;
+
+ switch (ev->action)
+ {
+ case VLC_INPUT_EVENT_VOUT_ADDED:
+ trackpriv->vout = ev->vout;
+ vlc_player_SendEvent(player, on_vout_changed,
+ VLC_PLAYER_VOUT_STARTED, ev->vout,
+ ev->order, ev->id);
+
+ if (is_video_es)
+ {
+ /* Register vout callbacks after the vout list event */
+ vlc_player_vout_AddCallbacks(player, ev->vout);
+ }
+ break;
+ case VLC_INPUT_EVENT_VOUT_DELETED:
+ if (is_video_es)
+ {
+ /* Un-register vout callbacks before the vout list event */
+ vlc_player_vout_DelCallbacks(player, ev->vout);
+ }
+
+ trackpriv->vout = NULL;
+ vlc_player_SendEvent(player, on_vout_changed,
+ VLC_PLAYER_VOUT_STOPPED, ev->vout,
+ VLC_VOUT_ORDER_NONE, ev->id);
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+static void
+input_thread_Events(input_thread_t *input_thread,
+ const struct vlc_input_event *event, void *user_data)
+{
+ struct vlc_player_input *input = user_data;
+ vlc_player_t *player = input->player;
+
+ assert(input_thread == input->thread);
+
+ vlc_mutex_lock(&player->lock);
+
+ switch (event->type)
+ {
+ case INPUT_EVENT_STATE:
+ vlc_player_input_HandleStateEvent(input, event->state.value,
+ event->state.date);
+ break;
+ case INPUT_EVENT_RATE:
+ input->rate = event->rate;
+ vlc_player_SendEvent(player, on_rate_changed, input->rate);
+ break;
+ case INPUT_EVENT_CAPABILITIES:
+ {
+ int old_caps = input->capabilities;
+ input->capabilities = event->capabilities;
+ vlc_player_SendEvent(player, on_capabilities_changed,
+ old_caps, input->capabilities);
+ break;
+ }
+ case INPUT_EVENT_TIMES:
+ if (event->times.ms != VLC_TICK_INVALID
+ && (input->time != event->times.ms
+ || input->position != event->times.percentage))
+ {
+ input->time = event->times.ms;
+ input->position = event->times.percentage;
+ vlc_player_SendEvent(player, on_position_changed,
+ input->time, input->position);
+
+ vlc_player_input_UpdateTime(input);
+ }
+ if (input->length != event->times.length)
+ {
+ input->length = event->times.length;
+ vlc_player_SendEvent(player, on_length_changed, input->length);
+ }
+ break;
+ case INPUT_EVENT_PROGRAM:
+ vlc_player_input_HandleProgramEvent(input, &event->program);
+ break;
+ case INPUT_EVENT_ES:
+ vlc_player_input_HandleEsEvent(input, &event->es);
+ break;
+ case INPUT_EVENT_TITLE:
+ vlc_player_input_HandleTitleEvent(input, &event->title);
+ break;
+ case INPUT_EVENT_CHAPTER:
+ vlc_player_input_HandleChapterEvent(input, &event->chapter);
+ break;
+ case INPUT_EVENT_RECORD:
+ input->recording = event->record;
+ vlc_player_SendEvent(player, on_recording_changed, input->recording);
+ break;
+ case INPUT_EVENT_STATISTICS:
+ input->stats = *event->stats;
+ vlc_player_SendEvent(player, on_statistics_changed, &input->stats);
+ break;
+ case INPUT_EVENT_SIGNAL:
+ input->signal_quality = event->signal.quality;
+ input->signal_strength = event->signal.strength;
+ vlc_player_SendEvent(player, on_signal_changed,
+ input->signal_quality, input->signal_strength);
+ break;
+ case INPUT_EVENT_CACHE:
+ input->cache = event->cache;
+ vlc_player_SendEvent(player, on_buffering_changed, event->cache);
+ break;
+ case INPUT_EVENT_VOUT:
+ vlc_player_input_HandleVoutEvent(input, &event->vout);
+ break;
+ case INPUT_EVENT_ITEM_META:
+ vlc_player_SendEvent(player, on_media_meta_changed,
+ input_GetItem(input->thread));
+ break;
+ case INPUT_EVENT_ITEM_EPG:
+ vlc_player_SendEvent(player, on_media_epg_changed,
+ input_GetItem(input->thread));
+ break;
+ case INPUT_EVENT_SUBITEMS:
+ vlc_player_SendEvent(player, on_media_subitems_changed,
+ input_GetItem(input->thread), event->subitems);
+ break;
+ case INPUT_EVENT_DEAD:
+ if (input->started) /* Can happen with early input_thread fails */
+ vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING,
+ VLC_TICK_INVALID);
+ vlc_player_destructor_AddJoinableInput(player, input);
+ break;
+ case INPUT_EVENT_VBI_PAGE:
+ input->teletext_page = event->vbi_page < 999 ? event->vbi_page : 100;
+ vlc_player_SendEvent(player, on_teletext_page_changed,
+ input->teletext_page);
+ break;
+ case INPUT_EVENT_VBI_TRANSPARENCY:
+ input->teletext_transparent = event->vbi_transparent;
+ vlc_player_SendEvent(player, on_teletext_transparency_changed,
+ input->teletext_transparent);
+ break;
+ default:
+ break;
+ }
+
+ vlc_mutex_unlock(&player->lock);
+}
+
+struct vlc_player_input *
+vlc_player_input_New(vlc_player_t *player, input_item_t *item)
+{
+ struct vlc_player_input *input = malloc(sizeof(*input));
+ if (!input)
+ return NULL;
+
+ input->player = player;
+ input->started = false;
+
+ input->state = VLC_PLAYER_STATE_STOPPED;
+ input->error = VLC_PLAYER_ERROR_NONE;
+ input->rate = 1.f;
+ input->capabilities = 0;
+ input->length = input->time = VLC_TICK_INVALID;
+ input->position = 0.f;
+
+ input->recording = false;
+
+ input->cache = 0.f;
+ input->signal_quality = input->signal_strength = -1.f;
+
+ memset(&input->stats, 0, sizeof(input->stats));
+
+ vlc_vector_init(&input->program_vector);
+ vlc_vector_init(&input->video_track_vector);
+ vlc_vector_init(&input->audio_track_vector);
+ vlc_vector_init(&input->spu_track_vector);
+ input->teletext_menu = NULL;
+
+ input->titles = NULL;
+ input->title_selected = input->chapter_selected = 0;
+
+ input->teletext_enabled = input->teletext_transparent = false;
+ input->teletext_page = 0;
+
+ input->abloop_state[0].set = input->abloop_state[1].set = false;
+
+ input->thread = input_Create(player, input_thread_Events, input, item,
+ player->resource, player->renderer);
+ if (!input->thread)
+ {
+ free(input);
+ return NULL;
+ }
+
+ /* Initial sub/audio delay */
+ const vlc_tick_t cat_delays[DATA_ES] = {
+ [AUDIO_ES] =
+ VLC_TICK_FROM_MS(var_InheritInteger(player, "audio-desync")),
+ [SPU_ES] =
+ vlc_tick_from_samples(var_InheritInteger(player, "sub-delay"), 10),
+ };
+
+ for (enum es_format_category_e i = UNKNOWN_ES; i < DATA_ES; ++i)
+ {
+ input->cat_delays[i] = cat_delays[i];
+ if (cat_delays[i] != 0)
+ {
+ const input_control_param_t param = {
+ .cat_delay = { i, cat_delays[i] }
+ };
+ input_ControlPush(input->thread, INPUT_CONTROL_SET_CATEGORY_DELAY,
+ ¶m);
+ vlc_player_SendEvent(player, on_category_delay_changed, i,
+ cat_delays[i]);
+ }
+ }
+ return input;
+}
+
+void
+vlc_player_input_Delete(struct vlc_player_input *input)
+{
+ assert(input->titles == NULL);
+ assert(input->program_vector.size == 0);
+ assert(input->video_track_vector.size == 0);
+ assert(input->audio_track_vector.size == 0);
+ assert(input->spu_track_vector.size == 0);
+ assert(input->teletext_menu == NULL);
+
+ vlc_vector_destroy(&input->program_vector);
+ vlc_vector_destroy(&input->video_track_vector);
+ vlc_vector_destroy(&input->audio_track_vector);
+ vlc_vector_destroy(&input->spu_track_vector);
+
+ input_Close(input->thread);
+ free(input);
+}
+
diff --git a/src/input/player_osd.c b/src/input/player_osd.c
new file mode 100644
index 0000000000..c8bc9bb983
--- /dev/null
+++ b/src/input/player_osd.c
@@ -0,0 +1,308 @@
+/*****************************************************************************
+ * player.c: Player interface
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include "player.h"
+#include "resource.h"
+
+static vout_thread_t **
+vlc_player_osd_HoldAll(vlc_player_t *player, size_t *count)
+{
+ vout_thread_t **vouts;
+ input_resource_HoldVouts(player->resource, &vouts, count);
+
+ for (size_t i = 0; i < *count; ++i)
+ {
+ vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD);
+ vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER);
+ vout_FlushSubpictureChannel(vouts[i], VOUT_SPU_CHANNEL_OSD_HSLIDER);
+ }
+ return vouts;
+}
+
+static void
+vlc_player_osd_ReleaseAll(vlc_player_t *player, vout_thread_t **vouts,
+ size_t count)
+{
+ for (size_t i = 0; i < count; ++i)
+ vout_Release(vouts[i]);
+ free(vouts);
+ (void) player;
+}
+
+static inline void
+vouts_osd_Message(vout_thread_t **vouts, size_t count, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ for (size_t i = 0; i < count; ++i)
+ {
+ va_list acpy;
+ va_copy(acpy, args);
+ vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, acpy);
+ va_end(acpy);
+ }
+ va_end(args);
+}
+
+static inline void
+vouts_osd_Icon(vout_thread_t **vouts, size_t count, short type)
+{
+ for (size_t i = 0; i < count; ++i)
+ vout_OSDIcon(vouts[i], VOUT_SPU_CHANNEL_OSD, type);
+}
+
+static inline void
+vouts_osd_Slider(vout_thread_t **vouts, size_t count, int position, short type)
+{
+ int channel = type == OSD_HOR_SLIDER ?
+ VOUT_SPU_CHANNEL_OSD_HSLIDER : VOUT_SPU_CHANNEL_OSD_VSLIDER;
+ for (size_t i = 0; i < count; ++i)
+ vout_OSDSlider(vouts[i], channel, position, type);
+}
+
+void
+vlc_player_osd_Message(vlc_player_t *player, const char *fmt, ...)
+{
+ size_t count;
+ vout_thread_t **vouts = vlc_player_osd_HoldAll(player, &count);
+
+ va_list args;
+ va_start(args, fmt);
+ for (size_t i = 0; i < count; ++i)
+ {
+ va_list acpy;
+ va_copy(acpy, args);
+ vout_OSDMessageVa(vouts[i], VOUT_SPU_CHANNEL_OSD, fmt, acpy);
+ va_end(acpy);
+ }
+ va_end(args);
+
+ vlc_player_osd_ReleaseAll(player, vouts, count);
+}
+
+void
+vlc_player_osd_Icon(vlc_player_t *player, short type)
+{
+ size_t count;
+ vout_thread_t **vouts = vlc_player_osd_HoldAll(player, &count);
+
+ vouts_osd_Icon(vouts, count, type);
+
+ vlc_player_osd_ReleaseAll(player, vouts, count);
+}
+
+void
+vlc_player_osd_Position(vlc_player_t *player,
+ struct vlc_player_input *input, vlc_tick_t time,
+ float position, enum vlc_player_whence whence)
+{
+ if (input->length != VLC_TICK_INVALID)
+ {
+ if (time == VLC_TICK_INVALID)
+ time = position * input->length;
+ else
+ position = time / (float) input->length;
+ }
+
+ size_t count;
+ vout_thread_t **vouts = vlc_player_osd_HoldAll(player, &count);
+
+ if (time != VLC_TICK_INVALID)
+ {
+ if (whence == VLC_PLAYER_WHENCE_RELATIVE)
+ {
+ time += vlc_player_input_GetTime(input); /* XXX: TOCTOU */
+ if (time < 0)
+ time = 0;
+ }
+
+ char time_text[MSTRTIME_MAX_SIZE];
+ secstotimestr(time_text, SEC_FROM_VLC_TICK(time));
+ if (input->length != VLC_TICK_INVALID)
+ {
+ char len_text[MSTRTIME_MAX_SIZE];
+ secstotimestr(len_text, SEC_FROM_VLC_TICK(input->length));
+ vouts_osd_Message(vouts, count, "%s / %s", time_text, len_text);
+ }
+ else
+ vouts_osd_Message(vouts, count, "%s", time_text);
+ }
+
+ if (vlc_player_vout_IsFullscreen(player))
+ {
+ if (whence == VLC_PLAYER_WHENCE_RELATIVE)
+ {
+ position += vlc_player_input_GetPos(input); /* XXX: TOCTOU */
+ if (position < 0.f)
+ position = 0.f;
+ }
+ vouts_osd_Slider(vouts, count, position * 100, OSD_HOR_SLIDER);
+ }
+ vlc_player_osd_ReleaseAll(player, vouts, count);
+}
+
+void
+vlc_player_osd_Volume(vlc_player_t *player, bool mute_action)
+{
+ size_t count;
+ vout_thread_t **vouts = vlc_player_osd_HoldAll(player, &count);
+
+ bool mute = vlc_player_aout_IsMuted(player);
+ int volume = lroundf(vlc_player_aout_GetVolume(player) * 100.f);
+ if (mute_action && mute)
+ vouts_osd_Icon(vouts, count, OSD_MUTE_ICON);
+ else
+ {
+ if (vlc_player_vout_IsFullscreen(player))
+ vouts_osd_Slider(vouts, count, volume, OSD_VERT_SLIDER);
+ vouts_osd_Message(vouts, count, _("Volume: %ld%%"), volume);
+ }
+
+ vlc_player_osd_ReleaseAll(player, vouts, count);
+}
+
+void
+vlc_player_osd_Track(vlc_player_t *player, vlc_es_id_t *id, bool select)
+{
+ enum es_format_category_e cat = vlc_es_id_GetCat(id);
+ const struct vlc_player_track *track = vlc_player_GetTrack(player, id);
+ if (!track && select)
+ return;
+
+ const char *cat_name = es_format_category_to_string(cat);
+ assert(cat_name);
+ const char *track_name = select ? track->name : _("N/A");
+ vlc_player_osd_Message(player, _("%s track: %s"), cat_name, track_name);
+}
+
+void
+vlc_player_osd_Program(vlc_player_t *player, const char *name)
+{
+ vlc_player_osd_Message(player, _("Program Service ID: %s"), name);
+}
+
+static bool
+vout_osd_PrintVariableText(vout_thread_t *vout, const char *varname, int vartype,
+ vlc_value_t varval, const char *osdfmt)
+{
+ bool found = false;
+ bool isvarstring = vartype == VLC_VAR_STRING;
+ size_t num_choices;
+ vlc_value_t *choices;
+ char **choices_text;
+ var_Change(vout, varname, VLC_VAR_GETCHOICES,
+ &num_choices, &choices, &choices_text);
+ for (size_t i = 0; i < num_choices; ++i)
+ {
+ if (!found)
+ if ((isvarstring &&
+ strcmp(choices[i].psz_string, varval.psz_string) == 0) ||
+ (!isvarstring && choices[i].f_float == varval.f_float))
+ {
+ vouts_osd_Message(&vout, 1, osdfmt, choices_text[i]);
+ found = true;
+ }
+ if (isvarstring)
+ free(choices[i].psz_string);
+ free(choices_text[i]);
+ }
+ free(choices);
+ free(choices_text);
+ return found;
+}
+
+int
+vlc_player_vout_OSDCallback(vlc_object_t *this, const char *var,
+ vlc_value_t oldval, vlc_value_t newval, void *data)
+{
+ VLC_UNUSED(oldval);
+
+ vout_thread_t *vout = (vout_thread_t *)this;
+
+ if (strcmp(var, "aspect-ratio") == 0)
+ vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING,
+ newval, _("Aspect ratio: %s"));
+
+ else if (strcmp(var, "autoscale") == 0)
+ vouts_osd_Message(&vout, 1, newval.b_bool ?
+ _("Scaled to screen") : _("Original size"));
+
+ else if (strcmp(var, "crop") == 0)
+ vout_osd_PrintVariableText(vout, var, VLC_VAR_STRING, newval,
+ _("Crop: %s"));
+
+ else if (strcmp(var, "crop-bottom") == 0)
+ vouts_osd_Message(&vout, 1, _("Bottom crop: %d px"), newval.i_int);
+
+ else if (strcmp(var, "crop-top") == 0)
+ vouts_osd_Message(&vout, 1, _("Top crop: %d px"), newval.i_int);
+
+ else if (strcmp(var, "crop-left") == 0)
+ vouts_osd_Message(&vout, 1, _("Left crop: %d px"), newval.i_int);
+
+ else if (strcmp(var, "crop-right") == 0)
+ vouts_osd_Message(&vout, 1, _("Right crop: %d px"), newval.i_int);
+
+ else if (strcmp(var, "deinterlace") == 0 ||
+ strcmp(var, "deinterlace-mode") == 0)
+ {
+ bool varmode = strcmp(var, "deinterlace-mode") == 0;
+ int on = !varmode ?
+ newval.i_int : var_GetInteger(vout, "deinterlace");
+ char *mode = varmode ?
+ newval.psz_string : var_GetString(vout, "deinterlace-mode");
+ vouts_osd_Message(&vout, 1, _("Deinterlace %s (%s)"),
+ on == 1 ? _("On") : _("Off"), mode);
+ if (!varmode)
+ free(mode);
+ }
+
+ else if (strcmp(var, "sub-margin") == 0)
+ vouts_osd_Message(&vout, 1, _("Subtitle position %d px"), newval.i_int);
+
+ else if (strcmp(var, "secondary-sub-margin") == 0)
+ vouts_osd_Message(&vout, 1, _("Secondary subtitle position %d px"), newval.i_int);
+
+ else if (strcmp(var, "sub-text-scale") == 0)
+ vouts_osd_Message(&vout, 1, _("Subtitle text scale %d%%"), newval.i_int);
+
+ else if (strcmp(var, "zoom") == 0)
+ {
+ if (newval.f_float == 1.f)
+ vouts_osd_Message(&vout, 1, _("Zooming reset"));
+ else
+ {
+ bool found = vout_osd_PrintVariableText(vout, var, VLC_VAR_FLOAT,
+ newval, _("Zoom mode: %s"));
+ if (!found)
+ vouts_osd_Message(&vout, 1, _("Zoom: x%f"), newval.f_float);
+ }
+ }
+
+ (void) data;
+ return VLC_SUCCESS;
+}
diff --git a/src/input/player_title.c b/src/input/player_title.c
new file mode 100644
index 0000000000..c961b03dbd
--- /dev/null
+++ b/src/input/player_title.c
@@ -0,0 +1,174 @@
+/*****************************************************************************
+ * player.c: Player interface
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include "player.h"
+
+struct vlc_player_title_list *
+vlc_player_title_list_Hold(struct vlc_player_title_list *titles)
+{
+ vlc_atomic_rc_inc(&titles->rc);
+ return titles;
+}
+
+void
+vlc_player_title_list_Release(struct vlc_player_title_list *titles)
+{
+ if (!vlc_atomic_rc_dec(&titles->rc))
+ return;
+ for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
+ {
+ struct vlc_player_title *title = &titles->array[title_idx];
+ free((char *)title->name);
+ for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
+ ++chapter_idx)
+ {
+ const struct vlc_player_chapter *chapter =
+ &title->chapters[chapter_idx];
+ free((char *)chapter->name);
+ }
+ free((void *)title->chapters);
+ }
+ free(titles);
+}
+
+static char *
+input_title_GetName(const struct input_title_t *input_title, int idx,
+ int title_offset)
+{
+ int ret;
+ char length_str[MSTRTIME_MAX_SIZE + sizeof(" []")];
+
+ if (input_title->i_length > 0)
+ {
+ strcpy(length_str, " [");
+ secstotimestr(&length_str[2], SEC_FROM_VLC_TICK(input_title->i_length));
+ strcat(length_str, "]");
+ }
+ else
+ length_str[0] = '\0';
+
+ char *dup;
+ if (input_title->psz_name && input_title->psz_name[0] != '\0')
+ ret = asprintf(&dup, "%s%s", input_title->psz_name, length_str);
+ else
+ ret = asprintf(&dup, _("Title %i%s"), idx + title_offset, length_str);
+ if (ret == -1)
+ return NULL;
+ return dup;
+}
+
+static char *
+seekpoint_GetName(seekpoint_t *seekpoint, int idx, int chapter_offset)
+{
+ if (seekpoint->psz_name && seekpoint->psz_name[0] != '\0' )
+ return strdup(seekpoint->psz_name);
+
+ char *dup;
+ int ret = asprintf(&dup, _("Chapter %i"), idx + chapter_offset);
+ if (ret == -1)
+ return NULL;
+ return dup;
+}
+
+struct vlc_player_title_list *
+vlc_player_title_list_Create(input_title_t *const *array, size_t count,
+ int title_offset, int chapter_offset)
+{
+ if (count == 0)
+ return NULL;
+
+ /* Allocate the struct + the whole list */
+ size_t size;
+ if (mul_overflow(count, sizeof(struct vlc_player_title), &size))
+ return NULL;
+ if (add_overflow(size, sizeof(struct vlc_player_title_list), &size))
+ return NULL;
+ struct vlc_player_title_list *titles = malloc(size);
+ if (!titles)
+ return NULL;
+
+ vlc_atomic_rc_init(&titles->rc);
+ titles->count = count;
+
+ for (size_t title_idx = 0; title_idx < titles->count; ++title_idx)
+ {
+ const struct input_title_t *input_title = array[title_idx];
+ struct vlc_player_title *title = &titles->array[title_idx];
+
+ title->name = input_title_GetName(input_title, title_idx, title_offset);
+ title->length = input_title->i_length;
+ title->flags = input_title->i_flags;
+ const size_t seekpoint_count = input_title->i_seekpoint > 0 ?
+ input_title->i_seekpoint : 0;
+ title->chapter_count = seekpoint_count;
+
+ struct vlc_player_chapter *chapters = title->chapter_count == 0 ? NULL :
+ vlc_alloc(title->chapter_count, sizeof(*chapters));
+
+ if (chapters)
+ {
+ for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
+ ++chapter_idx)
+ {
+ struct vlc_player_chapter *chapter = &chapters[chapter_idx];
+ seekpoint_t *seekpoint = input_title->seekpoint[chapter_idx];
+
+ chapter->name = seekpoint_GetName(seekpoint, chapter_idx,
+ chapter_offset);
+ chapter->time = seekpoint->i_time_offset;
+ if (!chapter->name) /* Will trigger the error path */
+ title->chapter_count = chapter_idx;
+ }
+ }
+ else if (seekpoint_count > 0) /* Will trigger the error path */
+ title->chapter_count = 0;
+
+ title->chapters = chapters;
+
+ if (!title->name || seekpoint_count != title->chapter_count)
+ {
+ /* Release titles up to title_idx */
+ titles->count = title_idx;
+ vlc_player_title_list_Release(titles);
+ return NULL;
+ }
+ }
+ return titles;
+}
+
+const struct vlc_player_title *
+vlc_player_title_list_GetAt(struct vlc_player_title_list *titles, size_t idx)
+{
+ assert(idx < titles->count);
+ return &titles->array[idx];
+}
+
+size_t
+vlc_player_title_list_GetCount(struct vlc_player_title_list *titles)
+{
+ return titles->count;
+}
diff --git a/src/input/player_track.c b/src/input/player_track.c
new file mode 100644
index 0000000000..d8d9184d1c
--- /dev/null
+++ b/src/input/player_track.c
@@ -0,0 +1,207 @@
+/*****************************************************************************
+ * player.c: Player interface
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include "player.h"
+
+static char *
+vlc_player_program_DupTitle(int id, const char *title)
+{
+ char *dup;
+ if (title)
+ dup = strdup(title);
+ else if (asprintf(&dup, "%d", id) == -1)
+ dup = NULL;
+ return dup;
+}
+
+struct vlc_player_program *
+vlc_player_program_New(int id, const char *name)
+{
+ struct vlc_player_program *prgm = malloc(sizeof(*prgm));
+ if (!prgm)
+ return NULL;
+ prgm->name = vlc_player_program_DupTitle(id, name);
+ if (!prgm->name)
+ {
+ free(prgm);
+ return NULL;
+ }
+ prgm->group_id = id;
+ prgm->selected = prgm->scrambled = false;
+
+ return prgm;
+}
+
+int
+vlc_player_program_Update(struct vlc_player_program *prgm, int id,
+ const char *name)
+{
+ free((char *)prgm->name);
+ prgm->name = vlc_player_program_DupTitle(id, name);
+ return prgm->name != NULL ? VLC_SUCCESS : VLC_ENOMEM;
+}
+
+struct vlc_player_program *
+vlc_player_program_Dup(const struct vlc_player_program *src)
+{
+ struct vlc_player_program *dup =
+ vlc_player_program_New(src->group_id, src->name);
+
+ if (!dup)
+ return NULL;
+ dup->selected = src->selected;
+ dup->scrambled = src->scrambled;
+ return dup;
+}
+
+void
+vlc_player_program_Delete(struct vlc_player_program *prgm)
+{
+ free((char *)prgm->name);
+ free(prgm);
+}
+
+struct vlc_player_program *
+vlc_player_program_vector_FindById(vlc_player_program_vector *vec, int id,
+ size_t *idx)
+{
+ for (size_t i = 0; i < vec->size; ++i)
+ {
+ struct vlc_player_program *prgm = vec->data[i];
+ if (prgm->group_id == id)
+ {
+ if (idx)
+ *idx = i;
+ return prgm;
+ }
+ }
+ return NULL;
+}
+
+struct vlc_player_track_priv *
+vlc_player_track_priv_New(vlc_es_id_t *id, const char *name, const es_format_t *fmt)
+{
+ struct vlc_player_track_priv *trackpriv = malloc(sizeof(*trackpriv));
+ if (!trackpriv)
+ return NULL;
+ struct vlc_player_track *track = &trackpriv->t;
+
+ trackpriv->delay = INT64_MAX;
+ trackpriv->vout = NULL;
+ trackpriv->vout_order = VLC_VOUT_ORDER_NONE;
+
+ track->name = strdup(name);
+ if (!track->name)
+ {
+ free(track);
+ return NULL;
+ }
+
+ int ret = es_format_Copy(&track->fmt, fmt);
+ if (ret != VLC_SUCCESS)
+ {
+ free((char *)track->name);
+ free(track);
+ return NULL;
+ }
+ track->es_id = vlc_es_id_Hold(id);
+ track->selected = false;
+
+ return trackpriv;
+}
+
+void
+vlc_player_track_priv_Delete(struct vlc_player_track_priv *trackpriv)
+{
+ struct vlc_player_track *track = &trackpriv->t;
+ es_format_Clean(&track->fmt);
+ free((char *)track->name);
+ vlc_es_id_Release(track->es_id);
+ free(trackpriv);
+}
+
+void
+vlc_player_track_Delete(struct vlc_player_track *track)
+{
+ struct vlc_player_track_priv *trackpriv =
+ container_of(track, struct vlc_player_track_priv, t);
+ vlc_player_track_priv_Delete(trackpriv);
+}
+
+struct vlc_player_track *
+vlc_player_track_Dup(const struct vlc_player_track *src)
+{
+ struct vlc_player_track_priv *duppriv =
+ vlc_player_track_priv_New(src->es_id, src->name, &src->fmt);
+
+ if (!duppriv)
+ return NULL;
+ duppriv->t.selected = src->selected;
+ return &duppriv->t;
+}
+
+int
+vlc_player_track_priv_Update(struct vlc_player_track_priv *trackpriv,
+ const char *name, const es_format_t *fmt)
+{
+ struct vlc_player_track *track = &trackpriv->t;
+
+ if (strcmp(name, track->name) != 0)
+ {
+ char *dup = strdup(name);
+ if (!dup)
+ return VLC_ENOMEM;
+ free((char *)track->name);
+ track->name = dup;
+ }
+
+ es_format_t fmtdup;
+ int ret = es_format_Copy(&fmtdup, fmt);
+ if (ret != VLC_SUCCESS)
+ return ret;
+
+ es_format_Clean(&track->fmt);
+ track->fmt = fmtdup;
+ return VLC_SUCCESS;
+}
+
+struct vlc_player_track_priv *
+vlc_player_track_vector_FindById(vlc_player_track_vector *vec, vlc_es_id_t *id,
+ size_t *idx)
+{
+ for (size_t i = 0; i < vec->size; ++i)
+ {
+ struct vlc_player_track_priv *trackpriv = vec->data[i];
+ if (trackpriv->t.es_id == id)
+ {
+ if (idx)
+ *idx = i;
+ return trackpriv;
+ }
+ }
+ return NULL;
+}
diff --git a/src/input/player_vout.c b/src/input/player_vout.c
new file mode 100644
index 0000000000..d7af571d70
--- /dev/null
+++ b/src/input/player_vout.c
@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * player.c: Player interface
+ *****************************************************************************
+ * Copyright © 2018-2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+
+#include <vlc_common.h>
+#include "player.h"
+#include "resource.h"
+
+#define vlc_player_vout_SendEvent(player, event, ...) do { \
+ vlc_mutex_lock(&player->vout_listeners_lock); \
+ vlc_player_vout_listener_id *listener; \
+ vlc_list_foreach(listener, &player->vout_listeners, node) \
+ { \
+ if (listener->cbs->event) \
+ listener->cbs->event(player, ##__VA_ARGS__, listener->cbs_data); \
+ } \
+ vlc_mutex_unlock(&player->vout_listeners_lock); \
+} while(0)
+
+vout_thread_t *
+vlc_player_vout_Hold(vlc_player_t *player)
+{
+ vout_thread_t *vout = input_resource_HoldVout(player->resource);
+ return vout ? vout : input_resource_HoldDummyVout(player->resource);
+}
+
+vout_thread_t **
+vlc_player_vout_HoldAll(vlc_player_t *player, size_t *count)
+{
+ vout_thread_t **vouts;
+ input_resource_HoldVouts(player->resource, &vouts, count);
+
+ if (*count == 0)
+ {
+ vouts = vlc_alloc(1, sizeof(*vouts));
+ if (vouts)
+ {
+ *count = 1;
+ vouts[0] = input_resource_HoldDummyVout(player->resource);
+ }
+ }
+ return vouts;
+}
+
+vlc_player_vout_listener_id *
+vlc_player_vout_AddListener(vlc_player_t *player,
+ const struct vlc_player_vout_cbs *cbs,
+ void *cbs_data)
+{
+ assert(cbs);
+
+ vlc_player_vout_listener_id *listener = malloc(sizeof(*listener));
+ if (!listener)
+ return NULL;
+
+ listener->cbs = cbs;
+ listener->cbs_data = cbs_data;
+
+ vlc_mutex_lock(&player->vout_listeners_lock);
+ vlc_list_append(&listener->node, &player->vout_listeners);
+ vlc_mutex_unlock(&player->vout_listeners_lock);
+
+ return listener;
+}
+
+void
+vlc_player_vout_RemoveListener(vlc_player_t *player,
+ vlc_player_vout_listener_id *id)
+{
+ assert(id);
+
+ vlc_mutex_lock(&player->vout_listeners_lock);
+ vlc_list_remove(&id->node);
+ vlc_mutex_unlock(&player->vout_listeners_lock);
+ free(id);
+}
+
+bool
+vlc_player_vout_IsFullscreen(vlc_player_t *player)
+{
+ return var_GetBool(player, "fullscreen");
+}
+
+static int
+vlc_player_VoutCallback(vlc_object_t *this, const char *var,
+ vlc_value_t oldval, vlc_value_t newval, void *data)
+{
+ vlc_player_t *player = data;
+
+ if (strcmp(var, "fullscreen") == 0)
+ {
+ if (oldval.b_bool != newval.b_bool )
+ vlc_player_vout_SendEvent(player, on_fullscreen_changed,
+ (vout_thread_t *)this, newval.b_bool);
+ }
+ else if (strcmp(var, "video-wallpaper") == 0)
+ {
+ if (oldval.b_bool != newval.b_bool )
+ vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed,
+ (vout_thread_t *)this, newval.b_bool);
+ }
+ else
+ vlc_assert_unreachable();
+
+ return VLC_SUCCESS;
+}
+
+static const char osd_vars[][sizeof("secondary-sub-margin")] = {
+ "aspect-ratio", "autoscale", "crop", "crop-bottom",
+ "crop-top", "crop-left", "crop-right", "deinterlace",
+ "deinterlace-mode", "sub-margin", "secondary-sub-margin", "zoom"
+};
+
+void
+vlc_player_vout_AddCallbacks(vlc_player_t *player, vout_thread_t *vout)
+{
+ var_AddCallback(vout, "fullscreen", vlc_player_VoutCallback, player);
+ var_AddCallback(vout, "video-wallpaper", vlc_player_VoutCallback, player);
+
+ for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i)
+ var_AddCallback(vout, osd_vars[i], vlc_player_vout_OSDCallback, player);
+}
+
+void
+vlc_player_vout_DelCallbacks(vlc_player_t *player, vout_thread_t *vout)
+{
+ var_DelCallback(vout, "fullscreen", vlc_player_VoutCallback, player);
+ var_DelCallback(vout, "video-wallpaper", vlc_player_VoutCallback, player);
+
+ for (size_t i = 0; i < ARRAY_SIZE(osd_vars); ++i)
+ var_DelCallback(vout, osd_vars[i], vlc_player_vout_OSDCallback, player);
+}
+
+static void
+vlc_player_vout_SetVar(vlc_player_t *player, const char *name, int type,
+ vlc_value_t val)
+{
+ vout_thread_t *vout = vlc_player_vout_Hold(player);
+ var_SetChecked(vout, name, type, val);
+ vout_Release(vout);
+}
+
+
+static void
+vlc_player_vout_TriggerOption(vlc_player_t *player, const char *option)
+{
+ /* Don't use vlc_player_vout_Hold() since there is nothing to trigger if it
+ * returns a dummy vout */
+ vout_thread_t *vout = input_resource_HoldVout(player->resource);
+ var_TriggerCallback(vout, option);
+ vout_Release(vout);
+}
+
+
+void
+vlc_player_vout_SetFullscreen(vlc_player_t *player, bool enabled)
+{
+ vlc_player_vout_SetVar(player, "fullscreen", VLC_VAR_BOOL,
+ (vlc_value_t) { .b_bool = enabled });
+ vlc_player_vout_SendEvent(player, on_fullscreen_changed, NULL, enabled);
+}
+
+bool
+vlc_player_vout_IsWallpaperModeEnabled(vlc_player_t *player)
+{
+ return var_GetBool(player, "video-wallpaper");
+}
+
+void
+vlc_player_vout_SetWallpaperModeEnabled(vlc_player_t *player, bool enabled)
+{
+ vlc_player_vout_SetVar(player, "video-wallpaper", VLC_VAR_BOOL,
+ (vlc_value_t) { .b_bool = enabled });
+ vlc_player_vout_SendEvent(player, on_wallpaper_mode_changed, NULL, enabled);
+}
+
+void
+vlc_player_vout_Snapshot(vlc_player_t *player)
+{
+ vlc_player_vout_TriggerOption(player, "video-snapshot");
+}
--
2.20.1
More information about the vlc-devel
mailing list