aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar wm4 <wm4@nowhere>2012-08-19 18:01:30 +0200
committerGravatar wm4 <wm4@nowhere>2012-09-18 21:04:45 +0200
commit0f155921b046c9e6cfed3fe601aa891c2d2a8b16 (patch)
treeced8b2b76d00f3461bf12d0b5063ce2cf6fb8fe8
parent13482fb397ce6bbcec8a9342015f21d48fb7efe6 (diff)
core: manage tracks in the frontend
Introduce a general track struct for every audio/video/subtitle track known to the frontend. External files (subtitles) are now represented as tracks too. This mainly serves to clean up the subtitle selection code: now every subtitle is simply a track, instead of using a messy numbering that goes by subtitle type (as it was stored in the global_sub_pos field). The mplayer fontend will list external subtitle files as additional tracks. The timeline code now tries to match the exact demuxer IDs of all tracks. This may cause problems when Matroska files with different track numberings are used with EDL timelines. Change demux_lavf not to set demuxer IDs, since most time they are not set.
-rw-r--r--command.c650
-rw-r--r--input/input.c4
-rw-r--r--input/input.h4
-rw-r--r--libmpdemux/demux_lavf.c3
-rw-r--r--libmpdemux/demuxer.c179
-rw-r--r--libmpdemux/demuxer.h28
-rw-r--r--libmpdemux/stheader.h7
-rw-r--r--mp_core.h76
-rw-r--r--mplayer.c751
-rw-r--r--sub/av_sub.c5
-rw-r--r--sub/av_sub.h1
-rw-r--r--sub/sub.c3
12 files changed, 694 insertions, 1017 deletions
diff --git a/command.c b/command.c
index edb0e5a77a..065802b6d0 100644
--- a/command.c
+++ b/command.c
@@ -105,86 +105,6 @@ static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
vo->dheight, vo_fs);
}
-static int sub_pos_by_source(MPContext *mpctx, int src)
-{
- int i, cnt = 0;
- if (src >= SUB_SOURCES || mpctx->sub_counts[src] == 0)
- return -1;
- for (i = 0; i < src; i++)
- cnt += mpctx->sub_counts[i];
- return cnt;
-}
-
-static int sub_source_and_index_by_pos(MPContext *mpctx, int *pos)
-{
- int start = 0;
- int i;
- for (i = 0; i < SUB_SOURCES; i++) {
- int cnt = mpctx->sub_counts[i];
- if (*pos >= start && *pos < start + cnt) {
- *pos -= start;
- return i;
- }
- start += cnt;
- }
- *pos = -1;
- return -1;
-}
-
-static int sub_source_by_pos(MPContext *mpctx, int pos)
-{
- return sub_source_and_index_by_pos(mpctx, &pos);
-}
-
-static int sub_source_pos(MPContext *mpctx)
-{
- int pos = mpctx->global_sub_pos;
- sub_source_and_index_by_pos(mpctx, &pos);
- return pos;
-}
-
-static int sub_source(MPContext *mpctx)
-{
- return sub_source_by_pos(mpctx, mpctx->global_sub_pos);
-}
-
-static void update_global_sub_size(MPContext *mpctx)
-{
- struct MPOpts *opts = &mpctx->opts;
- int i;
- int cnt = 0;
-
- if (!mpctx->demuxer) {
- mpctx->global_sub_size = -1;
- mpctx->global_sub_pos = -1;
- return;
- }
-
- // update number of demuxer sub streams
- for (i = 0; i < MAX_S_STREAMS; i++)
- if (mpctx->d_sub->demuxer->s_streams[i])
- cnt++;
- if (cnt > mpctx->sub_counts[SUB_SOURCE_DEMUX])
- mpctx->sub_counts[SUB_SOURCE_DEMUX] = cnt;
-
- // update global size
- mpctx->global_sub_size = 0;
- for (i = 0; i < SUB_SOURCES; i++)
- mpctx->global_sub_size += mpctx->sub_counts[i];
-
- // update global_sub_pos if we auto-detected a demuxer sub
- if (mpctx->global_sub_pos == -1) {
- int sub_id = -1;
- if (mpctx->demuxer->sub)
- sub_id = mpctx->demuxer->sub->id;
- if (sub_id < 0)
- sub_id = opts->sub_id;
- if (sub_id >= 0 && sub_id < mpctx->sub_counts[SUB_SOURCE_DEMUX])
- mpctx->global_sub_pos = sub_pos_by_source(mpctx, SUB_SOURCE_DEMUX) +
- sub_id;
- }
-}
-
static int mp_property_generic_option(struct m_option *prop, int action,
void *arg, MPContext *mpctx)
{
@@ -893,120 +813,91 @@ static int mp_property_balance(m_option_t *prop, int action, void *arg,
return M_PROPERTY_NOT_IMPLEMENTED;
}
-/// Selected audio id (RW)
-static int mp_property_audio(m_option_t *prop, int action, void *arg,
- MPContext *mpctx)
+static struct track* track_next(struct MPContext *mpctx, 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];
+ if (cur->type == type) {
+ if (cur == track) {
+ seen = true;
+ } else {
+ if (seen && !next) {
+ next = cur;
+ } else if (!seen || !track) {
+ prev = cur;
+ }
+ }
+ }
+ }
+ return direction > 0 ? next : prev;
+}
+
+static int property_switch_track(m_option_t *prop, int action, void *arg,
+ MPContext *mpctx, enum stream_type type)
{
- int current_id, tmp;
if (!mpctx->num_sources)
return M_PROPERTY_UNAVAILABLE;
- struct sh_audio *sh = mpctx->sh_audio;
- current_id = sh ? sh->aid : -2;
+ struct track *track = mpctx->current_track[type];
switch (action) {
case M_PROPERTY_GET:
if (!arg)
return M_PROPERTY_ERROR;
- *(int *) arg = current_id;
+ *(int *) arg = track ? track->user_tid : -1;
return M_PROPERTY_OK;
case M_PROPERTY_PRINT:
if (!arg)
return M_PROPERTY_ERROR;
- if (!sh || current_id < 0)
+ if (!track)
*(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
else {
- char *lang = demuxer_stream_lang(sh->ds->demuxer, sh->gsh);
+ char *lang = track->lang;
if (!lang)
- lang = talloc_strdup(NULL, mp_gtext("unknown"));
+ lang = mp_gtext("unknown");
- if (sh->gsh->title)
+ if (track->title)
*(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
- current_id, lang, sh->gsh->title);
+ track->user_tid, lang, track->title);
else
- *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id,
- lang);
-
- talloc_free(lang);
+ *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
+ track->user_tid, lang);
}
return M_PROPERTY_OK;
case M_PROPERTY_STEP_UP:
- case M_PROPERTY_SET:
+ case M_PROPERTY_STEP_DOWN:
+ case M_PROPERTY_SET: {
+ int i = (arg ? *((int *) arg) : +1) *
+ (action == M_PROPERTY_STEP_DOWN ? -1 : +1);
if (action == M_PROPERTY_SET && arg)
- tmp = *((int *) arg);
+ track = mp_track_by_tid(mpctx, type, i);
else
- tmp = -1;
- int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp);
- if (new_id != current_id)
- uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
- if (new_id != current_id && new_id >= 0) {
- mpctx->opts.audio_id = new_id;
- sh_audio_t *sh2;
- sh2 = mpctx->d_audio->demuxer->a_streams[mpctx->d_audio->id];
- sh2->ds = mpctx->d_audio;
- mpctx->sh_audio = sh2;
- reinit_audio_chain(mpctx);
- }
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", new_id);
+ track = track_next(mpctx, type, i > 0 ? +1 : -1, track);
+ mp_switch_track(mpctx, type, track);
return M_PROPERTY_OK;
+ }
default:
return M_PROPERTY_NOT_IMPLEMENTED;
}
+}
+/// Selected audio id (RW)
+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);
}
/// Selected video id (RW)
static int mp_property_video(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
{
- struct MPOpts *opts = &mpctx->opts;
- int current_id, tmp;
- if (!mpctx->num_sources)
- return M_PROPERTY_UNAVAILABLE;
- current_id = mpctx->sh_video ? mpctx->sh_video->vid : -2;
-
- switch (action) {
- case M_PROPERTY_GET:
- if (!arg)
- return M_PROPERTY_ERROR;
- *(int *) arg = current_id;
- return M_PROPERTY_OK;
- case M_PROPERTY_PRINT:
- if (!arg)
- return M_PROPERTY_ERROR;
-
- if (current_id < 0)
- *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
- else {
- *(char **) arg = talloc_asprintf(NULL, "(%d) %s", current_id,
- mp_gtext("unknown"));
- }
- return M_PROPERTY_OK;
-
- case M_PROPERTY_STEP_UP:
- case M_PROPERTY_SET:
- if (action == M_PROPERTY_SET && arg)
- tmp = *((int *) arg);
- else
- tmp = -1;
- int new_id = demuxer_switch_video(mpctx->d_video->demuxer, tmp);
- if (new_id != current_id)
- uninit_player(mpctx, INITIALIZED_VCODEC |
- (opts->fixed_vo && new_id >= 0 ? 0 : INITIALIZED_VO));
- if (new_id != current_id && new_id >= 0) {
- sh_video_t *sh2;
- sh2 = mpctx->d_video->demuxer->v_streams[mpctx->d_video->id];
- sh2->ds = mpctx->d_video;
- mpctx->sh_video = sh2;
- reinit_video_chain(mpctx);
- }
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", new_id);
- return M_PROPERTY_OK;
-
- default:
- return M_PROPERTY_NOT_IMPLEMENTED;
- }
+ return property_switch_track(prop, action, arg, mpctx, STREAM_VIDEO);
}
static int mp_property_program(m_option_t *prop, int action, void *arg,
@@ -1364,7 +1255,7 @@ static int mp_property_gamma(m_option_t *prop, int action, void *arg,
}
#ifdef CONFIG_TV
- if (mpctx->sh_video->ds->demuxer->type == DEMUXER_TYPE_TV) {
+ if (mpctx->sh_video->gsh->demuxer->type == DEMUXER_TYPE_TV) {
int l = strlen(prop->name);
char tv_prop[3 + l + 1];
sprintf(tv_prop, "tv_%s", prop->name);
@@ -1496,350 +1387,7 @@ static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
static int mp_property_sub(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
{
- struct MPOpts *opts = &mpctx->opts;
- demux_stream_t *const d_sub = mpctx->d_sub;
- int source = -1, reset_spu av_unused = 0; // used under CONFIG_DVDREAD
- int source_pos = -1;
-
- update_global_sub_size(mpctx);
- const int global_sub_size = mpctx->global_sub_size;
-
- if (global_sub_size <= 0)
- return M_PROPERTY_UNAVAILABLE;
-
- switch (action) {
- case M_PROPERTY_GET:
- if (!arg)
- return M_PROPERTY_ERROR;
- *(int *) arg = mpctx->global_sub_pos;
- return M_PROPERTY_OK;
- case M_PROPERTY_PRINT:
- if (!arg)
- return M_PROPERTY_ERROR;
- char *sub_name = NULL;
- if (mpctx->subdata)
- sub_name = mpctx->subdata->filename;
-#ifdef CONFIG_ASS
- if (mpctx->osd->ass_track)
- sub_name = mpctx->osd->ass_track->name;
-#endif
- if (!sub_name && mpctx->subdata)
- sub_name = mpctx->subdata->filename;
- if (sub_name) {
- const char *tmp = mp_basename(sub_name);
-
- *(char **) arg = talloc_asprintf(NULL, "(%d) %s%s",
- mpctx->set_of_sub_pos + 1,
- strlen(tmp) < 20 ? "" : "...",
- strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
- return M_PROPERTY_OK;
- }
-
- if (vo_vobsub && vobsub_id >= 0) {
- const char *language = mp_gtext("unknown");
- language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
- *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
- vobsub_id, language ? language : mp_gtext("unknown"));
- return M_PROPERTY_OK;
- }
- if (opts->sub_id >= 0 && mpctx->d_sub && mpctx->d_sub->sh) {
- struct sh_stream *sh = ((struct sh_sub *)mpctx->d_sub->sh)->gsh;
- char *lang = demuxer_stream_lang(sh->common_header->ds->demuxer, sh);
- if (!lang)
- lang = talloc_strdup(NULL, mp_gtext("unknown"));
- if (sh->title)
- *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
- opts->sub_id, lang, sh->title);
- else
- *(char **) arg = talloc_asprintf(NULL, "(%d) %s", opts->sub_id,
- lang);
- talloc_free(lang);
- return M_PROPERTY_OK;
- }
- *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
- return M_PROPERTY_OK;
-
- case M_PROPERTY_SET:
- if (!arg)
- return M_PROPERTY_ERROR;
- if (*(int *) arg < -1)
- *(int *) arg = -1;
- else if (*(int *) arg >= global_sub_size)
- *(int *) arg = global_sub_size - 1;
- mpctx->global_sub_pos = *(int *) arg;
- break;
- case M_PROPERTY_STEP_UP:
- mpctx->global_sub_pos += 2;
- mpctx->global_sub_pos =
- (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
- break;
- case M_PROPERTY_STEP_DOWN:
- mpctx->global_sub_pos += global_sub_size + 1;
- mpctx->global_sub_pos =
- (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
- break;
- default:
- return M_PROPERTY_NOT_IMPLEMENTED;
- }
-
- if (mpctx->global_sub_pos >= 0) {
- source = sub_source(mpctx);
- source_pos = sub_source_pos(mpctx);
- }
-
- mp_msg(MSGT_CPLAYER, MSGL_DBG3,
- "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
- global_sub_size,
- mpctx->sub_counts[SUB_SOURCE_VOBSUB],
- mpctx->sub_counts[SUB_SOURCE_SUBS],
- mpctx->sub_counts[SUB_SOURCE_DEMUX],
- mpctx->global_sub_pos, source);
-
- mpctx->set_of_sub_pos = -1;
- mpctx->subdata = NULL;
-
- vobsub_id = -1;
- opts->sub_id = -1;
- if (d_sub) {
- if (d_sub->id > -2)
- reset_spu = 1;
- d_sub->id = -2;
- }
- mpctx->osd->ass_track = NULL;
- uninit_player(mpctx, INITIALIZED_SUB);
-
- if (source == SUB_SOURCE_VOBSUB)
- vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
- else if (source == SUB_SOURCE_SUBS) {
- mpctx->set_of_sub_pos = source_pos;
-#ifdef CONFIG_ASS
- if (opts->ass_enabled
- && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos]) {
- mpctx->osd->ass_track =
- mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos];
- mpctx->osd->ass_track_changed = true;
- mpctx->osd->vsfilter_aspect =
- mpctx->track_was_native_ass[mpctx->set_of_sub_pos];
- } else
-#endif
- {
- mpctx->subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
- vo_osd_changed(OSDTYPE_SUBTITLE);
- }
- } else if (source == SUB_SOURCE_DEMUX) {
- opts->sub_id = source_pos;
- if (d_sub && opts->sub_id < MAX_S_STREAMS) {
- int i = 0;
- // default: assume 1:1 mapping of sid and stream id
- d_sub->id = opts->sub_id;
- d_sub->sh = mpctx->d_sub->demuxer->s_streams[d_sub->id];
- ds_free_packs(d_sub);
- for (i = 0; i < MAX_S_STREAMS; i++) {
- sh_sub_t *sh = mpctx->d_sub->demuxer->s_streams[i];
- if (sh && sh->sid == opts->sub_id) {
- d_sub->id = i;
- d_sub->sh = sh;
- break;
- }
- }
- if (d_sub->sh && d_sub->id >= 0) {
- sh_sub_t *sh = d_sub->sh;
- if (sh->type == 'v')
- init_vo_spudec(mpctx);
- else {
- sub_init(sh, mpctx->osd);
- mpctx->initialized_flags |= INITIALIZED_SUB;
- }
- } else {
- d_sub->id = -2;
- d_sub->sh = NULL;
- }
- }
- }
-#ifdef CONFIG_DVDREAD
- if (vo_spudec && (mpctx->stream->type == STREAMTYPE_DVD)
- && opts->sub_id < 0 && reset_spu)
- {
- d_sub->id = -2;
- d_sub->sh = NULL;
- }
-#endif
-
- update_subtitles(mpctx, 0, true);
-
- return M_PROPERTY_OK;
-}
-
-/// Selected sub source (RW)
-static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
- MPContext *mpctx)
-{
- int source;
- update_global_sub_size(mpctx);
- if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
- return M_PROPERTY_UNAVAILABLE;
-
- switch (action) {
- case M_PROPERTY_GET:
- if (!arg)
- return M_PROPERTY_ERROR;
- *(int *) arg = sub_source(mpctx);
- return M_PROPERTY_OK;
- case M_PROPERTY_PRINT:
- if (!arg)
- return M_PROPERTY_ERROR;
- char *sourcename;
- switch (sub_source(mpctx)) {
- case SUB_SOURCE_SUBS:
- sourcename = mp_gtext("file");
- break;
- case SUB_SOURCE_VOBSUB:
- sourcename = mp_gtext("vobsub");
- break;
- case SUB_SOURCE_DEMUX:
- sourcename = mp_gtext("embedded");
- break;
- default:
- sourcename = mp_gtext("disabled");
- }
- *(char **)arg = talloc_strdup(NULL, sourcename);
- return M_PROPERTY_OK;
- case M_PROPERTY_SET:
- if (!arg)
- return M_PROPERTY_ERROR;
- M_PROPERTY_CLAMP(prop, *(int *)arg);
- if (*(int *) arg < 0)
- mpctx->global_sub_pos = -1;
- else if (*(int *) arg != sub_source(mpctx)) {
- int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
- if (new_pos == -1)
- return M_PROPERTY_UNAVAILABLE;
- mpctx->global_sub_pos = new_pos;
- }
- break;
- case M_PROPERTY_STEP_UP:
- case M_PROPERTY_STEP_DOWN: {
- int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
- * (action == M_PROPERTY_STEP_UP ? 1 : -1);
- int step = (step_all > 0) ? 1 : -1;
- int cur_source = sub_source(mpctx);
- source = cur_source;
- while (step_all) {
- source += step;
- if (source >= SUB_SOURCES)
- source = -1;
- else if (source < -1)
- source = SUB_SOURCES - 1;
- if (source == cur_source || source == -1 ||
- mpctx->sub_counts[source])
- step_all -= step;
- }
- if (source == cur_source)
- return M_PROPERTY_OK;
- if (source == -1)
- mpctx->global_sub_pos = -1;
- else
- mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
- break;
- }
- default:
- return M_PROPERTY_NOT_IMPLEMENTED;
- }
- --mpctx->global_sub_pos;
- return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
-}
-
-/// Selected subtitles from specific source (RW)
-static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
- MPContext *mpctx)
-{
- int source, is_cur_source, offset, new_pos;
- update_global_sub_size(mpctx);
- if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
- return M_PROPERTY_UNAVAILABLE;
-
- if (!strcmp(prop->name, "sub_file"))
- source = SUB_SOURCE_SUBS;
- else if (!strcmp(prop->name, "sub_vob"))
- source = SUB_SOURCE_VOBSUB;
- else if (!strcmp(prop->name, "sub_demux"))
- source = SUB_SOURCE_DEMUX;
- else
- return M_PROPERTY_ERROR;
-
- offset = sub_pos_by_source(mpctx, source);
- if (offset < 0)
- return M_PROPERTY_UNAVAILABLE;
-
- is_cur_source = sub_source(mpctx) == source;
- new_pos = mpctx->global_sub_pos;
- switch (action) {
- case M_PROPERTY_GET:
- if (!arg)
- return M_PROPERTY_ERROR;
- if (is_cur_source) {
- *(int *) arg = sub_source_pos(mpctx);
- if (source == SUB_SOURCE_VOBSUB)
- *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
- } else
- *(int *) arg = -1;
- return M_PROPERTY_OK;
- case M_PROPERTY_PRINT:
- if (!arg)
- return M_PROPERTY_ERROR;
- if (is_cur_source)
- return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
- *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
- return M_PROPERTY_OK;
- case M_PROPERTY_SET:
- if (!arg)
- return M_PROPERTY_ERROR;
- if (*(int *) arg >= 0) {
- int index = *(int *)arg;
- if (source == SUB_SOURCE_VOBSUB)
- index = vobsub_get_index_by_id(vo_vobsub, index);
- new_pos = offset + index;
- if (index < 0 || index > mpctx->sub_counts[source]) {
- new_pos = -1;
- *(int *) arg = -1;
- }
- } else
- new_pos = -1;
- break;
- case M_PROPERTY_STEP_UP:
- case M_PROPERTY_STEP_DOWN: {
- int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
- * (action == M_PROPERTY_STEP_UP ? 1 : -1);
- int step = (step_all > 0) ? 1 : -1;
- int max_sub_pos_for_source = -1;
- if (!is_cur_source)
- new_pos = -1;
- while (step_all) {
- if (new_pos == -1) {
- if (step > 0)
- new_pos = offset;
- else if (max_sub_pos_for_source == -1) {
- // Find max pos for specific source
- new_pos = mpctx->global_sub_size - 1;
- while (new_pos >= 0 && sub_source(mpctx) != source)
- new_pos--;
- } else
- new_pos = max_sub_pos_for_source;
- } else {
- new_pos += step;
- if (new_pos < offset ||
- new_pos >= mpctx->global_sub_size ||
- sub_source(mpctx) != source)
- new_pos = -1;
- }
- step_all -= step;
- }
- break;
- }
- default:
- return M_PROPERTY_NOT_IMPLEMENTED;
- }
- return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
+ return property_switch_track(prop, action, arg, mpctx, STREAM_SUB);
}
/// Subtitle delay (RW)
@@ -1859,8 +1407,7 @@ static int mp_property_sub_alignment(m_option_t *prop, int action,
_("top"), _("center"), _("bottom")
};
- if (!mpctx->sh_video || mpctx->global_sub_pos < 0
- || sub_source(mpctx) != SUB_SOURCE_SUBS)
+ if (!mpctx->current_track[STREAM_SUB])
return M_PROPERTY_UNAVAILABLE;
switch (action) {
@@ -2183,14 +1730,6 @@ static const m_option_t mp_properties[] = {
// Subs
{ "sub", mp_property_sub, CONF_TYPE_INT,
M_OPT_MIN, -1, 0, NULL },
- { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
- M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
- { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
- M_OPT_MIN, -1, 0, NULL },
- { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
- M_OPT_MIN, -1, 0, NULL },
- { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
- M_OPT_MIN, -1, 0, NULL },
{ "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
0, 0, 0, NULL },
{ "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
@@ -2299,10 +1838,6 @@ static struct property_osd_display {
{ "vsync", 0, -1, _("VSync: %s") },
// subs
{ "sub", 0, -1, _("Subtitles: %s") },
- { "sub_source", 0, -1, _("Sub source: %s") },
- { "sub_vob", 0, -1, _("Subtitles: %s") },
- { "sub_demux", 0, -1, _("Subtitles: %s") },
- { "sub_file", 0, -1, _("Subtitles: %s") },
{ "sub_pos", 0, -1, _("Sub position: %s/100") },
{ "sub_alignment", 0, -1, _("Sub alignment: %s") },
{ "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
@@ -2423,10 +1958,6 @@ static struct {
{ "vsync", MP_CMD_SWITCH_VSYNC, 1},
// subs
{ "sub", MP_CMD_SUB_SELECT, 1},
- { "sub_source", MP_CMD_SUB_SOURCE, 1},
- { "sub_vob", MP_CMD_SUB_VOB, 1},
- { "sub_demux", MP_CMD_SUB_DEMUX, 1},
- { "sub_file", MP_CMD_SUB_FILE, 1},
{ "sub_pos", MP_CMD_SUB_POS, 0},
{ "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
{ "sub_delay", MP_CMD_SUB_DELAY, 0},
@@ -2539,52 +2070,46 @@ static void show_chapters_on_osd(MPContext *mpctx)
talloc_free(res);
}
+static const char *track_type_name(enum stream_type t)
+{
+ switch (t) {
+ case STREAM_VIDEO: return "Video";
+ case STREAM_AUDIO: return "Audio";
+ case STREAM_SUB: return "Sub";
+ }
+ return NULL;
+}
+
static void show_tracks_on_osd(MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
- demuxer_t *demuxer = mpctx->master_demuxer;
char *res = NULL;
- if (!demuxer)
- return;
-
- struct sh_stream *cur_a = mpctx->sh_audio ? mpctx->sh_audio->gsh : NULL;
- struct sh_stream *cur_s = NULL;
- if (opts->sub_id >= 0 && mpctx->d_sub && mpctx->d_sub->sh)
- cur_s = ((struct sh_sub *)mpctx->d_sub->sh)->gsh;
-
- int v_count = 0;
- enum stream_type t = STREAM_AUDIO;
-
- for (int n = 0; n < demuxer->num_streams; n++) {
- struct sh_stream *sh = demuxer->streams[n];
- if (sh->type == STREAM_VIDEO) {
- v_count++;
- continue;
- }
- if (t != sh->type)
+ for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (track->type != type)
+ continue;
+
+ bool selected = mpctx->current_track[track->type] == track;
+ res = talloc_asprintf_append(res, "%s: ", track_type_name(track->type));
+ if (selected)
+ res = talloc_asprintf_append(res, "> ");
+ res = talloc_asprintf_append(res, "(%d) ", track->user_tid);
+ if (track->title)
+ res = talloc_asprintf_append(res, "'%s' ", track->title);
+ if (track->lang)
+ res = talloc_asprintf_append(res, "(%s) ", track->lang);
+ if (track->is_external)
+ res = talloc_asprintf_append(res, "(external) ");
+ if (selected)
+ res = talloc_asprintf_append(res, "<");
res = talloc_asprintf_append(res, "\n");
- bool selected = sh == cur_a || sh == cur_s;
- res = talloc_asprintf_append(res, "%s: ",
- sh->type == STREAM_AUDIO ? "Audio" : "Sub");
- if (selected)
- res = talloc_asprintf_append(res, "> ");
- res = talloc_asprintf_append(res, "(%d) ", sh->tid);
- if (sh->title)
- res = talloc_asprintf_append(res, "'%s' ", sh->title);
- char *lang = demuxer_stream_lang(sh->common_header->ds->demuxer, sh);
- if (lang)
- res = talloc_asprintf_append(res, "(%s) ", lang);
- talloc_free(lang);
- if (selected)
- res = talloc_asprintf_append(res, "<");
+ }
+
res = talloc_asprintf_append(res, "\n");
- t = sh->type;
}
- if (v_count > 1)
- res = talloc_asprintf_append(res, "\n(Warning: more than one video stream.)\n");
-
set_osd_msg(mpctx, OSD_MSG_TEXT, 1, opts->osd_duration, "%s", res);
talloc_free(res);
}
@@ -2783,7 +2308,9 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_SUB_STEP:
if (sh_video) {
int movement = cmd->args[0].v.i;
- step_sub(mpctx->subdata, mpctx->video_pts, movement);
+ struct track *track = mpctx->current_track[STREAM_SUB];
+ if (track && track->subdata)
+ step_sub(track->subdata, mpctx->video_pts, movement);
#ifdef CONFIG_ASS
if (mpctx->osd->ass_track)
sub_delay +=
@@ -3083,12 +2610,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_SUB_LOAD:
if (sh_video) {
- int n = mpctx->set_of_sub_size;
add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
- if (n != mpctx->set_of_sub_size) {
- mpctx->sub_counts[SUB_SOURCE_SUBS]++;
- ++mpctx->global_sub_size;
- }
}
break;
diff --git a/input/input.c b/input/input.c
index 4ea9e0c531..d1136532ed 100644
--- a/input/input.c
+++ b/input/input.c
@@ -134,10 +134,6 @@ static const mp_cmd_t mp_cmds[] = {
{ MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } },
{ MP_CMD_SUB_SELECT, "vobsub_lang", { OARG_INT(-2) } }, // for compatibility
{ MP_CMD_SUB_SELECT, "sub_select", { OARG_INT(-2) } },
- { MP_CMD_SUB_SOURCE, "sub_source", { OARG_INT(-2) } },
- { MP_CMD_SUB_VOB, "sub_vob", { OARG_INT(-2) } },
- { MP_CMD_SUB_DEMUX, "sub_demux", { OARG_INT(-2) } },
- { MP_CMD_SUB_FILE, "sub_file", { OARG_INT(-2) } },
{ MP_CMD_SUB_SCALE, "sub_scale", { ARG_FLOAT, OARG_INT(0) } },
#ifdef CONFIG_ASS
{ MP_CMD_ASS_USE_MARGINS, "ass_use_margins", { OARG_INT(-1) } },
diff --git a/input/input.h b/input/input.h
index e1e333c486..a055b59138 100644
--- a/input/input.h
+++ b/input/input.h
@@ -118,10 +118,6 @@ enum mp_command_type {
MP_CMD_BALANCE,
MP_CMD_SUB_SCALE,
MP_CMD_TV_START_SCAN,
- MP_CMD_SUB_SOURCE,
- MP_CMD_SUB_FILE,
- MP_CMD_SUB_VOB,
- MP_CMD_SUB_DEMUX,
MP_CMD_SWITCH_ANGLE,
MP_CMD_ASS_USE_MARGINS,
MP_CMD_SWITCH_TITLE,
diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c
index baf013a8ef..4dd9938d07 100644
--- a/libmpdemux/demux_lavf.c
+++ b/libmpdemux/demux_lavf.c
@@ -309,7 +309,6 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
sh_audio = new_sh_audio_aid(demuxer, i, priv->audio_streams);
if (!sh_audio)
break;
- sh_audio->gsh->demuxer_id = i;
sh_audio->demuxer_codecname = codec_name;
stream_type = "audio";
priv->astreams[priv->audio_streams] = i;
@@ -391,7 +390,6 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
sh_video = new_sh_video_vid(demuxer, i, priv->video_streams);
if (!sh_video)
break;
- sh_video->gsh->demuxer_id = i;
sh_video->demuxer_codecname = codec_name;
stream_type = "video";
priv->vstreams[priv->video_streams] = i;
@@ -504,7 +502,6 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i)
sh_sub = new_sh_sub_sid(demuxer, i, priv->sub_streams);
if (!sh_sub)
break;
- sh_sub->gsh->demuxer_id = i;
sh_sub->demuxer_codecname = codec_name;
stream_type = "subtitle";
priv->sstreams[priv->sub_streams] = i;
diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c
index bc2e132c60..13f5fab268 100644
--- a/libmpdemux/demuxer.c
+++ b/libmpdemux/demuxer.c
@@ -189,10 +189,12 @@ static void free_demuxer_stream(struct demux_stream *ds)
free(ds);
}
-static struct demux_stream *new_demuxer_stream(struct demuxer *demuxer, int id)
+static struct demux_stream *new_demuxer_stream(struct demuxer *demuxer,
+ enum stream_type type, int id)
{
demux_stream_t *ds = malloc(sizeof(demux_stream_t));
- *ds = (demux_stream_t){
+ *ds = (demux_stream_t) {
+ .stream_type = type,
.id = id,
.demuxer = demuxer,
.asf_seq = -1,
@@ -200,6 +202,19 @@ static struct demux_stream *new_demuxer_stream(struct demuxer *demuxer, int id)
return ds;
}
+struct sh_stream *ds_gsh(struct demux_stream *ds)
+{
+ // Ideally ds would have a gsh field, but since all the old demuxers set
+ // ds->sh themselves and we don't want to change them, enjoy this hack.
+ if (!ds->sh)
+ return NULL;
+ switch (ds->stream_type) {
+ case STREAM_VIDEO: return ((struct sh_video *)ds->sh)->gsh;
+ case STREAM_AUDIO: return ((struct sh_audio *)ds->sh)->gsh;
+ case STREAM_SUB: return ((struct sh_sub *)ds->sh)->gsh;
+ }
+ assert(false);
+}
/**
* Get demuxer description structure for a given demuxer type
@@ -231,9 +246,12 @@ demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
d->seekable = 1;
d->synced = 0;
d->filepos = -1;
- d->audio = new_demuxer_stream(d, a_id);
- d->video = new_demuxer_stream(d, v_id);
- d->sub = new_demuxer_stream(d, s_id);
+ d->audio = new_demuxer_stream(d, STREAM_VIDEO, a_id);
+ d->video = new_demuxer_stream(d, STREAM_AUDIO, v_id);
+ d->sub = new_demuxer_stream(d, STREAM_SUB, s_id);
+ d->ds[STREAM_VIDEO] = d->video;
+ d->ds[STREAM_AUDIO] = d->audio;
+ d->ds[STREAM_SUB] = d->sub;
d->type = type;
d->opts = opts;
if (type)
@@ -269,8 +287,9 @@ static struct sh_stream *new_sh_stream(demuxer_t *demuxer,
{
struct sh_stream *sh = talloc_struct(demuxer, struct sh_stream, {
.type = type,
+ .demuxer = demuxer,
.index = demuxer->num_streams,
- .demuxer_id = demuxer->new_stream_id++, // possibly temporary value only
+ .demuxer_id = tid, // may be overwritten by demuxer
.tid = tid,
.stream_index = stream_index,
.opts = demuxer->opts,
@@ -308,7 +327,6 @@ static struct sh_stream *new_sh_stream(demuxer_t *demuxer,
}
default: assert(false);
}
- sh->common_header->id = sh->tid;
sh->common_header->opts = sh->opts;
sh->common_header->gsh = sh;
return sh;
@@ -1235,37 +1253,42 @@ int demux_control(demuxer_t *demuxer, int cmd, void *arg)
return DEMUXER_CTRL_NOTIMPL;
}
-int demuxer_switch_audio(demuxer_t *demuxer, int index)
+struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
+ enum stream_type t, int id)
{
- int res = demux_control(demuxer, DEMUXER_CTRL_SWITCH_AUDIO, &index);
- if (res == DEMUXER_CTRL_NOTIMPL) {
- struct sh_audio *sh_audio = demuxer->audio->sh;
- return sh_audio ? sh_audio->aid : -2;
+ for (int n = 0; n < d->num_streams; n++) {
+ struct sh_stream *s = d->streams[n];
+ if (s->type == t && s->demuxer_id == id)
+ return d->streams[n];
}
- if (demuxer->audio->id >= 0) {
- struct sh_audio *sh_audio = demuxer->a_streams[demuxer->audio->id];
- demuxer->audio->sh = sh_audio;
- index = sh_audio->aid; // internal MPEG demuxers don't set it right
- }
- else
- demuxer->audio->sh = NULL;
- return index;
+ return NULL;
}
-int demuxer_switch_video(demuxer_t *demuxer, int index)
-{
- int res = demux_control(demuxer, DEMUXER_CTRL_SWITCH_VIDEO, &index);
- if (res == DEMUXER_CTRL_NOTIMPL) {
- struct sh_video *sh_video = demuxer->video->sh;
- return sh_video ? sh_video->vid : -2;
+void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
+ struct sh_stream *stream)
+{
+ assert(!stream || stream->type == type);
+ int index = stream ? stream->tid : -2;
+ if (type == STREAM_AUDIO) {
+ demux_control(demuxer, DEMUXER_CTRL_SWITCH_AUDIO, &index);
+ } else if (type == STREAM_VIDEO) {
+ demux_control(demuxer, DEMUXER_CTRL_SWITCH_VIDEO, &index);
+ } else if (type == STREAM_SUB) {
+ int index2 = stream ? stream->stream_index : -2;
+ if (demuxer->ds[type]->id != index2)
+ ds_free_packs(demuxer->ds[type]);
+ demuxer->ds[type]->id = index2;
}
- if (demuxer->video->id >= 0) {
- struct sh_video *sh_video = demuxer->v_streams[demuxer->video->id];
- demuxer->video->sh = sh_video;
- index = sh_video->vid; // internal MPEG demuxers don't set it right
- } else
- demuxer->video->sh = NULL;
- return index;
+ int new_id = demuxer->ds[type]->id;
+ void *new = NULL;
+ if (new_id >= 0) {
+ switch (type) {
+ case STREAM_VIDEO: new = demuxer->v_streams[new_id]; break;
+ case STREAM_AUDIO: new = demuxer->a_streams[new_id]; break;
+ case STREAM_SUB: new = demuxer->s_streams[new_id]; break;
+ }
+ }
+ demuxer->ds[type]->sh = new;
}
int demuxer_add_attachment(demuxer_t *demuxer, struct bstr name,
@@ -1456,91 +1479,15 @@ int demuxer_set_angle(demuxer_t *demuxer, int angle)
return angle;
}
-static char *demuxer_audio_lang(demuxer_t *d, int id)
-{
- struct stream_lang_req req;
- sh_audio_t *sh;
- if (id < 0 || id >= MAX_A_STREAMS)
- return NULL;
- sh = d->a_streams[id];
- if (!sh)
- return NULL;
- if (sh->lang)
- return talloc_strdup(NULL, sh->lang);
- req.type = stream_ctrl_audio;
- req.id = sh->aid;
- if (stream_control(d->stream, STREAM_CTRL_GET_LANG, &req) == STREAM_OK)
- return req.name;
- return NULL;
-}
-
-static char *demuxer_sub_lang(demuxer_t *d, int id)
+char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *sh)
{
- struct stream_lang_req req;
- sh_sub_t *sh;
- if (id < 0 || id >= MAX_S_STREAMS)
- return NULL;
- sh = d->s_streams[id];
- if (sh && sh->lang)
- return talloc_strdup(NULL, sh->lang);
- req.type = stream_ctrl_sub;
- // assume 1:1 mapping so we can show the language of
- // DVD subs even when we have not yet created the stream.
- req.id = sh ? sh->sid : id;
+ struct stream_lang_req req = { .id = sh->tid }; // assume 1:1 mapping
+ switch (sh->type) {
+ case STREAM_AUDIO: req.type = stream_ctrl_audio; break;
+ case STREAM_SUB: req.type = stream_ctrl_sub; break;
+ default: return NULL;
+ }
if (stream_control(d->stream, STREAM_CTRL_GET_LANG, &req) == STREAM_OK)
return req.name;
return NULL;
}
-
-char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *s)
-{
- switch (s->type) {
- case STREAM_AUDIO: return demuxer_audio_lang(d, s->stream_index);
- case STREAM_SUB: return demuxer_sub_lang(d, s->stream_index);
- default: return NULL;
- }
-}
-
-int demuxer_audio_track_by_lang_and_default(struct demuxer *d, char **langt)
-{
- int n = 0;
- while (1) {
- char *lang = langt ? langt[n++] : NULL;
- int id = -1;
- for (int i = 0; i < MAX_A_STREAMS; i++) {
- struct sh_audio *sh = d->a_streams[i];
- if (sh && (!lang || sh->lang && !strcmp(lang, sh->lang))) {
- if (sh->gsh->default_track)
- return sh->aid;
- if (id < 0)
- id = sh->aid;
- }
- }
- if (id >= 0)
- return id;
- if (!lang)
- return -1;
- }
-}
-
-int demuxer_sub_track_by_lang_and_default(struct demuxer *d, char **langt)
-{
- int n = 0;
- while (1) {
- char *lang = langt ? langt[n++] : NULL;
- int id = -1;
- for (int i = 0; i < MAX_S_STREAMS; i++) {
- struct sh_sub *sh = d->s_streams[i];
- if (sh && (!lang || sh->lang && !strcmp(lang, sh->lang))) {
- if (sh->gsh->default_track)
- return sh->sid;
- if (id < 0)
- id = sh->sid;
- }
- }
- if (!lang)
- return -1;
- if (id >= 0)
- return id;
- }
-}
diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h
index 63910240bc..50ab0525c4 100644
--- a/libmpdemux/demuxer.h
+++ b/libmpdemux/demuxer.h
@@ -28,6 +28,7 @@
#include "bstr.h"
#include "mpcommon.h"
#include "demux_packet.h"
+#include "stheader.h"
struct MPOpts;
@@ -103,6 +104,7 @@ enum timestamp_type {
#define MP_INPUT_BUFFER_PADDING_SIZE 16
typedef struct demux_stream {
+ enum stream_type stream_type;
int buffer_pos; // current buffer position
int buffer_size; // current buffer size
unsigned char *buffer; // current buffer, never free() it, always use free_demux_packet(buffer_ref);
@@ -233,6 +235,9 @@ typedef struct demuxer {
bool accurate_seek;
enum timestamp_type timestamp_type;
+ struct demux_stream *ds[STREAM_TYPE_COUNT]; // video/audio/sub buffers
+
+ // These correspond to ds[], e.g.: audio == ds[STREAM_AUDIO]
struct demux_stream *audio; // audio buffer/demuxer
struct demux_stream *video; // video buffer/demuxer
struct demux_stream *sub; // dvd subtitle buffer/demuxer
@@ -261,8 +266,6 @@ typedef struct demuxer {
char **info; // metadata
struct MPOpts *opts;
struct demuxer_params *params;
-
- int new_stream_id;
} demuxer_t;
typedef struct {
@@ -295,6 +298,8 @@ struct demuxer *new_demuxer(struct MPOpts *opts, struct stream *stream,
char *filename);
void free_demuxer(struct demuxer *demuxer);
+struct sh_stream *ds_gsh(struct demux_stream *ds);
+
void ds_add_packet(struct demux_stream *ds, struct demux_packet *dp);
void ds_read_packet(struct demux_stream *ds, struct stream *stream, int len,
double pts, off_t pos, bool keyframe);
@@ -369,8 +374,8 @@ char *demux_info_get(struct demuxer *demuxer, const char *opt);
int demux_info_print(struct demuxer *demuxer);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
-int demuxer_switch_audio(struct demuxer *demuxer, int index);
-int demuxer_switch_video(struct demuxer *demuxer, int index);
+void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
+ struct sh_stream *stream);
int demuxer_type_by_filename(char *filename);
@@ -398,19 +403,8 @@ int demuxer_set_angle(struct demuxer *demuxer, int angle);
/// Get number of angles.
int demuxer_angles_count(struct demuxer *demuxer);
-/* Get the index of a track.
- * lang is a string list, NULL is same as empty list
- * Sort tracks based on the following criteria:
- * 1) earlier match in lang list, or last no match
- * 2) track is marked default (default wins)
- * 3) track number (lower wins)
- * For audio, select best track according to these criteria; only return -1
- * if there are no tracks at all.
- * For subs, select best track according to the same criteria, but return -1
- * if all tracks are no-lang-match, not-default.
- */
-int demuxer_audio_track_by_lang_and_default(struct demuxer *d, char **langt);
-int demuxer_sub_track_by_lang_and_default(struct demuxer *d, char **langt);
+struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
+ enum stream_type t, int id);
char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *s);
diff --git a/libmpdemux/stheader.h b/libmpdemux/stheader.h
index f36ac8c347..90d06f96d5 100644
--- a/libmpdemux/stheader.h
+++ b/libmpdemux/stheader.h
@@ -27,22 +27,24 @@ struct MPOpts;
struct demuxer;
enum stream_type {
- STREAM_VIDEO = 1,
+ STREAM_VIDEO,
STREAM_AUDIO,
STREAM_SUB,
+ STREAM_TYPE_COUNT,
};
// Stream headers:
struct sh_stream {
enum stream_type type;
+ struct demuxer *demuxer;
// Index into demuxer->streams.
int index;
// The (possibly) type specific id, e.g. aid or sid.
int tid;
// Index into stream array (currently one array per type, e.g. a_streams).
int stream_index;
- // Demuxer specific ID (-1 if unknown, otherwise >= 0).
+ // Demuxer specific ID (always set, defaults to tid).
int demuxer_id;
// Abomination.
struct sh_common *common_header;
@@ -61,7 +63,6 @@ struct sh_stream {
#define SH_COMMON \
- int id; \
struct sh_stream *gsh; \
const char *demuxer_codecname; \
struct MPOpts *opts; \
diff --git a/mp_core.h b/mp_core.h
index 6eea692acd..04a3dac735 100644
--- a/mp_core.h
+++ b/mp_core.h
@@ -25,6 +25,7 @@
#include "mixer.h"
#include "sub/subreader.h"
#include "sub/find_subfiles.h"
+#include "libmpdemux/demuxer.h"
// definitions used internally by the core player code
@@ -75,6 +76,46 @@ struct chapter {
char *name;
};
+struct track {
+ enum stream_type type;
+ // The type specific ID, also called aid (audio), sid (subs), vid (video).
+ // For UI purposes only; this ID doesn't have anything to do with any
+ // IDs coming from demuxers or container files.
+ int user_tid;
+
+ // Same as stream->demuxer_id. -1 if not set.
+ int demuxer_id;
+
+ char *title;
+ bool default_track;
+ char *lang;
+
+ // If this track is from an external file (e.g. subtitle file).
+ bool is_external;
+
+ // If the track's stream changes with the timeline (ordered chapters).
+ bool under_timeline;
+
+ // NULL if not backed by a demuxer (e.g. external subtitles).
+ // Value can change if under_timeline==true.
+ struct demuxer *demuxer;
+ // Invariant: (!demuxer && !stream) || stream->demuxer == demuxer
+ struct sh_stream *stream;
+
+ // NOTE: demuxer subtitles, i.e. if stream!=NULL, do not use the following
+ // fields. The data is stored in stream->sub this case.
+
+ // External text subtitle using libass subtitle renderer.
+ struct ass_track *ass_track;
+ bool native_ass_track;
+
+ // External text subtitle using non-libass subtitle renderer.
+ struct sub_data *subdata;
+
+ // External image subtitle (data is in vo_vobsub). 0 if not set.
+ int vobsub_id_plus_one;
+};
+
typedef struct MPContext {
struct MPOpts opts;
struct m_config *mconfig;
@@ -83,7 +124,7 @@ typedef struct MPContext {
struct osd_state *osd;
struct mp_osd_msg *osd_msg_stack;
char *terminal_osd_text;
- struct sub_data *subdata; // current sub_data style subtitles if any
+ subtitle subs; // subtitle list used when reading subtitles from demuxer
bool add_osd_seek_info;
unsigned int osd_visible;
@@ -99,6 +140,7 @@ typedef struct MPContext {
struct demuxer **sources;
int num_sources;
+
struct timeline_part *timeline;
int num_timeline_parts;
int timeline_part;
@@ -110,11 +152,17 @@ typedef struct MPContext {
struct stream *stream;
struct demuxer *demuxer;
- struct sh_audio *sh_audio;
- struct sh_video *sh_video;
- struct demux_stream *d_audio;
- struct demux_stream *d_video;
- struct demux_stream *d_sub;
+
+ struct track **tracks;
+ int num_tracks;
+
+ // Selected tracks. NULL if no track selected.
+ struct track *current_track[STREAM_TYPE_COUNT];
+
+ struct sh_stream *sh[STREAM_TYPE_COUNT];
+ struct sh_audio *sh_audio; // same as sh[STREAM_AUDIO]->audio
+ struct sh_video *sh_video; // same as sh[STREAM_VIDEO]->video
+ struct sh_sub *sh_sub; // same as sh[STREAM_SUB]->sub
// Uses: accessing metadata (consider ordered chapters case, where the main
// demuxer defines metadata), or special purpose demuxers like TV.
@@ -185,16 +233,6 @@ typedef struct MPContext {
float begin_skip; ///< start time of the current skip while on edlout mode
- int global_sub_size; // this encompasses all subtitle sources
- int global_sub_pos; // this encompasses all subtitle sources
- int set_of_sub_pos;
- int set_of_sub_size;
- int sub_counts[SUB_SOURCES];
- // set_of_ass_tracks[i] contains subtitles from set_of_subtitles[i]
- // parsed by libass or NULL if format unsupported
- struct ass_track *set_of_ass_tracks[MAX_SUBTITLE_FILES];
- sub_data* set_of_subtitles[MAX_SUBTITLE_FILES];
- bool track_was_native_ass[MAX_SUBTITLE_FILES];
struct ass_library *ass_library;
int file_format;
@@ -240,8 +278,10 @@ char *chapter_display_name(struct MPContext *mpctx, int chapter);
char *chapter_name(struct MPContext *mpctx, int chapter);
double chapter_start_time(struct MPContext *mpctx, int chapter);
int get_chapter_count(struct MPContext *mpctx);
-void update_subtitles(struct MPContext *mpctx, double refpts, bool reset);
-
+void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
+ struct track *track);
+struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
+ int tid);
// timeline/tl_matroska.c
void build_ordered_chapter_timeline(struct MPContext *mpctx);
diff --git a/mplayer.c b/mplayer.c
index 7edbeb533a..e7565adfc5 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -251,6 +251,8 @@ int use_filedir_conf;
#include "metadata.h"
+static void reset_subtitles(struct MPContext *mpctx);
+
static float get_relative_time(struct MPContext *mpctx)
{
unsigned int new_time = GetTimer();
@@ -382,34 +384,34 @@ char *get_metadata(struct MPContext *mpctx, metadata_t type)
return talloc_strdup(NULL, "");
}
-static void print_stream(struct MPContext *mpctx, struct sh_stream *s)
+static void print_stream(struct MPContext *mpctx, struct track *t, int id)
{
+ struct sh_stream *s = t->stream;
const char *tname = "?";
const char *selopt = "?";
const char *langopt = "?";
- switch (s->type) {
+ switch (t->type) {
case STREAM_VIDEO:
- tname = "video"; selopt = "vid"; langopt = "vlang";
+ tname = "Video"; selopt = "vid"; langopt = "vlang";
break;
case STREAM_AUDIO:
- tname = "audio"; selopt = "aid"; langopt = "alang";
+ tname = "Audio"; selopt = "aid"; langopt = "alang";
break;
case STREAM_SUB:
- tname = "subtitle"; selopt = "sid"; langopt = "slang";
+ tname = "Subs"; selopt = "sid"; langopt = "slang";
break;
}
- mp_msg(MSGT_CPLAYER, MSGL_INFO, "[stream] ID %d: %s", s->demuxer_id, tname);
- mp_msg(MSGT_CPLAYER, MSGL_INFO, " --%s=%d", selopt, s->tid);
- char *lang = demuxer_stream_lang(s->common_header->ds->demuxer, s);
- if (lang)
- mp_msg(MSGT_CPLAYER, MSGL_INFO, " --%s=%s", langopt, lang);
- talloc_free(lang);
- if (s->default_track)
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "[stream] %-5s %3s",
+ tname, mpctx->current_track[t->type] == t ? "(+)" : "");
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, " --%s=%d", selopt, t->user_tid);
+ if (t->lang)
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, " --%s=%s", langopt, t->lang);
+ if (t->default_track)
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (*)");
- if (s->title)
- mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", s->title);
+ if (t->title)
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", t->title);
mp_msg(MSGT_CPLAYER, MSGL_INFO, " (");
- if (s->common_header->format) {
+ if (s && s->common_header->format) {
int format = s->common_header->format;
// not sure about endian crap
char name[sizeof(format) + 1] = {0};
@@ -424,7 +426,7 @@ static void print_stream(struct MPContext *mpctx, struct sh_stream *s)
} else {
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%#x", format);
}
- } else if (s->type == STREAM_SUB) {
+ } else if (s && t->type == STREAM_SUB) {
char t = s->sub->type;
const char *name = NULL;
switch (t) {
@@ -436,9 +438,11 @@ static void print_stream(struct MPContext *mpctx, struct sh_stream *s)
name = (char[2]){t, '\0'};
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", name);
}
- if (s->common_header->demuxer_codecname)
+ if (s && s->common_header->demuxer_codecname)
mp_msg(MSGT_CPLAYER, MSGL_INFO, "/%s", s->common_header->demuxer_codecname);
mp_msg(MSGT_CPLAYER, MSGL_INFO, ")");
+ if (t->is_external)
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)");
mp_msg(MSGT_CPLAYER, MSGL_INFO, "\n");
}
@@ -466,7 +470,7 @@ static void print_file_properties(struct MPContext *mpctx, const char *filename)
"ID_VIDEO_FPS=%5.3f\n", mpctx->sh_video->fps);
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
"ID_VIDEO_ASPECT=%1.4f\n", mpctx->sh_video->aspect);
- video_start_pts = ds_get_next_pts(mpctx->d_video);
+ video_start_pts = ds_get_next_pts(mpctx->sh_video->ds);
}
if (mpctx->sh_audio) {
/* Assume FOURCC if all bytes >= 0x20 (' ') */
@@ -482,7 +486,7 @@ static void print_file_properties(struct MPContext *mpctx, const char *filename)
"ID_AUDIO_RATE=%d\n", mpctx->sh_audio->samplerate);
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
"ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels);
- start_pts = ds_get_next_pts(mpctx->d_audio);
+ start_pts = ds_get_next_pts(mpctx->sh_audio->ds);
}
if (video_start_pts != MP_NOPTS_VALUE) {
if (start_pts == MP_NOPTS_VALUE || !mpctx->sh_audio ||
@@ -512,21 +516,73 @@ static void print_file_properties(struct MPContext *mpctx, const char *filename)
}
}
}
- if (mpctx->master_demuxer) {
- for (int n = 0; n < mpctx->master_demuxer->num_streams; n++)
- print_stream(mpctx, mpctx->master_demuxer->streams[n]);
+ for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
+ for (int n = 0; n < mpctx->num_tracks; n++)
+ if (mpctx->tracks[n]->type == t)
+ print_stream(mpctx, mpctx->tracks[n], n);
}
}
/// step size of mixer changes
int volstep = 3;
+static void set_demux_field(struct MPContext *mpctx, enum stream_type type,
+ struct sh_stream *s)
+{
+ mpctx->sh[type] = s;
+ // redundant fields for convenience access
+ switch(type) {
+ case STREAM_VIDEO: mpctx->sh_video = s ? s->video : NULL; break;
+ case STREAM_AUDIO: mpctx->sh_audio = s ? s->audio : NULL; break;
+ case STREAM_SUB: mpctx->sh_sub = s ? s->sub : NULL; break;
+ }
+}
+
+static void init_demux_stream(struct MPContext *mpctx, enum stream_type type)
+{
+ struct track *track = mpctx->current_track[type];
+ set_demux_field(mpctx, type, track ? track->stream : NULL);
+ struct sh_stream *stream = mpctx->sh[type];
+ if (stream)
+ demuxer_switch_track(stream->demuxer, type, stream);
+}
+
+static void cleanup_demux_stream(struct MPContext *mpctx, enum stream_type type)
+{
+ struct sh_stream *stream = mpctx->sh[type];
+ if (stream)
+ demuxer_switch_track(stream->demuxer, type, NULL);
+ set_demux_field(mpctx, type, NULL);
+}
+
+// Switch the demuxers to current track selection. This is possibly important
+// for intialization: if something reads packets from the demuxer (like at least
+// reinit_audio_chain does, or when seeking), packets from the other streams
+// should be queued instead of discarded. So all streams should be enabled
+// before the first initialization function is called.
+static void preselect_demux_streams(struct MPContext *mpctx)
+{
+ // Disable all streams, just to be sure no unwanted streams are selected.
+ for (int n = 0; n < mpctx->num_sources; n++) {
+ for (int type = 0; type < STREAM_TYPE_COUNT; type++)
+ demuxer_switch_track(mpctx->sources[n], type, NULL);
+ }
+
+ for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
+ struct track *track = mpctx->current_track[type];
+ if (track && track->stream)
+ demuxer_switch_track(track->stream->demuxer, type, track->stream);
+ }
+}
+
static void uninit_subs(struct demuxer *demuxer)
{
for (int i = 0; i < MAX_S_STREAMS; i++) {
struct sh_sub *sh = demuxer->s_streams[i];
if (sh && sh->initialized)
sub_uninit(sh);
+ if (sh && is_av_sub(sh->type))
+ reset_avsub(sh);
}
}
@@ -540,37 +596,51 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
mpctx->initialized_flags &= ~INITIALIZED_ACODEC;
if (mpctx->sh_audio)
uninit_audio(mpctx->sh_audio);
- mpctx->sh_audio = NULL;
+ cleanup_demux_stream(mpctx, STREAM_AUDIO);
mpctx->mixer.afilter = NULL;
}
if (mask & INITIALIZED_SUB) {
mpctx->initialized_flags &= ~INITIALIZED_SUB;
- if (mpctx->d_sub->sh)
- sub_switchoff(mpctx->d_sub->sh, mpctx->osd);
+ if (mpctx->sh_sub)
+ sub_switchoff(mpctx->sh_sub, mpctx->osd);
+ cleanup_demux_stream(mpctx, STREAM_SUB);
+ reset_subtitles(mpctx);
}
if (mask & INITIALIZED_VCODEC) {
mpctx->initialized_flags &= ~INITIALIZED_VCODEC;
if (mpctx->sh_video)
uninit_video(mpctx->sh_video);
- mpctx->sh_video = NULL;
+ cleanup_demux_stream(mpctx, STREAM_VIDEO);
}
if (mask & INITIALIZED_DEMUXER) {
mpctx->initialized_flags &= ~INITIALIZED_DEMUXER;
+ for (int i = 0; i < mpctx->num_tracks; i++) {
+ struct track *track = mpctx->tracks[i];
+ sub_free(track->subdata);
+#ifdef CONFIG_ASS
+ if (track->ass_track)
+ ass_free_track(track->ass_track);
+#endif
+ talloc_free(track);
+ }
+ mpctx->num_tracks = 0;
+ for (int t = 0; t < STREAM_TYPE_COUNT; t++)
+ mpctx->current_track[t] = NULL;
+ assert(!mpctx->sh_video && !mpctx->sh_audio && !mpctx->sh_sub);
mpctx->master_demuxer = NULL;
- if (mpctx->num_sources) {
- mpctx->demuxer = mpctx->sources[0];
- for (int i = 1; i < mpctx->num_sources; i++) {
- struct demuxer *demuxer = mpctx->sources[i];
- uninit_subs(demuxer);
+ for (int i = 0; i < mpctx->num_sources; i++) {
+ uninit_subs(mpctx->sources[i]);
+ struct demuxer *demuxer = mpctx->sources[i];
+ if (demuxer->stream != mpctx->stream)
free_stream(demuxer->stream);
- free_demuxer(demuxer);
- }
+ free_demuxer(demuxer);
}
talloc_free(mpctx->sources);
mpctx->sources = NULL;
+ mpctx->demuxer = NULL;
mpctx->num_sources = 0;
talloc_free(mpctx->timeline);
mpctx->timeline = NULL;
@@ -579,12 +649,6 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
mpctx->chapters = NULL;
mpctx->num_chapters = 0;
mpctx->video_offset = 0;
- if (mpctx->demuxer) {
- mpctx->stream = mpctx->demuxer->stream;
- uninit_subs(mpctx->demuxer);
- free_demuxer(mpctx->demuxer);
- }
- mpctx->demuxer = NULL;
}
// kill the cache process:
@@ -861,6 +925,56 @@ static bool libmpdemux_was_interrupted(struct MPContext *mpctx)
|| mpctx->stop_play != AT_END_OF_FILE;
}
+static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
+{
+ int new_id = -1;
+ for (int i = 0; i < mpctx->num_tracks; i++) {
+ struct track *track = mpctx->tracks[i];
+ if (track->type == t)
+ new_id = FFMAX(new_id, track->user_tid);
+ }
+ return new_id + 1;
+}
+
+static struct track *add_stream_track(struct MPContext *mpctx,
+ struct sh_stream *stream,
+ bool under_timeline)
+{
+ for (int i = 0; i < mpctx->num_tracks; i++) {
+ struct track *track = mpctx->tracks[i];
+ if (track->stream == stream)
+ return track;
+ }
+ struct track *track = talloc_ptrtype(NULL, track);
+ *track = (struct track) {
+ .type = stream->type,
+ .user_tid = find_new_tid(mpctx, stream->type),
+ .demuxer_id = stream->demuxer_id,
+ .title = stream->title,
+ .default_track = stream->default_track,
+ .lang = stream->common_header->lang,
+ .under_timeline = under_timeline,
+ .demuxer = stream->demuxer,
+ .stream = stream,
+ };
+ MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
+
+ // Needed for DVD and Blu-Ray. (Note that at least with DVDs and demux_lavf,
+ // this code is broken: unlike demux_mpg, the demuxer streams are not
+ // directly mapped to MPEG stream IDs.)
+ if (!track->lang)
+ track->lang = talloc_steal(track, demuxer_stream_lang(track->demuxer,
+ track->stream));
+
+ return track;
+}
+
+static void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer)
+{
+ for (int n = 0; n < demuxer->num_streams; n++)
+ add_stream_track(mpctx, demuxer->streams[n], !!mpctx->timeline);
+}
+
void add_subtitles(struct MPContext *mpctx, char *filename, float fps,
int noerr)
{
@@ -869,7 +983,7 @@ void add_subtitles(struct MPContext *mpctx, char *filename, float fps,
struct ass_track *asst = NULL;
bool is_native_ass = false;
- if (filename == NULL || mpctx->set_of_sub_size >= MAX_SUBTITLE_FILES)
+ if (filename == NULL)
return;
#ifdef CONFIG_ASS
@@ -895,23 +1009,23 @@ void add_subtitles(struct MPContext *mpctx, char *filename, float fps,
return;
}
- mpctx->set_of_ass_tracks[mpctx->set_of_sub_size] = asst;
- mpctx->set_of_subtitles[mpctx->set_of_sub_size] = subd;
- mpctx->track_was_native_ass[mpctx->set_of_sub_size] = is_native_ass;
- mp_msg(MSGT_IDENTIFY, MSGL_INFO,
- "ID_FILE_SUB_ID=%d\n", mpctx->set_of_sub_size);
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILE_SUB_FILENAME=%s\n", filename);
- ++mpctx->set_of_sub_size;
- mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "SUB: Added subtitle file (%d): %s\n",
- mpctx->set_of_sub_size, filename);
+ struct track *track = talloc_ptrtype(NULL, track);
+ *track = (struct track) {
+ .type = STREAM_SUB,
+ .user_tid = find_new_tid(mpctx, STREAM_SUB),
+ .demuxer_id = -1,
+ .is_external = true,
+ .ass_track = asst,
+ .native_ass_track = is_native_ass,
+ .subdata = subd,
+ };
+ MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
}
void init_vo_spudec(struct MPContext *mpctx)
{
+ uninit_player(mpctx, INITIALIZED_SPUDEC);
unsigned width, height;
- spudec_free(vo_spudec);
- mpctx->initialized_flags &= ~INITIALIZED_SPUDEC;
- vo_spudec = NULL;
// we currently can't work without video stream
if (!mpctx->sh_video)
@@ -934,8 +1048,8 @@ void init_vo_spudec(struct MPContext *mpctx)
}
#endif
- if (vo_spudec == NULL) {
- sh_sub_t *sh = mpctx->d_sub->sh;
+ if (vo_spudec == NULL && mpctx->sh_sub) {
+ sh_sub_t *sh = mpctx->sh_sub;
vo_spudec = spudec_new_scaled(NULL, width, height, sh->extradata,
sh->extradata_len);
spudec_set_font_factor(vo_spudec, font_factor);
@@ -1444,9 +1558,10 @@ void reinit_audio_chain(struct MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
struct ao *ao;
+ init_demux_stream(mpctx, STREAM_AUDIO);
if (!mpctx->sh_audio) {
uninit_player(mpctx, INITIALIZED_AO);
- return;
+ goto no_audio;
}
if (!(mpctx->initialized_flags & INITIALIZED_ACODEC)) {
if (!init_best_audio_codec(mpctx->sh_audio, audio_codec_list, audio_fm_list))
@@ -1509,8 +1624,10 @@ void reinit_audio_chain(struct MPContext *mpctx)
init_error:
uninit_player(mpctx, INITIALIZED_ACODEC | INITIALIZED_AO);
- mpctx->sh_audio = mpctx->d_audio->sh = NULL; // -> nosound
- mpctx->d_audio->id = -2;
+ cleanup_demux_stream(mpctx, STREAM_AUDIO);
+no_audio:
+ mpctx->current_track[STREAM_AUDIO] = NULL;
+ mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Audio: no audio\n");
}
@@ -1521,7 +1638,7 @@ static double written_audio_pts(struct MPContext *mpctx)
sh_audio_t *sh_audio = mpctx->sh_audio;
if (!sh_audio)
return MP_NOPTS_VALUE;
- demux_stream_t *d_audio = mpctx->d_audio;
+ demux_stream_t *d_audio = mpctx->sh_audio->ds;
// first calculate the end pts of audio that has been output by decoder
double a_pts = sh_audio->pts;
if (a_pts != MP_NOPTS_VALUE)
@@ -1580,54 +1697,58 @@ double playing_audio_pts(struct MPContext *mpctx)
return pts - mpctx->opts.playback_speed *ao_get_delay(mpctx->ao);
}
-static bool is_av_sub(int type)
+static void reset_subtitles(struct MPContext *mpctx)
{
- return type == 'b' || type == 'p' || type == 'x';
+ struct sh_sub *sh_sub = mpctx->sh_sub;
+ int type = sh_sub ? sh_sub->type : '\0';
+
+ if (sh_sub)
+ sub_reset(sh_sub, mpctx->osd);
+ sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
+ if (vo_sub)
+ set_osd_subtitle(mpctx, NULL);
+ if (vo_spudec) {
+ spudec_reset(vo_spudec);
+ vo_osd_changed(OSDTYPE_SPU);
+ }
+ if (sh_sub && is_av_sub(type))
+ reset_avsub(sh_sub);
}
-void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset)
+static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
{
mpctx->osd->sub_offset = mpctx->video_offset;
struct MPOpts *opts = &mpctx->opts;
struct sh_video *sh_video = mpctx->sh_video;
- struct demux_stream *d_sub = mpctx->d_sub;
+ struct sh_sub *sh_sub = mpctx->sh_sub;
+ struct demux_stream *d_sub = sh_sub ? sh_sub->ds : NULL;
double refpts_s = refpts_tl - mpctx->osd->sub_offset;
double curpts_s = refpts_s + sub_delay;
unsigned char *packet = NULL;
int len;
- struct sh_sub *sh_sub = d_sub->sh;
- int type = sh_sub ? sh_sub->type : 'v';
- static subtitle subs;
- if (reset) {
- if (sh_sub)
- sub_reset(sh_sub, mpctx->osd);
- sub_clear_text(&subs, MP_NOPTS_VALUE);
- if (vo_sub)
- set_osd_subtitle(mpctx, NULL);
- if (vo_spudec) {
- spudec_reset(vo_spudec);
- vo_osd_changed(OSDTYPE_SPU);
- }
- if (is_av_sub(type))
- reset_avsub(sh_sub);
+ int type = sh_sub ? sh_sub->type : '\0';
+
+ struct track *track = mpctx->current_track[STREAM_SUB];
+ if (!track)
return;
- }
+
// find sub
- if (mpctx->subdata) {
+ if (track->subdata) {
if (sub_fps == 0)
sub_fps = sh_video ? sh_video->fps : 25;
- find_sub(mpctx, mpctx->subdata, curpts_s *
- (mpctx->subdata->sub_uses_time ? 100. : sub_fps));
+ find_sub(mpctx, track->subdata, curpts_s *
+ (track->subdata->sub_uses_time ? 100. : sub_fps));
}
// DVD sub:
- if (vobsub_id >= 0 || type == 'v') {
+ if (track->vobsub_id_plus_one || type == 'v') {
int timestamp;
- /* Get a sub packet from the DVD or a vobsub */
+ // Get a sub packet from the demuxer (or the vobsub.c thing, which
+ // should be a demuxer, but isn't).
while (1) {
// Vobsub
len = 0;
- if (vo_vobsub) {
+ if (track->vobsub_id_plus_one) {
if (curpts_s >= 0) {
len = vobsub_get_packet(vo_vobsub, curpts_s,
(void **)&packet, &timestamp);
@@ -1639,6 +1760,7 @@ void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset)
}
} else {
// DVD sub
+ assert(d_sub->sh == sh_sub);
len = ds_get_packet_sub(d_sub, (unsigned char **)&packet);
if (len > 0) {
// XXX This is wrong, sh_video->pts can be arbitrarily
@@ -1664,10 +1786,10 @@ void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset)
// PGS subtitles.
if (!vo_spudec)
vo_spudec = spudec_new(NULL);
- if (vo_vobsub || timestamp >= 0)
+ if (track->vobsub_id_plus_one || timestamp >= 0)
spudec_assemble(vo_spudec, packet, len, timestamp);
}
- } else if (is_text_sub(type) || is_av_sub(type)) {
+ } else if (d_sub && (is_text_sub(type) || is_av_sub(type))) {
if (d_sub->non_interleaved)
ds_get_next_pts(d_sub);
@@ -1702,7 +1824,7 @@ void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset)
}
if (subpts_s != MP_NOPTS_VALUE) {
if (duration < 0)
- sub_clear_text(&subs, MP_NOPTS_VALUE);
+ sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE);
if (type == 'a') { // ssa/ass subs without libass => convert to plaintext
int i;
unsigned char *p = packet;
@@ -1717,15 +1839,15 @@ void update_subtitles(struct MPContext *mpctx, double refpts_tl, bool reset)
double endpts_s = MP_NOPTS_VALUE;
if (subpts_s != MP_NOPTS_VALUE && duration >= 0)
endpts_s = subpts_s + duration;
- sub_add_text(&subs, packet, len, endpts_s);
- set_osd_subtitle(mpctx, &subs);
+ sub_add_text(&mpctx->subs, packet, len, endpts_s);
+ set_osd_subtitle(mpctx, &mpctx->subs);
}
if (d_sub->non_interleaved)
ds_get_next_pts(d_sub);
}
if (!opts->ass_enabled)
- if (sub_clear_text(&subs, curpts_s))
- set_osd_subtitle(mpctx, &subs);
+ if (sub_clear_text(&mpctx->subs, curpts_s))
+ set_osd_subtitle(mpctx, &mpctx->subs);
}
if (vo_spudec) {
spudec_heartbeat(vo_spudec, 90000 * curpts_s);
@@ -1738,7 +1860,7 @@ static int check_framedrop(struct MPContext *mpctx, double frame_time)
{
struct MPOpts *opts = &mpctx->opts;
// check for frame-drop:
- if (mpctx->sh_audio && !mpctx->ao->untimed && !mpctx->d_audio->eof) {
+ if (mpctx->sh_audio && !mpctx->ao->untimed && !mpctx->sh_audio->ds->eof) {
static int dropped_frames;
float delay = opts->playback_speed * ao_get_delay(mpctx->ao);
float d = delay - mpctx->delay;
@@ -1775,47 +1897,87 @@ static float timing_sleep(struct MPContext *mpctx, float time_frame)
return time_frame;
}
-static int select_subtitle(MPContext *mpctx)
+static void reinit_subs(struct MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
- // find the best sub to use
- int id;
- int found = 0;
- mpctx->global_sub_pos = -1; // no subs by default
- if (vobsub_id >= 0) {
- // if user asks for a vobsub id, use that first.
- id = vobsub_id;
- found = mp_property_do("sub_vob", M_PROPERTY_SET, &id, mpctx) ==
- M_PROPERTY_OK;
+ struct track *track = mpctx->current_track[STREAM_SUB];
+
+ assert(!(mpctx->initialized_flags & INITIALIZED_SUB));
+
+ init_demux_stream(mpctx, STREAM_SUB);
+
+ mpctx->osd->ass_track = NULL;
+ vobsub_id = -1;
+
+ if (!track)
+ return;
+
+ mpctx->initialized_flags |= INITIALIZED_SUB;
+
+ if (track->vobsub_id_plus_one) {
+ vobsub_id = track->vobsub_id_plus_one - 1;
+ } else if (track->subdata || track->ass_track) {
+#ifdef CONFIG_ASS
+ if (opts->ass_enabled && track->ass_track) {
+ mpctx->osd->ass_track = track->ass_track;
+ mpctx->osd->ass_track_changed = true;
+ mpctx->osd->vsfilter_aspect = track->native_ass_track;
+ } else
+#endif
+ {
+ vo_osd_changed(OSDTYPE_SUBTITLE);
+ }
+ } else if (track->stream) {
+ if (mpctx->sh_sub->type == 'v')
+ init_vo_spudec(mpctx);
+ else {
+ sub_init(mpctx->sh_sub, mpctx->osd);
+ }
}
+}
+
+void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
+ struct track *track)
+{
+ assert(!track || track->type == type);
+
+ struct track *current = mpctx->current_track[type];
+ if (track == current)
+ return;
- if (!found && opts->sub_id >= 0) {
- // if user asks for a dvd sub id, use that next.
- id = opts->sub_id;
- found = mp_property_do("sub_demux", M_PROPERTY_SET, &id, mpctx) ==
- M_PROPERTY_OK;
+ if (type == STREAM_VIDEO) {
+ uninit_player(mpctx, INITIALIZED_VCODEC |
+ (mpctx->opts.fixed_vo && track ? 0 : INITIALIZED_VO));
+ } else if (type == STREAM_AUDIO) {
+ uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
+ } else if (type == STREAM_SUB) {
+ uninit_player(mpctx, INITIALIZED_SUB);
}
- if (!found) {
- // if there are text subs to use, use those. (autosubs come last here)
- id = 0;
- found = mp_property_do("sub_file", M_PROPERTY_SET, &id, mpctx) ==
- M_PROPERTY_OK;
+ mpctx->current_track[type] = track;
+
+ int user_tid = track ? track->user_tid : -2;
+ if (type == STREAM_VIDEO) {
+ mpctx->opts.video_id = user_tid;
+ reinit_video_chain(mpctx);
+ } else if (type == STREAM_AUDIO) {
+ mpctx->opts.audio_id = user_tid;
+ reinit_audio_chain(mpctx);
+ } else if (type == STREAM_SUB) {
+ mpctx->opts.sub_id = user_tid;
+ reinit_subs(mpctx);
}
+}
- if (!found && opts->sub_id == -1) {
- // finally select subs by language and container hints
- if (opts->sub_id == -1)
- opts->sub_id =
- demuxer_sub_track_by_lang_and_default(mpctx->d_sub->demuxer,
- opts->sub_lang);
- if (opts->sub_id >= 0) {
- id = opts->sub_id;
- found = mp_property_do("sub_demux", M_PROPERTY_SET, &id, mpctx) ==
- M_PROPERTY_OK;
- }
+struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
+ int tid)
+{
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (track->type == type && track->user_tid == tid)
+ return track;
}
- return found;
+ return NULL;
}
/* Modify video timing to match the audio timeline. There are two main
@@ -2007,7 +2169,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
return -1;
} else if (res == ASYNC_PLAY_DONE)
return 0;
- else if (mpctx->d_audio->eof)
+ else if (mpctx->sh_audio->ds->eof)
audio_eof = true;
}
t = GetTimer() - t;
@@ -2065,16 +2227,38 @@ static void vo_update_window_title(struct MPContext *mpctx)
int reinit_video_chain(struct MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
+ assert(!(mpctx->initialized_flags & INITIALIZED_VCODEC));
+ init_demux_stream(mpctx, STREAM_VIDEO);
sh_video_t * const sh_video = mpctx->sh_video;
if (!sh_video) {
uninit_player(mpctx, INITIALIZED_VO);
- return 0;
+ goto no_video;
}
+
+ if (!video_read_properties(mpctx->sh_video)) {
+ mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Video: Cannot read properties.\n");
+ goto err_out;
+ } else {
+ mp_tmsg(MSGT_CPLAYER, MSGL_V, "[V] filefmt:%d fourcc:0x%X "
+ "size:%dx%d fps:%5.3f ftime:=%6.4f\n",
+ mpctx->master_demuxer->file_format, mpctx->sh_video->format,
+ mpctx->sh_video->disp_w, mpctx->sh_video->disp_h,
+ mpctx->sh_video->fps, mpctx->sh_video->frametime);
+ if (force_fps) {
+ mpctx->sh_video->fps = force_fps;
+ mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
+ }
+ vo_fps = mpctx->sh_video->fps;
+
+ if (!mpctx->sh_video->fps && !force_fps && !opts->correct_pts) {
+ mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "FPS not specified in the "
+ "header or invalid, use the -fps option.\n");
+ }
+ }
+
double ar = -1.0;
//================== Init VIDEO (codec & libvo) ==========================
if (!opts->fixed_vo || !(mpctx->initialized_flags & INITIALIZED_VO)) {
- //shouldn't we set dvideo->id=-2 when we fail?
- //if((mpctx->video_out->preinit(vo_subdevice))!=0){
if (!(mpctx->video_out = init_best_video_out(opts, mpctx->key_fifo,
mpctx->input))) {
mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "Error opening/initializing "
@@ -2086,8 +2270,7 @@ int reinit_video_chain(struct MPContext *mpctx)
vo_update_window_title(mpctx);
- assert(mpctx->sh_video == mpctx->d_video->sh);
- if (stream_control(mpctx->d_video->demuxer->stream,
+ if (stream_control(mpctx->sh_video->ds->demuxer->stream,
STREAM_CTRL_GET_ASPECT_RATIO, &ar) != STREAM_UNSUPPORTED)
mpctx->sh_video->stream_aspect = ar;
@@ -2142,11 +2325,8 @@ int reinit_video_chain(struct MPContext *mpctx)
init_best_video_codec(sh_video, video_codec_list, video_fm_list);
- if (!sh_video->initialized) {
- if (!opts->fixed_vo)
- uninit_player(mpctx, INITIALIZED_VO);
+ if (!sh_video->initialized)
goto err_out;
- }
mpctx->initialized_flags |= INITIALIZED_VCODEC;
@@ -2165,7 +2345,12 @@ int reinit_video_chain(struct MPContext *mpctx)
return 1;
err_out:
- mpctx->sh_video = mpctx->d_video->sh = NULL;
+ if (!opts->fixed_vo)
+ uninit_player(mpctx, INITIALIZED_VO);
+ cleanup_demux_stream(mpctx, STREAM_VIDEO);
+no_video:
+ mpctx->current_track[STREAM_VIDEO] = NULL;
+ mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Video: no video\n");
return 0;
}
@@ -2218,7 +2403,7 @@ static void determine_frame_pts(struct MPContext *mpctx)
if (opts->user_pts_assoc_mode)
sh_video->pts_assoc_mode = opts->user_pts_assoc_mode;
else if (sh_video->pts_assoc_mode == 0) {
- if (mpctx->d_video->demuxer->timestamp_type == TIMESTAMP_TYPE_PTS
+ if (mpctx->sh_video->ds->demuxer->timestamp_type == TIMESTAMP_TYPE_PTS
&& sh_video->codec_reordered_pts != MP_NOPTS_VALUE)
sh_video->pts_assoc_mode = 1;
else
@@ -2264,7 +2449,7 @@ static double update_video(struct MPContext *mpctx)
pts = MP_NOPTS_VALUE;
struct demux_packet *pkt;
while (1) {
- pkt = ds_get_packet2(mpctx->d_video, false);
+ pkt = ds_get_packet2(mpctx->sh_video->ds, false);
if (!pkt || pkt->len)
break;
/* Packets with size 0 are assumed to not correspond to frames,
@@ -2414,9 +2599,8 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
// Not all demuxers set d_video->pts during seek, so this value
// (which is used by at least vobsub code below) may be completely
// wrong (probably 0).
- mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset;
+ mpctx->sh_video->pts = mpctx->sh_video->ds->pts + mpctx->video_offset;
mpctx->video_pts = mpctx->sh_video->pts;
- update_subtitles(mpctx, mpctx->sh_video->pts, true);
}
if (mpctx->sh_audio && reset_ac) {
@@ -2425,10 +2609,10 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
ao_reset(mpctx->ao);
mpctx->ao->buffer.len = mpctx->ao->buffer_playable_size;
mpctx->sh_audio->a_buffer_len = 0;
- if (!mpctx->sh_video)
- update_subtitles(mpctx, mpctx->sh_audio->pts, true);
}
+ reset_subtitles(mpctx);
+
if (vo_vobsub && mpctx->sh_video) {
vobsub_seek(vo_vobsub, mpctx->sh_video->pts);
}
@@ -2440,26 +2624,36 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac)
drop_frame_cnt = 0;
}
-static bool timeline_set_part(struct MPContext *mpctx, int i)
+static bool timeline_set_part(struct MPContext *mpctx, int i, bool force)
{
struct timeline_part *p = mpctx->timeline + mpctx->timeline_part;
struct timeline_part *n = mpctx->timeline + i;
mpctx->timeline_part = i;
mpctx->video_offset = n->start - n->source_start;
- if (n->source == p->source)
+ if (n->source == p->source && !force)
return false;
enum stop_play_reason orig_stop_play = mpctx->stop_play;
if (!mpctx->sh_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);
mpctx->stop_play = orig_stop_play;
+
mpctx->demuxer = n->source;
- mpctx->d_video = mpctx->demuxer->video;
- mpctx->d_audio = mpctx->demuxer->audio;
- mpctx->d_sub = mpctx->demuxer->sub;
- mpctx->sh_video = mpctx->d_video->sh;
- mpctx->sh_audio = mpctx->d_audio->sh;
mpctx->stream = mpctx->demuxer->stream;
+
+ // While another timeline was active, the selection of active tracks might
+ // have been changed - possibly we need to update this source.
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (track->under_timeline) {
+ track->demuxer = mpctx->demuxer;
+ track->stream = demuxer_stream_by_demuxer_id(track->demuxer,
+ track->type,
+ track->demuxer_id);
+ }
+ }
+ preselect_demux_streams(mpctx);
+
return true;
}
@@ -2473,7 +2667,7 @@ static double timeline_set_from_time(struct MPContext *mpctx, double pts,
for (int i = 0; i < mpctx->num_timeline_parts; i++) {
struct timeline_part *p = mpctx->timeline + i;
if (pts < (p + 1)->start) {
- *need_reset = timeline_set_part(mpctx, i);
+ *need_reset = timeline_set_part(mpctx, i, false);
return pts - p->start + p->source_start;
}
}
@@ -2521,6 +2715,7 @@ static int seek(MPContext *mpctx, struct seek_params seek,
demuxer_amount = timeline_set_from_time(mpctx, seek.amount,
&need_reset);
if (demuxer_amount == -1) {
+ assert(!need_reset);
mpctx->stop_play = AT_END_OF_FILE;
// Clear audio from current position
if (mpctx->sh_audio && !timeline_fallthrough) {
@@ -2532,8 +2727,7 @@ static int seek(MPContext *mpctx, struct seek_params seek,
}
if (need_reset) {
reinit_video_chain(mpctx);
- mp_property_do("sub", M_PROPERTY_SET, &(int){mpctx->global_sub_pos},
- mpctx);
+ reinit_subs(mpctx);
}
int demuxer_style = 0;
@@ -2635,8 +2829,8 @@ double get_time_length(struct MPContext *mpctx)
(void *) &get_time_ans) > 0)
return get_time_ans;
- struct sh_video *sh_video = mpctx->d_video->sh;
- struct sh_audio *sh_audio = mpctx->d_audio->sh;
+ struct sh_video *sh_video = mpctx->sh_video;
+ struct sh_audio *sh_audio = mpctx->sh_audio;
if (sh_video && sh_video->i_bps && sh_audio && sh_audio->i_bps)
return (double) (demuxer->movi_end - demuxer->movi_start) /
(sh_video->i_bps + sh_audio->i_bps);
@@ -2802,6 +2996,10 @@ static void run_playloop(struct MPContext *mpctx)
double sleeptime = WAKEUP_PERIOD;
bool was_restart = mpctx->restart_playback;
+ // Add tracks that were added by the demuxer later (e.g. MPEG)
+ if (!mpctx->timeline && mpctx->demuxer)
+ add_demuxer_tracks(mpctx, mpctx->demuxer);
+
if (mpctx->timeline) {
double end = mpctx->timeline[mpctx->timeline_part + 1].start;
if (endpts == MP_NOPTS_VALUE || end < endpts) {
@@ -2816,9 +3014,14 @@ static void run_playloop(struct MPContext *mpctx)
mpctx->stop_play = PT_NEXT_ENTRY;
}
- if (!mpctx->sh_audio && mpctx->d_audio->sh) {
- mpctx->sh_audio = mpctx->d_audio->sh;
- mpctx->sh_audio->ds = mpctx->d_audio;
+ // Possibly needed for stream auto selection in demux_lavf (?)
+ if (!mpctx->sh_audio && mpctx->master_demuxer->audio->sh) {
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ if (mpctx->tracks[n]->stream == ds_gsh(mpctx->master_demuxer->audio)) {
+ mpctx->current_track[STREAM_AUDIO] = mpctx->tracks[n];
+ break;
+ }
+ }
reinit_audio_chain(mpctx);
}
@@ -2930,7 +3133,7 @@ static void run_playloop(struct MPContext *mpctx)
vo_new_frame_imminent(vo);
struct sh_video *sh_video = mpctx->sh_video;
mpctx->video_pts = sh_video->pts;
- update_subtitles(mpctx, sh_video->pts, false);
+ update_subtitles(mpctx, sh_video->pts);
update_osd_msg(mpctx);
struct vf_instance *vf = sh_video->vfilter;
mpctx->osd->pts = mpctx->video_pts - mpctx->osd->sub_offset;
@@ -3029,7 +3232,7 @@ static void run_playloop(struct MPContext *mpctx)
print_status(mpctx, a_pos, false);
if (!mpctx->sh_video)
- update_subtitles(mpctx, a_pos, false);
+ update_subtitles(mpctx, a_pos);
}
/* It's possible for the user to simultaneously switch both audio
@@ -3165,17 +3368,64 @@ static bool attachment_is_font(struct demux_attachment *att)
return false;
}
-static int select_audio(demuxer_t *demuxer, int audio_id, char **audio_lang)
+// Result numerically higher => better match. 0 == no match.
+static int match_lang(char **langs, char *lang)
{
- if (audio_id == -1)
- audio_id = demuxer_audio_track_by_lang_and_default(demuxer, audio_lang);
- if (audio_id != -1) // -1 (automatic) is the default behaviour of demuxers
- demuxer_switch_audio(demuxer, audio_id);
- if (audio_id == -2) { // some demuxers don't yet know how to switch to no sound
- demuxer->audio->id = -2;
- demuxer->audio->sh = NULL;
+ for (int idx = 0; langs && langs[idx]; idx++) {
+ if (lang && strcmp(langs[idx], lang) == 0)
+ return INT_MAX - idx;
}
- return demuxer->audio->id;
+ return 0;
+}
+
+/* Get the track wanted by the user.
+ * tid is the track ID requested by the user (-2: deselect, -1: default)
+ * lang is a string list, NULL is same as empty list
+ * Sort tracks based on the following criteria, and pick the first:
+ * 0) track matches tid (always wins)
+ * 1) track is external
+ * 1.5) track is external and vobsub and has higher vobsub ID
+ * 2) earlier match in lang list
+ * 3) track is marked default
+ * 4) lower track number
+ * If select_fallback is not set, 4) is only used to determine whether a
+ * matching track is preferred over another track. Otherwise, always pick a
+ * track (if nothing else matches, return the track with lowest ID).
+ */
+// Return whether t1 is preferred over t2
+static bool compare_track(struct track *t1, struct track *t2, char **langs)
+{
+ if (t1->is_external != t2->is_external)
+ return t1->is_external;
+ int l1 = match_lang(langs, t1->lang), l2 = match_lang(langs, t2->lang);
+ if (l1 != l2)
+ return l1 > l2;
+ if (t1->default_track != t2->default_track)
+ return t1->default_track;
+ if (t1->vobsub_id_plus_one != t2->vobsub_id_plus_one)
+ return t1->vobsub_id_plus_one >= t2->vobsub_id_plus_one;
+ return t1->user_tid <= t2->user_tid;
+}
+static struct track *select_track(struct MPContext *mpctx,
+ enum stream_type type, int tid, char **langs,
+ bool select_fallback)
+{
+ if (tid == -2)
+ return NULL;
+ struct track *pick = NULL;
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (track->type != type)
+ continue;
+ if (track->user_tid == tid)
+ return track;
+ if (!pick || compare_track(track, pick, langs))
+ pick = track;
+ }
+ if (pick && !select_fallback && !pick->is_external
+ && !match_lang(langs, pick->lang) && !pick->default_track)
+ pick = NULL;
+ return pick;
}
static void init_input(struct MPContext *mpctx)
@@ -3208,13 +3458,24 @@ static void open_vobsubs_from_options(struct MPContext *mpctx)
}
if (vo_vobsub) {
mpctx->initialized_flags |= INITIALIZED_VOBSUB;
+ // TODO: let frontend do the selection
vobsub_set_from_lang(vo_vobsub, mpctx->opts.sub_lang);
mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only,
mpctx);
- // setup global sub numbering
- mpctx->sub_counts[SUB_SOURCE_VOBSUB] =
- vobsub_get_indexes_count(vo_vobsub);
+ for (int i = 0; i < vobsub_get_indexes_count(vo_vobsub); i++) {
+ int id = vobsub_get_id_by_index(vo_vobsub, i);
+ struct track *track = talloc_ptrtype(NULL, track);
+ *track = (struct track) {
+ .type = STREAM_SUB,
+ .user_tid = find_new_tid(mpctx, STREAM_SUB),
+ .demuxer_id = -1,
+ .lang = talloc_strdup(track, vobsub_get_id(vo_vobsub, id)),
+ .is_external = true,
+ .vobsub_id_plus_one = id + 1,
+ };
+ MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
+ }
}
}
@@ -3235,8 +3496,6 @@ static void open_subtitles_from_options(struct MPContext *mpctx)
add_subtitles(mpctx, tmp[i], sub_fps, 1);
talloc_free(tmp);
}
- if (mpctx->set_of_sub_size > 0)
- mpctx->sub_counts[SUB_SOURCE_SUBS] = mpctx->set_of_sub_size;
}
static void print_timeline(struct MPContext *mpctx)
@@ -3303,10 +3562,6 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->stop_play = 0;
- // init global sub numbers
- mpctx->global_sub_size = 0;
- memset(mpctx->sub_counts, 0, sizeof(mpctx->sub_counts));
-
mpctx->filename = NULL;
if (mpctx->playlist->current)
mpctx->filename = mpctx->playlist->current->filename;
@@ -3346,7 +3601,7 @@ static void play_current_file(struct MPContext *mpctx)
#ifdef CONFIG_ASS
ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list);
#endif
- if (mpctx->video_out && mpctx->sh_video && mpctx->video_out->config_ok)
+ if (mpctx->video_out && mpctx->video_out->config_ok)
vo_control(mpctx->video_out, VOCTRL_RESUME, NULL);
mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Playing %s.\n", mpctx->filename);
@@ -3361,17 +3616,13 @@ static void play_current_file(struct MPContext *mpctx)
}
}
- open_vobsubs_from_options(mpctx);
-
//============ Open & Sync STREAM --- fork cache2 ====================
assert(mpctx->stream == NULL);
assert(mpctx->demuxer == NULL);
- assert(mpctx->d_audio == NULL);
- assert(mpctx->d_video == NULL);
- assert(mpctx->d_sub == NULL);
assert(mpctx->sh_audio == NULL);
assert(mpctx->sh_video == NULL);
+ assert(mpctx->sh_sub == NULL);
mpctx->stream = open_stream(mpctx->filename, opts, &mpctx->file_format);
if (!mpctx->stream) { // error...
@@ -3404,17 +3655,6 @@ static void play_current_file(struct MPContext *mpctx)
}
mpctx->stream->start_pos += seek_to_byte;
-#ifdef CONFIG_DVDREAD
- if (mpctx->stream->type == STREAMTYPE_DVD) {
- if (opts->audio_lang && opts->audio_id == -1)
- opts->audio_id = dvd_aid_from_lang(mpctx->stream, opts->audio_lang);
- if (opts->sub_lang && opts->sub_id == -1)
- opts->sub_id = dvd_sid_from_lang(mpctx->stream, opts->sub_lang);
- // setup global sub numbering
- mpctx->sub_counts[SUB_SOURCE_DEMUX] = dvd_number_of_subs(mpctx->stream);
- }
-#endif
-
// CACHE2: initial prefill: 20% later: 5% (should be set by -cacheopts)
goto_enable_cache:
if (stream_cache_size > 0) {
@@ -3450,10 +3690,6 @@ goto_enable_cache:
if (mpctx->demuxer->type == DEMUXER_TYPE_CUE)
build_cue_timeline(mpctx);
- if (mpctx->timeline) {
- mpctx->timeline_part = 0;
- mpctx->demuxer = mpctx->timeline[0].source;
- }
print_timeline(mpctx);
if (!mpctx->num_sources) {
@@ -3461,48 +3697,47 @@ goto_enable_cache:
mpctx->demuxer);
}
+ if (mpctx->timeline && !mpctx->demuxer->matroska_data.ordered_chapters) {
+ // With Matroska, the "master" file dictates track layout etc.
+ // On the contrary, the EDL and CUE demuxers are empty wrappers.
+ mpctx->demuxer = mpctx->timeline[0].source;
+ }
+ add_demuxer_tracks(mpctx, mpctx->demuxer);
+
+ mpctx->timeline_part = 0;
+ if (mpctx->timeline)
+ timeline_set_part(mpctx, mpctx->timeline_part, true);
+
mpctx->initialized_flags |= INITIALIZED_DEMUXER;
add_subtitle_fonts_from_sources(mpctx);
- mpctx->d_audio = mpctx->demuxer->audio;
- mpctx->d_video = mpctx->demuxer->video;
- mpctx->d_sub = mpctx->demuxer->sub;
+ open_subtitles_from_options(mpctx);
+ open_vobsubs_from_options(mpctx);
- // select audio stream
- for (int i = 0; i < mpctx->num_sources; i++)
- select_audio(mpctx->sources[i]->audio->demuxer, opts->audio_id,
- opts->audio_lang);
+ mpctx->current_track[STREAM_VIDEO] =
+ select_track(mpctx, STREAM_VIDEO, mpctx->opts.video_id, NULL, true);
+ mpctx->current_track[STREAM_AUDIO] =
+ select_track(mpctx, STREAM_AUDIO, mpctx->opts.audio_id,
+ mpctx->opts.audio_lang, true);
+ mpctx->current_track[STREAM_SUB] =
+ select_track(mpctx, STREAM_SUB, mpctx->opts.sub_id,
+ mpctx->opts.sub_lang, false);
- mpctx->sh_audio = mpctx->d_audio->sh;
- mpctx->sh_video = mpctx->d_video->sh;
+ demux_info_print(mpctx->master_demuxer);
+ print_file_properties(mpctx, mpctx->filename);
- if (mpctx->sh_video) {
- if (!video_read_properties(mpctx->sh_video)) {
- mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Video: Cannot read properties.\n");
- mpctx->sh_video = mpctx->d_video->sh = NULL;
- } else {
- mp_tmsg(MSGT_CPLAYER, MSGL_V, "[V] filefmt:%d fourcc:0x%X "
- "size:%dx%d fps:%5.3f ftime:=%6.4f\n",
- mpctx->master_demuxer->file_format, mpctx->sh_video->format,
- mpctx->sh_video->disp_w, mpctx->sh_video->disp_h,
- mpctx->sh_video->fps, mpctx->sh_video->frametime);
- if (force_fps) {
- mpctx->sh_video->fps = force_fps;
- mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
- }
- vo_fps = mpctx->sh_video->fps;
+ preselect_demux_streams(mpctx);
- if (!mpctx->sh_video->fps && !force_fps && !opts->correct_pts) {
- mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "FPS not specified in the "
- "header or invalid, use the -fps option.\n");
- }
- }
+ reinit_video_chain(mpctx);
+ reinit_audio_chain(mpctx);
+ reinit_subs(mpctx);
- }
+ //================== MAIN: ==========================
if (!mpctx->sh_video && !mpctx->sh_audio) {
- mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, "No stream found.\n");
+ mp_tmsg(MSGT_CPLAYER, MSGL_FATAL,
+ "No video or audio streams selected.\n");
#ifdef CONFIG_DVBIN
if (mpctx->stream->type == STREAMTYPE_DVB) {
int dir;
@@ -3521,27 +3756,6 @@ goto_enable_cache:
goto terminate_playback;
}
- /* display clip info */
- demux_info_print(mpctx->master_demuxer);
-
- //================= Read SUBTITLES (DVD & TEXT) =========================
- if (vo_spudec == NULL && (mpctx->stream->type == STREAMTYPE_DVD))
- init_vo_spudec(mpctx);
-
- open_subtitles_from_options(mpctx);
-
- select_subtitle(mpctx);
-
- print_file_properties(mpctx, mpctx->filename);
-
- reinit_video_chain(mpctx);
- if (mpctx->sh_video) {
- osd_font_invalidate();
- } else if (!mpctx->sh_audio)
- goto terminate_playback;
-
- //================== MAIN: ==========================
-
if (opts->playing_msg) {
char *msg = property_expand_string(mpctx, opts->playing_msg);
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", msg);
@@ -3557,13 +3771,6 @@ goto_enable_cache:
//================ SETUP STREAMS ==========================
- if (mpctx->sh_audio) {
- reinit_audio_chain(mpctx);
- if (mpctx->sh_audio && mpctx->sh_audio->codec)
- mp_msg(MSGT_IDENTIFY, MSGL_INFO,
- "ID_AUDIO_CODEC=%s\n", mpctx->sh_audio->codec->name);
- }
-
if (mpctx->sh_video) {
mpctx->sh_video->timer = 0;
if (!ignore_start)
@@ -3576,25 +3783,8 @@ goto_enable_cache:
audio_delay -= mpctx->sh_audio->stream_delay;
}
- if (!mpctx->sh_audio) {
- mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Audio: no sound\n");
- mp_msg(MSGT_CPLAYER, MSGL_V, "Freeing %d unused audio chunks.\n",
- mpctx->d_audio->packs);
- ds_free_packs(mpctx->d_audio); // free buffered chunks
- }
- if (!mpctx->sh_video) {
- mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "Video: no video\n");
- mp_msg(MSGT_CPLAYER, MSGL_V, "Freeing %d unused video chunks.\n",
- mpctx->d_video->packs);
- ds_free_packs(mpctx->d_video);
- mpctx->d_video->id = -2;
- }
-
- if (!mpctx->sh_video && !mpctx->sh_audio)
- goto terminate_playback;
-
if (force_fps && mpctx->sh_video) {
- vo_fps = mpctx->sh_video->fps = force_fps;
+ mpctx->sh_video->fps = force_fps;
mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
"FPS forced to be %5.3f (ftime: %5.3f).\n",
@@ -3697,18 +3887,7 @@ terminate_playback: // don't jump here after ao/vo/getch initialization!
mpctx->filename = NULL;
- if (mpctx->set_of_sub_size > 0) {
- for (int i = 0; i < mpctx->set_of_sub_size; ++i) {
- sub_free(mpctx->set_of_subtitles[i]);
-#ifdef CONFIG_ASS
- if (mpctx->set_of_ass_tracks[i])
- ass_free_track(mpctx->set_of_ass_tracks[i]);
-#endif
- }
- mpctx->set_of_sub_size = 0;
- }
vo_sub = NULL;
- mpctx->subdata = NULL;
#ifdef CONFIG_ASS
mpctx->osd->ass_track = NULL;
if (mpctx->ass_library)
@@ -3899,8 +4078,6 @@ int main(int argc, char *argv[])
*mpctx = (struct MPContext){
.osd_function = OSD_PLAY,
.begin_skip = MP_NOPTS_VALUE,
- .global_sub_pos = -1,
- .set_of_sub_pos = -1,
.file_format = DEMUXER_TYPE_UNKNOWN,
.last_dvb_step = 1,
.terminal_osd_text = talloc_strdup(mpctx, ""),
diff --git a/sub/av_sub.c b/sub/av_sub.c
index 56acfc63ca..dbf349133f 100644
--- a/sub/av_sub.c
+++ b/sub/av_sub.c
@@ -24,6 +24,11 @@
#include "spudec.h"
#include "av_sub.h"
+bool is_av_sub(int type)
+{
+ return type == 'b' || type == 'p' || type == 'x';
+}
+
void reset_avsub(struct sh_sub *sh)
{
if (sh->context) {
diff --git a/sub/av_sub.h b/sub/av_sub.h
index af3edc4d34..ee6205c87d 100644
--- a/sub/av_sub.h
+++ b/sub/av_sub.h
@@ -26,5 +26,6 @@ struct sh_sub;
void reset_avsub(struct sh_sub *sh);
int decode_avsub(struct sh_sub *sh, uint8_t *data, int size,
double pts, double endpts);
+bool is_av_sub(int type);
#endif /* MPLAYER_AV_SUB_H */
diff --git a/sub/sub.c b/sub/sub.c
index 4d4d91ea86..67413c936e 100644
--- a/sub/sub.c
+++ b/sub/sub.c
@@ -289,7 +289,8 @@ void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys,
if(obj->flags&OSDFLAG_VISIBLE){
switch(obj->type){
case OSDTYPE_SPU:
- vo_draw_spudec_sub(obj, draw_alpha, ctx); // FIXME
+ if (vo_spudec)
+ vo_draw_spudec_sub(obj, draw_alpha, ctx); // FIXME
break;
case OSDTYPE_OSD:
case OSDTYPE_SUBTITLE: