Commit 2f18f559 authored by Thomas Guillem's avatar Thomas Guillem

player: split the implementation into several files

player.c was starting getting huge (~4000 lines), and therefore harder to
navigate into.
parent 9d25fc21
......@@ -77,7 +77,6 @@ src/input/input.c
src/input/input_internal.h
src/input/item.c
src/input/meta.c
src/input/player.c
src/input/stream.c
src/input/stream.h
src/input/stream_memory.c
......@@ -120,6 +119,8 @@ src/network/rootbind.c
src/network/tcp.c
src/network/tls.c
src/network/udp.c
src/player/osd.c
src/player/player.c
src/stream_output/sap.c
src/stream_output/sdp.c
src/stream_output/stream_output.c
......
......@@ -260,10 +260,16 @@ libvlccore_la_SOURCES = \
input/es_out.c \
input/es_out_timeshift.c \
input/input.c \
input/player.c \
input/player.h \
input/info.h \
input/meta.c \
player/player.c \
player/player.h \
player/input.c \
player/track.c \
player/title.c \
player/aout.c \
player/vout.c \
player/osd.c \
clock/input_clock.h \
clock/clock.h \
clock/clock_internal.h \
......
/*****************************************************************************
* player.h: Player internal interface
*****************************************************************************
* Copyright © 2018 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_PLAYER_INTERNAL_H
#define VLC_PLAYER_INTERNAL_H
#include <vlc_player.h>
/**
* Assert that the player mutex is locked.
*
* This is exposed in this internal header because the playlist and its
* associated player share the lock to avoid lock-order inversion issues.
*/
void
vlc_player_assert_locked(vlc_player_t *player);
vlc_object_t *
vlc_player_GetObject(vlc_player_t *player);
#endif
......@@ -44,7 +44,7 @@
#include <vlc_playlist.h>
#include "libvlc.h"
#include "../lib/libvlc_internal.h"
#include "input/player.h"
#include "player/player.h"
static int AddIntfCallback( vlc_object_t *, char const *,
vlc_value_t , vlc_value_t , void * );
......
/*****************************************************************************
* player_aout.c: Player aout implementation
*****************************************************************************
* 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 "input/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);
}
/*****************************************************************************
* player_input.c: Player input implementation
*****************************************************************************
* 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_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)
{
switch (state)
{
case OPENING_S:
vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STARTED);
break;
case PLAYING_S:
vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PLAYING);
break;
case PAUSE_S:
vlc_player_input_HandleState(input, VLC_PLAYER_STATE_PAUSED);
break;
case END_S:
vlc_player_input_HandleState(input, VLC_PLAYER_STATE_STOPPING);
vlc_player_destructor_AddStoppingInput(input->player, input);
break;
case ERROR_S:
/* Don't send errors if the input is stopped by the user */
if (input->started)
{
/* Contrary to the input_thead_t, an error is not a state */
input->error = VLC_PLAYER_ERROR_GENERIC;
vlc_player_SendEvent(input->player, on_error_changed, input->error);
}
break;
default:
vlc_assert_unreachable();
}
}
static void
vlc_player_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)
{