aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar wm4 <wm4@nowhere>2013-12-24 17:46:14 +0100
committerGravatar wm4 <wm4@nowhere>2013-12-24 17:46:14 +0100
commit3720b3f17d4951ab839418b5cbfd1a85eb74adba (patch)
tree819b311b4c224ba2fd3dcd0efe8008a83f2cc938
parent9292f537d661af16321fd35eb0016e830594863b (diff)
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.
-rw-r--r--DOCS/man/en/input.rst1
-rw-r--r--DOCS/man/en/options.rst22
-rw-r--r--options/options.c1
-rw-r--r--options/options.h1
-rw-r--r--player/command.c49
-rw-r--r--player/core.h7
-rw-r--r--player/dvdnav.c4
-rw-r--r--player/loadfile.c51
-rw-r--r--player/osd.c4
-rw-r--r--player/playloop.c6
-rw-r--r--player/sub.c125
-rw-r--r--player/video.c3
-rw-r--r--sub/osd.c27
-rw-r--r--sub/osd.h16
-rw-r--r--sub/osd_libass.c9
15 files changed, 220 insertions, 106 deletions
diff --git a/DOCS/man/en/input.rst b/DOCS/man/en/input.rst
index aca35177a6..a5de6df18d 100644
--- a/DOCS/man/en/input.rst
+++ b/DOCS/man/en/input.rst
@@ -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``
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index a1f48d32a1..b2f9d30b68 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -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.
diff --git a/options/options.c b/options/options.c
index ea1de3e3f6..5693a34cd0 100644
--- a/options/options.c
+++ b/options/options.c
@@ -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),
diff --git a/options/options.h b/options/options.h
index 1d997d363b..4889201b93 100644
--- a/options/options.h
+++ b/options/options.h
@@ -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;
diff --git a/player/command.c b/player/command.c
index 3d15d96676..e59a2d50e8 100644
--- a/player/command.c
+++ b/player/command.c
@@ -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;
diff --git a/player/core.h b/player/core.h
index cad18a6b36..c016dcf4f7 100644
--- a/player/core.h
+++ b/player/core.h
@@ -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);
diff --git a/player/dvdnav.c b/player/dvdnav.c
index e90a65e035..bc14e7c35f 100644
--- a/player/dvdnav.c
+++ b/player/dvdnav.c
@@ -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)
diff --git a/player/loadfile.c b/player/loadfile.c
index 8bf29f22f3..99b669f109 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -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 =======================
diff --git a/player/osd.c b/player/osd.c
index 607af6714e..d1af1a6e65 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -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])
diff --git a/player/playloop.c b/player/playloop.c
index dcbc1de9ba..6359803fcf 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -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;
diff --git a/player/sub.c b/player/sub.c
index 3593bab710..cc8f89a531 100644
--- a/player/sub.c
+++ b/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, &params);
}
- 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;
-
- 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);
- }
- }
+ reinit_subdec(mpctx, track, dec_sub);
- 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);
}
diff --git a/player/video.c b/player/video.c
index 9df3caa940..039248c1d0 100644
--- a/player/video.c
+++ b/player/video.c
@@ -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;
diff --git a/sub/osd.c b/sub/osd.c
index 72ae5db841..107ca232cf 100644
--- a/sub/osd.c
+++ b/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) {
diff --git a/sub/osd.h b/sub/osd.h
index 9714a06c18..17e8a02c08 100644
--- a/sub/osd.h
+++ b/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);
diff --git a/sub/osd_libass.c b/sub/osd_libass.c
index 48083bc71f..e43408829b 100644
--- a/sub/osd_libass.c
+++ b/sub/osd_libass.c
@@ -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: