mirror of
https://github.com/mpv-player/mpv
synced 2025-03-19 18:05:21 +00:00
player: add --secondary-sid for displaying a second subtitle stream
This is relatively hacky, but it's Christmas, so it's ok. This does two things: 1. allow selecting two subtitle tracks, and 2. include a hack that renders the second subtitle always as toptitle. See manpage additions how to use this.
This commit is contained in:
parent
9292f537d6
commit
3720b3f17d
@ -595,6 +595,7 @@ Name W Comment
|
||||
``video-unscaled`` x see ``--video-unscaled``
|
||||
``program`` x switch TS program (write-only)
|
||||
``sid`` x current subtitle track (similar to ``--sid``)
|
||||
``secondary-sid`` x secondary subtitle track (``--secondary-sid``)
|
||||
``sub`` x alias for ``sid``
|
||||
``sub-delay`` x see ``--sub-delay``
|
||||
``sub-pos`` x see ``--sub-pos``
|
||||
|
@ -2017,6 +2017,28 @@ OPTIONS
|
||||
Specify the screen width for video output drivers which do not know the
|
||||
screen resolution, like ``x11`` and TV-out.
|
||||
|
||||
``--secondary-sid=<ID|auto|no>``
|
||||
Select a secondary subtitle stream. This is similar to ``--sid``. If a
|
||||
secondary subtitle is selected, it will be rendered as toptitle (i.e. on
|
||||
the top of the screen) alongside the normal subtitle, and provides a way
|
||||
to render two subtitles at once.
|
||||
|
||||
there are some caveats associated with this feature. For example, secondary
|
||||
subtitles are never shown on the terminal if video is disabled.
|
||||
|
||||
.. note::
|
||||
|
||||
Styling and interpretation of any formatting tags is disabled for the
|
||||
secondary subtitle. Internally, the same mechanism as ``--no-ass`` is
|
||||
used to strip the styling.
|
||||
|
||||
.. note::
|
||||
|
||||
If the main subtitle stream contains formatting tags which display the
|
||||
subtitle at the top of the screen, it will overlap with the secondary
|
||||
subtitle. To prevent this, you could use ``--no-ass`` to disable
|
||||
styling in the main subtitle stream.
|
||||
|
||||
``--show-profile=<profile>``
|
||||
Show the description and content of a profile.
|
||||
|
||||
|
@ -301,6 +301,7 @@ const m_option_t mp_opts[] = {
|
||||
OPT_TRACKCHOICE("aid", audio_id),
|
||||
OPT_TRACKCHOICE("vid", video_id),
|
||||
OPT_TRACKCHOICE("sid", sub_id),
|
||||
OPT_TRACKCHOICE("secondary-sid", sub2_id),
|
||||
OPT_FLAG_STORE("no-sub", sub_id, 0, -2),
|
||||
OPT_FLAG_STORE("no-video", video_id, 0, -2),
|
||||
OPT_FLAG_STORE("no-audio", audio_id, 0, -2),
|
||||
|
@ -147,6 +147,7 @@ typedef struct MPOpts {
|
||||
int audio_id;
|
||||
int video_id;
|
||||
int sub_id;
|
||||
int sub2_id;
|
||||
char **audio_lang;
|
||||
char **sub_lang;
|
||||
int audio_display;
|
||||
|
@ -980,14 +980,23 @@ static int mp_property_balance(m_option_t *prop, int action, void *arg,
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static struct track* track_next(struct MPContext *mpctx, enum stream_type type,
|
||||
int direction, struct track *track)
|
||||
static struct track* track_next(struct MPContext *mpctx, int order,
|
||||
enum stream_type type, int direction,
|
||||
struct track *track)
|
||||
{
|
||||
assert(direction == -1 || direction == +1);
|
||||
struct track *prev = NULL, *next = NULL;
|
||||
bool seen = track == NULL;
|
||||
for (int n = 0; n < mpctx->num_tracks; n++) {
|
||||
struct track *cur = mpctx->tracks[n];
|
||||
// One track can be selected only one time - pretend already selected
|
||||
// tracks don't exist.
|
||||
for (int r = 0; r < NUM_PTRACKS; r++) {
|
||||
if (r != order && mpctx->current_track[r][type] == cur)
|
||||
cur = NULL;
|
||||
}
|
||||
if (!cur)
|
||||
continue;
|
||||
if (cur->type == type) {
|
||||
if (cur == track) {
|
||||
seen = true;
|
||||
@ -1005,11 +1014,12 @@ static struct track* track_next(struct MPContext *mpctx, enum stream_type type,
|
||||
}
|
||||
|
||||
static int property_switch_track(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx, enum stream_type type)
|
||||
MPContext *mpctx, int order,
|
||||
enum stream_type type)
|
||||
{
|
||||
if (!mpctx->num_sources)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
struct track *track = mpctx->current_track[0][type];
|
||||
struct track *track = mpctx->current_track[order][type];
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
@ -1034,12 +1044,13 @@ static int property_switch_track(m_option_t *prop, int action, void *arg,
|
||||
|
||||
case M_PROPERTY_SWITCH: {
|
||||
struct m_property_switch_arg *sarg = arg;
|
||||
mp_switch_track(mpctx, type,
|
||||
track_next(mpctx, type, sarg->inc >= 0 ? +1 : -1, track));
|
||||
mp_switch_track_n(mpctx, order, type,
|
||||
track_next(mpctx, order, type, sarg->inc >= 0 ? +1 : -1, track));
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
case M_PROPERTY_SET:
|
||||
mp_switch_track(mpctx, type, mp_track_by_tid(mpctx, type, *(int *)arg));
|
||||
track = mp_track_by_tid(mpctx, type, *(int *)arg);
|
||||
mp_switch_track_n(mpctx, order, type, track);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return mp_property_generic_option(prop, action, arg, mpctx);
|
||||
@ -1102,14 +1113,14 @@ static int property_list_tracks(m_option_t *prop, int action, void *arg,
|
||||
static int mp_property_audio(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx)
|
||||
{
|
||||
return property_switch_track(prop, action, arg, mpctx, STREAM_AUDIO);
|
||||
return property_switch_track(prop, action, arg, mpctx, 0, STREAM_AUDIO);
|
||||
}
|
||||
|
||||
/// Selected video id (RW)
|
||||
static int mp_property_video(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx)
|
||||
{
|
||||
return property_switch_track(prop, action, arg, mpctx, STREAM_VIDEO);
|
||||
return property_switch_track(prop, action, arg, mpctx, 0, STREAM_VIDEO);
|
||||
}
|
||||
|
||||
static struct track *find_track_by_demuxer_id(MPContext *mpctx,
|
||||
@ -1623,7 +1634,13 @@ static int property_osd_helper(m_option_t *prop, int action, void *arg,
|
||||
static int mp_property_sub(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx)
|
||||
{
|
||||
return property_switch_track(prop, action, arg, mpctx, STREAM_SUB);
|
||||
return property_switch_track(prop, action, arg, mpctx, 0, STREAM_SUB);
|
||||
}
|
||||
|
||||
static int mp_property_sub2(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx)
|
||||
{
|
||||
return property_switch_track(prop, action, arg, mpctx, 1, STREAM_SUB);
|
||||
}
|
||||
|
||||
/// Subtitle delay (RW)
|
||||
@ -1997,6 +2014,7 @@ static const m_option_t mp_properties[] = {
|
||||
|
||||
// Subs
|
||||
M_OPTION_PROPERTY_CUSTOM("sid", mp_property_sub),
|
||||
M_OPTION_PROPERTY_CUSTOM("secondary-sid", mp_property_sub2),
|
||||
M_OPTION_PROPERTY_CUSTOM("sub-delay", mp_property_sub_delay),
|
||||
M_OPTION_PROPERTY_CUSTOM("sub-pos", mp_property_sub_pos),
|
||||
M_OPTION_PROPERTY_CUSTOM("sub-visibility", property_osd_helper),
|
||||
@ -2109,6 +2127,7 @@ static struct property_osd_display {
|
||||
{ "angle", "Angle" },
|
||||
// subs
|
||||
{ "sub", "Subtitles" },
|
||||
{ "secondary-sid", "Secondary subtitles" },
|
||||
{ "sub-pos", "Sub position" },
|
||||
{ "sub-delay", "Sub delay", .osd_id = OSD_MSG_SUB_DELAY },
|
||||
{ "sub-visibility", "Subtitles" },
|
||||
@ -2714,12 +2733,13 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
}
|
||||
|
||||
case MP_CMD_SUB_STEP:
|
||||
case MP_CMD_SUB_SEEK:
|
||||
if (mpctx->osd->dec_sub) {
|
||||
case MP_CMD_SUB_SEEK: {
|
||||
struct osd_object *obj = mpctx->osd->objs[OSDTYPE_SUB];
|
||||
if (obj->dec_sub) {
|
||||
double a[2];
|
||||
a[0] = mpctx->video_pts - mpctx->osd->video_offset + opts->sub_delay;
|
||||
a[0] = mpctx->video_pts - obj->video_offset + opts->sub_delay;
|
||||
a[1] = cmd->args[0].v.i;
|
||||
if (sub_control(mpctx->osd->dec_sub, SD_CTRL_SUB_STEP, a) > 0) {
|
||||
if (sub_control(obj->dec_sub, SD_CTRL_SUB_STEP, a) > 0) {
|
||||
if (cmd->id == MP_CMD_SUB_STEP) {
|
||||
opts->sub_delay += a[0];
|
||||
osd_changed_all(mpctx->osd);
|
||||
@ -2742,6 +2762,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MP_CMD_OSD: {
|
||||
int v = cmd->args[0].v.i;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#define INITIALIZED_ACODEC 1024
|
||||
#define INITIALIZED_VCODEC 2048
|
||||
#define INITIALIZED_SUB 4096
|
||||
#define INITIALIZED_SUB2 8192
|
||||
#define INITIALIZED_ALL 0xFFFF
|
||||
|
||||
|
||||
@ -208,7 +209,7 @@ typedef struct MPContext {
|
||||
|
||||
struct dec_video *d_video;
|
||||
struct dec_audio *d_audio;
|
||||
struct dec_sub *d_sub;
|
||||
struct dec_sub *d_sub[2];
|
||||
|
||||
// Uses: accessing metadata (consider ordered chapters case, where the main
|
||||
// demuxer defines metadata), or special purpose demuxers like TV.
|
||||
@ -439,9 +440,9 @@ void handle_force_window(struct MPContext *mpctx, bool reconfig);
|
||||
void add_frame_pts(struct MPContext *mpctx, double pts);
|
||||
|
||||
// sub.c
|
||||
void reset_subtitles(struct MPContext *mpctx);
|
||||
void reset_subtitles(struct MPContext *mpctx, int order);
|
||||
void uninit_subs(struct demuxer *demuxer);
|
||||
void reinit_subs(struct MPContext *mpctx);
|
||||
void reinit_subs(struct MPContext *mpctx, int order);
|
||||
void update_osd_msg(struct MPContext *mpctx);
|
||||
void update_subtitles(struct MPContext *mpctx);
|
||||
|
||||
|
@ -222,8 +222,8 @@ void mp_nav_get_highlight(struct osd_state *osd, struct mp_osd_res res,
|
||||
|
||||
nav->hi_elem = sub;
|
||||
int sizes[2] = {0};
|
||||
if (mpctx->d_sub)
|
||||
sub_control(mpctx->d_sub, SD_CTRL_GET_RESOLUTION, sizes);
|
||||
if (mpctx->d_sub[0])
|
||||
sub_control(mpctx->d_sub[0], SD_CTRL_GET_RESOLUTION, sizes);
|
||||
if (sizes[0] < 1 || sizes[1] < 1) {
|
||||
struct mp_image_params vid = {0};
|
||||
if (mpctx->d_video)
|
||||
|
@ -62,6 +62,16 @@
|
||||
#include "stream/dvbin.h"
|
||||
#endif
|
||||
|
||||
static void uninit_sub(struct MPContext *mpctx, int order)
|
||||
{
|
||||
if (mpctx->d_sub[order])
|
||||
sub_reset(mpctx->d_sub[order]);
|
||||
mpctx->d_sub[order] = NULL; // Note: not free'd.
|
||||
mpctx->osd->objs[order ? OSDTYPE_SUB2 : OSDTYPE_SUB]->dec_sub = NULL;
|
||||
reset_subtitles(mpctx, order);
|
||||
reselect_demux_streams(mpctx);
|
||||
}
|
||||
|
||||
void uninit_player(struct MPContext *mpctx, unsigned int mask)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
@ -80,12 +90,11 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
|
||||
|
||||
if (mask & INITIALIZED_SUB) {
|
||||
mpctx->initialized_flags &= ~INITIALIZED_SUB;
|
||||
if (mpctx->d_sub)
|
||||
sub_reset(mpctx->d_sub);
|
||||
mpctx->d_sub = NULL; // Note: not free'd.
|
||||
mpctx->osd->dec_sub = NULL;
|
||||
reset_subtitles(mpctx);
|
||||
reselect_demux_streams(mpctx);
|
||||
uninit_sub(mpctx, 0);
|
||||
}
|
||||
if (mask & INITIALIZED_SUB2) {
|
||||
mpctx->initialized_flags &= ~INITIALIZED_SUB2;
|
||||
uninit_sub(mpctx, 1);
|
||||
}
|
||||
|
||||
if (mask & INITIALIZED_LIBASS) {
|
||||
@ -110,7 +119,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
|
||||
if (mask & INITIALIZED_DEMUXER) {
|
||||
mpctx->initialized_flags &= ~INITIALIZED_DEMUXER;
|
||||
assert(!(mpctx->initialized_flags &
|
||||
(INITIALIZED_VCODEC | INITIALIZED_ACODEC | INITIALIZED_SUB)));
|
||||
(INITIALIZED_VCODEC | INITIALIZED_ACODEC |
|
||||
INITIALIZED_SUB2 | INITIALIZED_SUB)));
|
||||
for (int i = 0; i < mpctx->num_tracks; i++) {
|
||||
talloc_free(mpctx->tracks[i]);
|
||||
}
|
||||
@ -119,7 +129,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
|
||||
for (int t = 0; t < STREAM_TYPE_COUNT; t++)
|
||||
mpctx->current_track[r][t] = NULL;
|
||||
}
|
||||
assert(!mpctx->d_video && !mpctx->d_audio && !mpctx->d_sub);
|
||||
assert(!mpctx->d_video && !mpctx->d_audio &&
|
||||
!mpctx->d_sub[0] && !mpctx->d_sub[1]);
|
||||
mpctx->master_demuxer = NULL;
|
||||
for (int i = 0; i < mpctx->num_sources; i++) {
|
||||
uninit_subs(mpctx->sources[i]);
|
||||
@ -324,7 +335,7 @@ bool timeline_set_part(struct MPContext *mpctx, int i, bool force)
|
||||
enum stop_play_reason orig_stop_play = mpctx->stop_play;
|
||||
if (!mpctx->d_video && mpctx->stop_play == KEEP_PLAYING)
|
||||
mpctx->stop_play = AT_END_OF_FILE; // let audio uninit drain data
|
||||
uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts->fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts->gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_ACODEC | INITIALIZED_SUB);
|
||||
uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts->fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts->gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_ACODEC | INITIALIZED_SUB | INITIALIZED_SUB2);
|
||||
mpctx->stop_play = orig_stop_play;
|
||||
|
||||
mpctx->demuxer = n->source;
|
||||
@ -405,8 +416,10 @@ static struct track *add_stream_track(struct MPContext *mpctx,
|
||||
track->demuxer_id = stream->demuxer_id;
|
||||
// Initialize lazily selected track
|
||||
demuxer_select_track(track->demuxer, stream, track->selected);
|
||||
if (track->selected)
|
||||
reinit_subs(mpctx);
|
||||
if (mpctx->current_track[0][STREAM_SUB] == track)
|
||||
reinit_subs(mpctx, 0);
|
||||
if (mpctx->current_track[1][STREAM_SUB] == track)
|
||||
reinit_subs(mpctx, 1);
|
||||
return track;
|
||||
}
|
||||
}
|
||||
@ -605,6 +618,9 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
|
||||
} else if (type == STREAM_SUB) {
|
||||
uninit_player(mpctx, INITIALIZED_SUB);
|
||||
}
|
||||
} else if (order == 1) {
|
||||
if (type == STREAM_SUB)
|
||||
uninit_player(mpctx, INITIALIZED_SUB2);
|
||||
}
|
||||
|
||||
if (current)
|
||||
@ -631,9 +647,14 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
|
||||
mp_notify_property(mpctx, "aid");
|
||||
} else if (type == STREAM_SUB) {
|
||||
mpctx->opts->sub_id = user_tid;
|
||||
reinit_subs(mpctx);
|
||||
reinit_subs(mpctx, 0);
|
||||
mp_notify_property(mpctx, "sid");
|
||||
}
|
||||
} else if (order == 1) {
|
||||
if (type == STREAM_SUB) {
|
||||
mpctx->opts->sub2_id = user_tid;
|
||||
reinit_subs(mpctx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(mpctx->track_layout_hash);
|
||||
@ -1048,7 +1069,8 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
assert(mpctx->demuxer == NULL);
|
||||
assert(mpctx->d_audio == NULL);
|
||||
assert(mpctx->d_video == NULL);
|
||||
assert(mpctx->d_sub == NULL);
|
||||
assert(mpctx->d_sub[0] == NULL);
|
||||
assert(mpctx->d_sub[1] == NULL);
|
||||
|
||||
char *stream_filename = mpctx->filename;
|
||||
mpctx->resolve_result = resolve_url(stream_filename, mpctx->global);
|
||||
@ -1189,7 +1211,8 @@ goto_reopen_demuxer: ;
|
||||
|
||||
reinit_video_chain(mpctx);
|
||||
reinit_audio_chain(mpctx);
|
||||
reinit_subs(mpctx);
|
||||
reinit_subs(mpctx, 0);
|
||||
reinit_subs(mpctx, 1);
|
||||
|
||||
//==================== START PLAYING =======================
|
||||
|
||||
|
@ -373,8 +373,8 @@ void set_osd_subtitle(struct MPContext *mpctx, const char *text)
|
||||
{
|
||||
if (!text)
|
||||
text = "";
|
||||
if (strcmp(mpctx->osd->sub_text, text) != 0) {
|
||||
osd_set_sub(mpctx->osd, text);
|
||||
if (strcmp(mpctx->osd->objs[OSDTYPE_SUB]->sub_text, text) != 0) {
|
||||
osd_set_sub(mpctx->osd, mpctx->osd->objs[OSDTYPE_SUB], text);
|
||||
if (!mpctx->video_out) {
|
||||
rm_osd_msg(mpctx, OSD_MSG_SUB_BASE);
|
||||
if (text && text[0])
|
||||
|
@ -181,7 +181,8 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao)
|
||||
clear_audio_output_buffers(mpctx);
|
||||
}
|
||||
|
||||
reset_subtitles(mpctx);
|
||||
reset_subtitles(mpctx, 0);
|
||||
reset_subtitles(mpctx, 1);
|
||||
|
||||
mpctx->video_pts = MP_NOPTS_VALUE;
|
||||
mpctx->video_next_pts = MP_NOPTS_VALUE;
|
||||
@ -271,7 +272,8 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
|
||||
if (need_reset) {
|
||||
reinit_video_chain(mpctx);
|
||||
reinit_audio_chain(mpctx);
|
||||
reinit_subs(mpctx);
|
||||
reinit_subs(mpctx, 0);
|
||||
reinit_subs(mpctx, 1);
|
||||
}
|
||||
|
||||
int demuxer_style = 0;
|
||||
|
125
player/sub.c
125
player/sub.c
@ -66,23 +66,32 @@ static bool is_interleaved(struct MPContext *mpctx, struct track *track)
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset_subtitles(struct MPContext *mpctx)
|
||||
void reset_subtitles(struct MPContext *mpctx, int order)
|
||||
{
|
||||
if (mpctx->d_sub)
|
||||
sub_reset(mpctx->d_sub);
|
||||
struct osd_object *osd_obj =
|
||||
mpctx->osd->objs[order ? OSDTYPE_SUB2 : OSDTYPE_SUB];
|
||||
if (mpctx->d_sub[order])
|
||||
sub_reset(mpctx->d_sub[order]);
|
||||
set_osd_subtitle(mpctx, NULL);
|
||||
osd_changed(mpctx->osd, OSDTYPE_SUB);
|
||||
osd_set_sub(mpctx->osd, osd_obj, NULL);
|
||||
}
|
||||
|
||||
void update_subtitles(struct MPContext *mpctx)
|
||||
static void update_subtitle(struct MPContext *mpctx, int order)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
if (!(mpctx->initialized_flags & INITIALIZED_SUB))
|
||||
return;
|
||||
if (order == 0) {
|
||||
if (!(mpctx->initialized_flags & INITIALIZED_SUB))
|
||||
return;
|
||||
} else {
|
||||
if (!(mpctx->initialized_flags & INITIALIZED_SUB2))
|
||||
return;
|
||||
}
|
||||
|
||||
struct track *track = mpctx->current_track[0][STREAM_SUB];
|
||||
struct dec_sub *dec_sub = mpctx->d_sub;
|
||||
struct track *track = mpctx->current_track[order][STREAM_SUB];
|
||||
struct dec_sub *dec_sub = mpctx->d_sub[order];
|
||||
assert(track && dec_sub);
|
||||
struct osd_object *osd_obj
|
||||
= mpctx->osd->objs[order ? OSDTYPE_SUB2 : OSDTYPE_SUB];
|
||||
|
||||
if (mpctx->d_video) {
|
||||
struct mp_image_params params = mpctx->d_video->vf_input;
|
||||
@ -90,9 +99,9 @@ void update_subtitles(struct MPContext *mpctx)
|
||||
sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, ¶ms);
|
||||
}
|
||||
|
||||
mpctx->osd->video_offset = track->under_timeline ? mpctx->video_offset : 0;
|
||||
osd_obj->video_offset = track->under_timeline ? mpctx->video_offset : 0;
|
||||
|
||||
double refpts_s = mpctx->playback_pts - mpctx->osd->video_offset;
|
||||
double refpts_s = mpctx->playback_pts - osd_obj->video_offset;
|
||||
double curpts_s = refpts_s + opts->sub_delay;
|
||||
|
||||
if (!track->preloaded && track->stream) {
|
||||
@ -125,8 +134,19 @@ void update_subtitles(struct MPContext *mpctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (!mpctx->osd->render_bitmap_subs || !mpctx->video_out)
|
||||
set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s));
|
||||
// Handle displaying subtitles on terminal; never done for secondary subs
|
||||
if (order == 0) {
|
||||
if (!osd_obj->render_bitmap_subs || !mpctx->video_out)
|
||||
set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s));
|
||||
} else if (order == 1) {
|
||||
osd_set_sub(mpctx->osd, osd_obj, sub_get_text(dec_sub, curpts_s));
|
||||
}
|
||||
}
|
||||
|
||||
void update_subtitles(struct MPContext *mpctx)
|
||||
{
|
||||
update_subtitle(mpctx, 0);
|
||||
update_subtitle(mpctx, 1);
|
||||
}
|
||||
|
||||
static void set_dvdsub_fake_extradata(struct dec_sub *dec_sub, struct stream *st,
|
||||
@ -169,12 +189,42 @@ static void set_dvdsub_fake_extradata(struct dec_sub *dec_sub, struct stream *st
|
||||
talloc_free(s);
|
||||
}
|
||||
|
||||
void reinit_subs(struct MPContext *mpctx)
|
||||
static void reinit_subdec(struct MPContext *mpctx, struct track *track,
|
||||
struct dec_sub *dec_sub)
|
||||
{
|
||||
if (sub_is_initialized(dec_sub))
|
||||
return;
|
||||
|
||||
struct sh_video *sh_video =
|
||||
mpctx->d_video ? mpctx->d_video->header->video : NULL;
|
||||
int w = sh_video ? sh_video->disp_w : 0;
|
||||
int h = sh_video ? sh_video->disp_h : 0;
|
||||
float fps = sh_video ? sh_video->fps : 25;
|
||||
|
||||
set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h);
|
||||
sub_set_video_res(dec_sub, w, h);
|
||||
sub_set_video_fps(dec_sub, fps);
|
||||
sub_set_ass_renderer(dec_sub, mpctx->ass_library, mpctx->ass_renderer);
|
||||
sub_init_from_sh(dec_sub, track->stream);
|
||||
|
||||
// Don't do this if the file has video/audio streams. Don't do it even
|
||||
// if it has only sub streams, because reading packets will change the
|
||||
// demuxer position.
|
||||
if (!track->preloaded && track->is_external) {
|
||||
demux_seek(track->demuxer, 0, SEEK_ABSOLUTE);
|
||||
track->preloaded = sub_read_all_packets(dec_sub, track->stream);
|
||||
}
|
||||
}
|
||||
|
||||
void reinit_subs(struct MPContext *mpctx, int order)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
struct track *track = mpctx->current_track[0][STREAM_SUB];
|
||||
struct track *track = mpctx->current_track[order][STREAM_SUB];
|
||||
struct osd_object *osd_obj =
|
||||
mpctx->osd->objs[order ? OSDTYPE_SUB2 : OSDTYPE_SUB];
|
||||
int init_flag = order ? INITIALIZED_SUB2 : INITIALIZED_SUB;
|
||||
|
||||
assert(!(mpctx->initialized_flags & INITIALIZED_SUB));
|
||||
assert(!(mpctx->initialized_flags & init_flag));
|
||||
|
||||
struct sh_stream *sh = init_demux_stream(mpctx, track);
|
||||
|
||||
@ -184,48 +234,33 @@ void reinit_subs(struct MPContext *mpctx)
|
||||
return;
|
||||
|
||||
if (!sh->sub->dec_sub) {
|
||||
assert(!mpctx->d_sub);
|
||||
assert(!mpctx->d_sub[order]);
|
||||
sh->sub->dec_sub = sub_create(mpctx->global);
|
||||
}
|
||||
|
||||
assert(!mpctx->d_sub || sh->sub->dec_sub == mpctx->d_sub);
|
||||
assert(!mpctx->d_sub[order] || sh->sub->dec_sub == mpctx->d_sub[order]);
|
||||
|
||||
// The decoder is kept in the stream header in order to make ordered
|
||||
// chapters work well.
|
||||
mpctx->d_sub = sh->sub->dec_sub;
|
||||
mpctx->d_sub[order] = sh->sub->dec_sub;
|
||||
|
||||
mpctx->initialized_flags |= INITIALIZED_SUB;
|
||||
mpctx->initialized_flags |= init_flag;
|
||||
|
||||
struct dec_sub *dec_sub = mpctx->d_sub;
|
||||
struct dec_sub *dec_sub = mpctx->d_sub[order];
|
||||
assert(dec_sub);
|
||||
|
||||
if (!sub_is_initialized(dec_sub)) {
|
||||
struct sh_video *sh_video =
|
||||
mpctx->d_video ? mpctx->d_video->header->video : NULL;
|
||||
int w = sh_video ? sh_video->disp_w : 0;
|
||||
int h = sh_video ? sh_video->disp_h : 0;
|
||||
float fps = sh_video ? sh_video->fps : 25;
|
||||
reinit_subdec(mpctx, track, dec_sub);
|
||||
|
||||
set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h);
|
||||
sub_set_video_res(dec_sub, w, h);
|
||||
sub_set_video_fps(dec_sub, fps);
|
||||
sub_set_ass_renderer(dec_sub, mpctx->ass_library, mpctx->ass_renderer);
|
||||
sub_init_from_sh(dec_sub, sh);
|
||||
|
||||
// Don't do this if the file has video/audio streams. Don't do it even
|
||||
// if it has only sub streams, because reading packets will change the
|
||||
// demuxer position.
|
||||
if (!track->preloaded && track->is_external) {
|
||||
demux_seek(track->demuxer, 0, SEEK_ABSOLUTE);
|
||||
track->preloaded = sub_read_all_packets(dec_sub, sh);
|
||||
}
|
||||
}
|
||||
|
||||
mpctx->osd->dec_sub = dec_sub;
|
||||
osd_obj->dec_sub = dec_sub;
|
||||
|
||||
// Decides whether to use OSD path or normal subtitle rendering path.
|
||||
mpctx->osd->render_bitmap_subs =
|
||||
osd_obj->render_bitmap_subs =
|
||||
opts->ass_enabled || !sub_has_get_text(dec_sub);
|
||||
|
||||
reset_subtitles(mpctx);
|
||||
// Secondary subs are rendered with the "text" renderer to transform them
|
||||
// to toptitles.
|
||||
if (order == 1 && sub_has_get_text(dec_sub))
|
||||
osd_obj->render_bitmap_subs = false;
|
||||
|
||||
reset_subtitles(mpctx, order);
|
||||
}
|
||||
|
@ -199,7 +199,8 @@ int reinit_video_chain(struct MPContext *mpctx)
|
||||
mpctx->vo_pts_history_seek_ts++;
|
||||
|
||||
vo_seek_reset(mpctx->video_out);
|
||||
reset_subtitles(mpctx);
|
||||
reset_subtitles(mpctx, 0);
|
||||
reset_subtitles(mpctx, 1);
|
||||
|
||||
if (opts->force_fps) {
|
||||
d_video->fps = opts->force_fps;
|
||||
|
27
sub/osd.c
27
sub/osd.c
@ -88,21 +88,22 @@ struct osd_state *osd_create(struct mpv_global *global)
|
||||
.global = global,
|
||||
.log = mp_log_new(osd, global->log, "osd"),
|
||||
.osd_text = talloc_strdup(osd, ""),
|
||||
.sub_text = talloc_strdup(osd, ""),
|
||||
.progbar_type = -1,
|
||||
};
|
||||
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
struct osd_object *obj = talloc_struct(osd, struct osd_object, {
|
||||
struct osd_object *obj = talloc(osd, struct osd_object);
|
||||
*obj = (struct osd_object) {
|
||||
.type = n,
|
||||
});
|
||||
.sub_text = talloc_strdup(obj, ""),
|
||||
};
|
||||
for (int i = 0; i < OSD_CONV_CACHE_MAX; i++)
|
||||
obj->cache[i] = talloc_steal(obj, osd_conv_cache_new());
|
||||
osd->objs[n] = obj;
|
||||
}
|
||||
|
||||
osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c
|
||||
osd->objs[OSDTYPE_SUBTEXT]->is_sub = true; // osd_libass.c
|
||||
osd->objs[OSDTYPE_SUB]->is_sub = true;
|
||||
osd->objs[OSDTYPE_SUB2]->is_sub = true;
|
||||
|
||||
osd_init_backend(osd);
|
||||
return osd;
|
||||
@ -133,10 +134,10 @@ void osd_set_text(struct osd_state *osd, const char *text)
|
||||
osd_changed(osd, OSDTYPE_OSD);
|
||||
}
|
||||
|
||||
void osd_set_sub(struct osd_state *osd, const char *text)
|
||||
void osd_set_sub(struct osd_state *osd, struct osd_object *obj, const char *text)
|
||||
{
|
||||
if (!set_text(osd, &osd->sub_text, text))
|
||||
osd_changed(osd, OSDTYPE_SUBTEXT);
|
||||
if (!set_text(obj, &obj->sub_text, text))
|
||||
osd_changed(osd, obj->type);
|
||||
}
|
||||
|
||||
static void render_object(struct osd_state *osd, struct osd_object *obj,
|
||||
@ -157,12 +158,14 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
|
||||
obj->force_redraw = true;
|
||||
obj->vo_res = res;
|
||||
|
||||
if (obj->type == OSDTYPE_SUB) {
|
||||
if (osd->render_bitmap_subs && osd->dec_sub) {
|
||||
if (obj->type == OSDTYPE_SUB || obj->type == OSDTYPE_SUB2) {
|
||||
if (obj->render_bitmap_subs && obj->dec_sub) {
|
||||
double sub_pts = video_pts;
|
||||
if (sub_pts != MP_NOPTS_VALUE)
|
||||
sub_pts -= osd->video_offset - opts->sub_delay;
|
||||
sub_get_bitmaps(osd->dec_sub, obj->vo_res, sub_pts, out_imgs);
|
||||
sub_pts -= obj->video_offset - opts->sub_delay;
|
||||
sub_get_bitmaps(obj->dec_sub, obj->vo_res, sub_pts, out_imgs);
|
||||
} else {
|
||||
osd_object_get_bitmaps(osd, obj, out_imgs);
|
||||
}
|
||||
} else if (obj->type == OSDTYPE_EXTERNAL2) {
|
||||
if (osd->external2.format) {
|
||||
|
16
sub/osd.h
16
sub/osd.h
@ -84,7 +84,7 @@ struct mp_osd_res {
|
||||
|
||||
enum mp_osdtype {
|
||||
OSDTYPE_SUB,
|
||||
OSDTYPE_SUBTEXT,
|
||||
OSDTYPE_SUB2,
|
||||
|
||||
OSDTYPE_NAV_HIGHLIGHT, // dvdnav fake highlights
|
||||
|
||||
@ -105,6 +105,12 @@ struct osd_object {
|
||||
|
||||
bool force_redraw;
|
||||
|
||||
// OSDTYPE_SUB
|
||||
struct dec_sub *dec_sub;
|
||||
double video_offset;
|
||||
bool render_bitmap_subs;
|
||||
char *sub_text;
|
||||
|
||||
// caches for OSD conversion (internal to render_object())
|
||||
struct osd_conv_cache *cache[OSD_CONV_CACHE_MAX];
|
||||
struct sub_bitmaps cached;
|
||||
@ -124,11 +130,9 @@ struct osd_object {
|
||||
struct osd_state {
|
||||
struct osd_object *objs[MAX_OSD_PARTS];
|
||||
|
||||
double video_offset;
|
||||
double vo_pts;
|
||||
|
||||
bool render_subs_in_filter;
|
||||
bool render_bitmap_subs;
|
||||
|
||||
struct mp_osd_res last_vo_res;
|
||||
|
||||
@ -136,8 +140,6 @@ struct osd_state {
|
||||
|
||||
// OSDTYPE_OSD
|
||||
char *osd_text;
|
||||
// OSDTYPE_SUBTEXT
|
||||
char *sub_text;
|
||||
// OSDTYPE_PROGBAR
|
||||
int progbar_type; // <0: disabled, 1-255: symbol, else: no symbol
|
||||
float progbar_value; // range 0.0-1.0
|
||||
@ -148,8 +150,6 @@ struct osd_state {
|
||||
int external_res_x, external_res_y;
|
||||
// OSDTYPE_EXTERNAL2
|
||||
struct sub_bitmaps external2;
|
||||
// OSDTYPE_SUB
|
||||
struct dec_sub *dec_sub;
|
||||
// OSDTYPE_NAV_HIGHLIGHT
|
||||
void *highlight_priv;
|
||||
|
||||
@ -206,7 +206,7 @@ extern const struct m_sub_options osd_style_conf;
|
||||
|
||||
struct osd_state *osd_create(struct mpv_global *global);
|
||||
void osd_set_text(struct osd_state *osd, const char *text);
|
||||
void osd_set_sub(struct osd_state *osd, const char *text);
|
||||
void osd_set_sub(struct osd_state *osd, struct osd_object *obj, const char *text);
|
||||
void osd_changed(struct osd_state *osd, int new_value);
|
||||
void osd_changed_all(struct osd_state *osd);
|
||||
void osd_free(struct osd_state *osd);
|
||||
|
@ -411,7 +411,7 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
|
||||
|
||||
clear_obj(obj);
|
||||
|
||||
if (!osd->sub_text || !osd->sub_text[0])
|
||||
if (!obj->sub_text || !obj->sub_text[0] || obj->render_bitmap_subs)
|
||||
return;
|
||||
|
||||
create_ass_renderer(osd, obj);
|
||||
@ -423,12 +423,14 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
|
||||
|
||||
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
|
||||
mp_ass_set_style(style, obj->osd_track->PlayResY, &font);
|
||||
if (obj->type == OSDTYPE_SUB2)
|
||||
style->Alignment = 6;
|
||||
|
||||
#if LIBASS_VERSION >= 0x01010000
|
||||
ass_set_line_position(obj->osd_render, 100 - opts->sub_pos);
|
||||
#endif
|
||||
|
||||
char *escaped_text = mangle_ass(osd->sub_text);
|
||||
char *escaped_text = mangle_ass(obj->sub_text);
|
||||
add_osd_ass_event(obj->osd_track, escaped_text);
|
||||
talloc_free(escaped_text);
|
||||
}
|
||||
@ -439,7 +441,8 @@ static void update_object(struct osd_state *osd, struct osd_object *obj)
|
||||
case OSDTYPE_OSD:
|
||||
update_osd(osd, obj);
|
||||
break;
|
||||
case OSDTYPE_SUBTEXT:
|
||||
case OSDTYPE_SUB:
|
||||
case OSDTYPE_SUB2:
|
||||
update_sub(osd, obj);
|
||||
break;
|
||||
case OSDTYPE_PROGBAR:
|
||||
|
Loading…
Reference in New Issue
Block a user