You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
12 KiB
366 lines
12 KiB
/*****************************************************************************
|
|
* player_osd.c: Player osd 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_memstream.h>
|
|
#include "player.h"
|
|
#include "input/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,
|
|
double position, enum vlc_player_whence whence)
|
|
{
|
|
if (input->length != VLC_TICK_INVALID)
|
|
{
|
|
if (time == VLC_TICK_INVALID)
|
|
time = position * input->length;
|
|
else
|
|
position = time / (double) 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];
|
|
vlc_tick_to_str(time_text, time);
|
|
if (input->length != VLC_TICK_INVALID)
|
|
{
|
|
char len_text[MSTRTIME_MAX_SIZE];
|
|
vlc_tick_to_str(len_text, 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_Tracks(vlc_player_t *player, vlc_es_id_t * const *selected, vlc_es_id_t *unselect)
|
|
{
|
|
if (!selected || !selected[0])
|
|
return;
|
|
|
|
const enum es_format_category_e cat = vlc_es_id_GetCat(selected[0]);
|
|
const char *cat_name = es_format_category_to_string(cat);
|
|
int tracks_count = 0;
|
|
struct vlc_memstream stream;
|
|
|
|
for (size_t i = 0; selected[i] != NULL; i++)
|
|
{
|
|
if (unselect != NULL
|
|
&& !strcmp(vlc_es_id_GetStrId(selected[i]), vlc_es_id_GetStrId(unselect)))
|
|
continue;
|
|
|
|
const struct vlc_player_track *track =
|
|
vlc_player_GetTrack(player, selected[i]);
|
|
/* The track can be NULL if it was terminated by the playback.
|
|
* In this case, we have nothing to display for it. */
|
|
if (unlikely(track == NULL))
|
|
continue;
|
|
|
|
/* Open the stream for the first track, and exit in case of error.
|
|
* On the next tracks, add comma to separate the track names. */
|
|
if (tracks_count == 0)
|
|
{
|
|
if (vlc_memstream_open(&stream) != 0)
|
|
return;
|
|
}
|
|
else
|
|
vlc_memstream_puts(&stream, ", ");
|
|
|
|
vlc_memstream_puts(&stream, track->name);
|
|
tracks_count++;
|
|
}
|
|
|
|
if (tracks_count == 0)
|
|
{
|
|
vlc_player_osd_Message(player, _("%s track: %s"), cat_name, _("N/A"));
|
|
return;
|
|
}
|
|
|
|
/* The vlc_memstream is opened only if tracks_count != 0. */
|
|
if (vlc_memstream_close(&stream) != 0)
|
|
return;
|
|
|
|
if (tracks_count == 1)
|
|
vlc_player_osd_Message(player, _("%s track: %s"), cat_name,
|
|
stream.ptr);
|
|
else
|
|
vlc_player_osd_Message(player, _("%s tracks: %s"), cat_name,
|
|
stream.ptr);
|
|
free(stream.ptr);
|
|
}
|
|
|
|
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 && newval.i_int != oldval.i_int)
|
|
vouts_osd_Message(&vout, 1, _("Subtitle position %d px"), newval.i_int);
|
|
|
|
else if (strcmp(var, "secondary-sub-margin") == 0 && newval.i_int != oldval.i_int)
|
|
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%.2f"), newval.f_float);
|
|
}
|
|
}
|
|
|
|
(void) data;
|
|
return VLC_SUCCESS;
|
|
}
|
|
|