1
0
mirror of https://github.com/mpv-player/mpv synced 2025-04-08 10:32:51 +00:00

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.
This commit is contained in:
wm4 2012-08-19 18:01:30 +02:00
parent 13482fb397
commit 0f155921b0
12 changed files with 696 additions and 1019 deletions

650
command.c
View File

@ -105,86 +105,6 @@ static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
vo->dheight, vo_fs); 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, static int mp_property_generic_option(struct m_option *prop, int action,
void *arg, MPContext *mpctx) 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; return M_PROPERTY_NOT_IMPLEMENTED;
} }
/// Selected audio id (RW) static struct track* track_next(struct MPContext *mpctx, enum stream_type type,
static int mp_property_audio(m_option_t *prop, int action, void *arg, int direction, struct track *track)
MPContext *mpctx) {
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) if (!mpctx->num_sources)
return M_PROPERTY_UNAVAILABLE; return M_PROPERTY_UNAVAILABLE;
struct sh_audio *sh = mpctx->sh_audio; struct track *track = mpctx->current_track[type];
current_id = sh ? sh->aid : -2;
switch (action) { switch (action) {
case M_PROPERTY_GET: case M_PROPERTY_GET:
if (!arg) if (!arg)
return M_PROPERTY_ERROR; return M_PROPERTY_ERROR;
*(int *) arg = current_id; *(int *) arg = track ? track->user_tid : -1;
return M_PROPERTY_OK; return M_PROPERTY_OK;
case M_PROPERTY_PRINT: case M_PROPERTY_PRINT:
if (!arg) if (!arg)
return M_PROPERTY_ERROR; return M_PROPERTY_ERROR;
if (!sh || current_id < 0) if (!track)
*(char **) arg = talloc_strdup(NULL, mp_gtext("disabled")); *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
else { else {
char *lang = demuxer_stream_lang(sh->ds->demuxer, sh->gsh); char *lang = track->lang;
if (!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\")", *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
current_id, lang, sh->gsh->title); track->user_tid, lang, track->title);
else else
*(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id, *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
lang); track->user_tid, lang);
talloc_free(lang);
} }
return M_PROPERTY_OK; return M_PROPERTY_OK;
case M_PROPERTY_STEP_UP: 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) if (action == M_PROPERTY_SET && arg)
tmp = *((int *) arg); track = mp_track_by_tid(mpctx, type, i);
else else
tmp = -1; track = track_next(mpctx, type, i > 0 ? +1 : -1, track);
int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp); mp_switch_track(mpctx, type, track);
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);
return M_PROPERTY_OK; return M_PROPERTY_OK;
}
default: default:
return M_PROPERTY_NOT_IMPLEMENTED; 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) /// Selected video id (RW)
static int mp_property_video(m_option_t *prop, int action, void *arg, static int mp_property_video(m_option_t *prop, int action, void *arg,
MPContext *mpctx) MPContext *mpctx)
{ {
struct MPOpts *opts = &mpctx->opts; return property_switch_track(prop, action, arg, mpctx, STREAM_VIDEO);
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;
}
} }
static int mp_property_program(m_option_t *prop, int action, void *arg, 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 #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); int l = strlen(prop->name);
char tv_prop[3 + l + 1]; char tv_prop[3 + l + 1];
sprintf(tv_prop, "tv_%s", prop->name); 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, static int mp_property_sub(m_option_t *prop, int action, void *arg,
MPContext *mpctx) MPContext *mpctx)
{ {
struct MPOpts *opts = &mpctx->opts; return property_switch_track(prop, action, arg, mpctx, STREAM_SUB);
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);
} }
/// Subtitle delay (RW) /// Subtitle delay (RW)
@ -1859,8 +1407,7 @@ static int mp_property_sub_alignment(m_option_t *prop, int action,
_("top"), _("center"), _("bottom") _("top"), _("center"), _("bottom")
}; };
if (!mpctx->sh_video || mpctx->global_sub_pos < 0 if (!mpctx->current_track[STREAM_SUB])
|| sub_source(mpctx) != SUB_SOURCE_SUBS)
return M_PROPERTY_UNAVAILABLE; return M_PROPERTY_UNAVAILABLE;
switch (action) { switch (action) {
@ -2183,14 +1730,6 @@ static const m_option_t mp_properties[] = {
// Subs // Subs
{ "sub", mp_property_sub, CONF_TYPE_INT, { "sub", mp_property_sub, CONF_TYPE_INT,
M_OPT_MIN, -1, 0, NULL }, 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, { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
0, 0, 0, NULL }, 0, 0, 0, NULL },
{ "sub_pos", mp_property_sub_pos, CONF_TYPE_INT, { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
@ -2299,10 +1838,6 @@ static struct property_osd_display {
{ "vsync", 0, -1, _("VSync: %s") }, { "vsync", 0, -1, _("VSync: %s") },
// subs // subs
{ "sub", 0, -1, _("Subtitles: %s") }, { "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_pos", 0, -1, _("Sub position: %s/100") },
{ "sub_alignment", 0, -1, _("Sub alignment: %s") }, { "sub_alignment", 0, -1, _("Sub alignment: %s") },
{ "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") }, { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
@ -2423,10 +1958,6 @@ static struct {
{ "vsync", MP_CMD_SWITCH_VSYNC, 1}, { "vsync", MP_CMD_SWITCH_VSYNC, 1},
// subs // subs
{ "sub", MP_CMD_SUB_SELECT, 1}, { "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_pos", MP_CMD_SUB_POS, 0},
{ "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1}, { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
{ "sub_delay", MP_CMD_SUB_DELAY, 0}, { "sub_delay", MP_CMD_SUB_DELAY, 0},
@ -2539,51 +2070,45 @@ static void show_chapters_on_osd(MPContext *mpctx)
talloc_free(res); 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) static void show_tracks_on_osd(MPContext *mpctx)
{ {
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
demuxer_t *demuxer = mpctx->master_demuxer;
char *res = NULL; char *res = NULL;
if (!demuxer) for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
return; for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *track = mpctx->tracks[n];
if (track->type != type)
continue;
struct sh_stream *cur_a = mpctx->sh_audio ? mpctx->sh_audio->gsh : NULL; bool selected = mpctx->current_track[track->type] == track;
struct sh_stream *cur_s = NULL; res = talloc_asprintf_append(res, "%s: ", track_type_name(track->type));
if (opts->sub_id >= 0 && mpctx->d_sub && mpctx->d_sub->sh) if (selected)
cur_s = ((struct sh_sub *)mpctx->d_sub->sh)->gsh; res = talloc_asprintf_append(res, "> ");
res = talloc_asprintf_append(res, "(%d) ", track->user_tid);
int v_count = 0; if (track->title)
enum stream_type t = STREAM_AUDIO; res = talloc_asprintf_append(res, "'%s' ", track->title);
if (track->lang)
for (int n = 0; n < demuxer->num_streams; n++) { res = talloc_asprintf_append(res, "(%s) ", track->lang);
struct sh_stream *sh = demuxer->streams[n]; if (track->is_external)
if (sh->type == STREAM_VIDEO) { res = talloc_asprintf_append(res, "(external) ");
v_count++; if (selected)
continue; res = talloc_asprintf_append(res, "<");
}
if (t != sh->type)
res = talloc_asprintf_append(res, "\n"); 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");
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); set_osd_msg(mpctx, OSD_MSG_TEXT, 1, opts->osd_duration, "%s", res);
talloc_free(res); talloc_free(res);
@ -2783,7 +2308,9 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_SUB_STEP: case MP_CMD_SUB_STEP:
if (sh_video) { if (sh_video) {
int movement = cmd->args[0].v.i; 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 #ifdef CONFIG_ASS
if (mpctx->osd->ass_track) if (mpctx->osd->ass_track)
sub_delay += sub_delay +=
@ -3083,12 +2610,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_SUB_LOAD: case MP_CMD_SUB_LOAD:
if (sh_video) { if (sh_video) {
int n = mpctx->set_of_sub_size;
add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0); 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; break;

View File

@ -134,10 +134,6 @@ static const mp_cmd_t mp_cmds[] = {
{ MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } }, { MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } },
{ MP_CMD_SUB_SELECT, "vobsub_lang", { OARG_INT(-2) } }, // for compatibility { MP_CMD_SUB_SELECT, "vobsub_lang", { OARG_INT(-2) } }, // for compatibility
{ MP_CMD_SUB_SELECT, "sub_select", { OARG_INT(-2) } }, { 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) } }, { MP_CMD_SUB_SCALE, "sub_scale", { ARG_FLOAT, OARG_INT(0) } },
#ifdef CONFIG_ASS #ifdef CONFIG_ASS
{ MP_CMD_ASS_USE_MARGINS, "ass_use_margins", { OARG_INT(-1) } }, { MP_CMD_ASS_USE_MARGINS, "ass_use_margins", { OARG_INT(-1) } },

View File

@ -118,10 +118,6 @@ enum mp_command_type {
MP_CMD_BALANCE, MP_CMD_BALANCE,
MP_CMD_SUB_SCALE, MP_CMD_SUB_SCALE,
MP_CMD_TV_START_SCAN, 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_SWITCH_ANGLE,
MP_CMD_ASS_USE_MARGINS, MP_CMD_ASS_USE_MARGINS,
MP_CMD_SWITCH_TITLE, MP_CMD_SWITCH_TITLE,

View File

@ -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); sh_audio = new_sh_audio_aid(demuxer, i, priv->audio_streams);
if (!sh_audio) if (!sh_audio)
break; break;
sh_audio->gsh->demuxer_id = i;
sh_audio->demuxer_codecname = codec_name; sh_audio->demuxer_codecname = codec_name;
stream_type = "audio"; stream_type = "audio";
priv->astreams[priv->audio_streams] = i; 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); sh_video = new_sh_video_vid(demuxer, i, priv->video_streams);
if (!sh_video) if (!sh_video)
break; break;
sh_video->gsh->demuxer_id = i;
sh_video->demuxer_codecname = codec_name; sh_video->demuxer_codecname = codec_name;
stream_type = "video"; stream_type = "video";
priv->vstreams[priv->video_streams] = i; 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); sh_sub = new_sh_sub_sid(demuxer, i, priv->sub_streams);
if (!sh_sub) if (!sh_sub)
break; break;
sh_sub->gsh->demuxer_id = i;
sh_sub->demuxer_codecname = codec_name; sh_sub->demuxer_codecname = codec_name;
stream_type = "subtitle"; stream_type = "subtitle";
priv->sstreams[priv->sub_streams] = i; priv->sstreams[priv->sub_streams] = i;

View File

@ -189,10 +189,12 @@ static void free_demuxer_stream(struct demux_stream *ds)
free(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)); demux_stream_t *ds = malloc(sizeof(demux_stream_t));
*ds = (demux_stream_t){ *ds = (demux_stream_t) {
.stream_type = type,
.id = id, .id = id,
.demuxer = demuxer, .demuxer = demuxer,
.asf_seq = -1, .asf_seq = -1,
@ -200,6 +202,19 @@ static struct demux_stream *new_demuxer_stream(struct demuxer *demuxer, int id)
return ds; 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 * 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->seekable = 1;
d->synced = 0; d->synced = 0;
d->filepos = -1; d->filepos = -1;
d->audio = new_demuxer_stream(d, a_id); d->audio = new_demuxer_stream(d, STREAM_VIDEO, a_id);
d->video = new_demuxer_stream(d, v_id); d->video = new_demuxer_stream(d, STREAM_AUDIO, v_id);
d->sub = new_demuxer_stream(d, s_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->type = type;
d->opts = opts; d->opts = opts;
if (type) 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, { struct sh_stream *sh = talloc_struct(demuxer, struct sh_stream, {
.type = type, .type = type,
.demuxer = demuxer,
.index = demuxer->num_streams, .index = demuxer->num_streams,
.demuxer_id = demuxer->new_stream_id++, // possibly temporary value only .demuxer_id = tid, // may be overwritten by demuxer
.tid = tid, .tid = tid,
.stream_index = stream_index, .stream_index = stream_index,
.opts = demuxer->opts, .opts = demuxer->opts,
@ -308,7 +327,6 @@ static struct sh_stream *new_sh_stream(demuxer_t *demuxer,
} }
default: assert(false); default: assert(false);
} }
sh->common_header->id = sh->tid;
sh->common_header->opts = sh->opts; sh->common_header->opts = sh->opts;
sh->common_header->gsh = sh; sh->common_header->gsh = sh;
return sh; return sh;
@ -1235,37 +1253,42 @@ int demux_control(demuxer_t *demuxer, int cmd, void *arg)
return DEMUXER_CTRL_NOTIMPL; 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); for (int n = 0; n < d->num_streams; n++) {
if (res == DEMUXER_CTRL_NOTIMPL) { struct sh_stream *s = d->streams[n];
struct sh_audio *sh_audio = demuxer->audio->sh; if (s->type == t && s->demuxer_id == id)
return sh_audio ? sh_audio->aid : -2; return d->streams[n];
} }
if (demuxer->audio->id >= 0) { return NULL;
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;
} }
int demuxer_switch_video(demuxer_t *demuxer, int index) void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
struct sh_stream *stream)
{ {
int res = demux_control(demuxer, DEMUXER_CTRL_SWITCH_VIDEO, &index); assert(!stream || stream->type == type);
if (res == DEMUXER_CTRL_NOTIMPL) { int index = stream ? stream->tid : -2;
struct sh_video *sh_video = demuxer->video->sh; if (type == STREAM_AUDIO) {
return sh_video ? sh_video->vid : -2; 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) { int new_id = demuxer->ds[type]->id;
struct sh_video *sh_video = demuxer->v_streams[demuxer->video->id]; void *new = NULL;
demuxer->video->sh = sh_video; if (new_id >= 0) {
index = sh_video->vid; // internal MPEG demuxers don't set it right switch (type) {
} else case STREAM_VIDEO: new = demuxer->v_streams[new_id]; break;
demuxer->video->sh = NULL; case STREAM_AUDIO: new = demuxer->a_streams[new_id]; break;
return index; 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, 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; return angle;
} }
static char *demuxer_audio_lang(demuxer_t *d, int id) char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *sh)
{ {
struct stream_lang_req req; struct stream_lang_req req = { .id = sh->tid }; // assume 1:1 mapping
sh_audio_t *sh; switch (sh->type) {
if (id < 0 || id >= MAX_A_STREAMS) case STREAM_AUDIO: req.type = stream_ctrl_audio; break;
return NULL; case STREAM_SUB: req.type = stream_ctrl_sub; break;
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)
{
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;
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; default: return NULL;
} }
} if (stream_control(d->stream, STREAM_CTRL_GET_LANG, &req) == STREAM_OK)
return req.name;
int demuxer_audio_track_by_lang_and_default(struct demuxer *d, char **langt) return NULL;
{
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;
}
} }

View File

@ -28,6 +28,7 @@
#include "bstr.h" #include "bstr.h"
#include "mpcommon.h" #include "mpcommon.h"
#include "demux_packet.h" #include "demux_packet.h"
#include "stheader.h"
struct MPOpts; struct MPOpts;
@ -103,6 +104,7 @@ enum timestamp_type {
#define MP_INPUT_BUFFER_PADDING_SIZE 16 #define MP_INPUT_BUFFER_PADDING_SIZE 16
typedef struct demux_stream { typedef struct demux_stream {
enum stream_type stream_type;
int buffer_pos; // current buffer position int buffer_pos; // current buffer position
int buffer_size; // current buffer size int buffer_size; // current buffer size
unsigned char *buffer; // current buffer, never free() it, always use free_demux_packet(buffer_ref); 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; bool accurate_seek;
enum timestamp_type timestamp_type; 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 *audio; // audio buffer/demuxer
struct demux_stream *video; // video buffer/demuxer struct demux_stream *video; // video buffer/demuxer
struct demux_stream *sub; // dvd subtitle buffer/demuxer struct demux_stream *sub; // dvd subtitle buffer/demuxer
@ -261,8 +266,6 @@ typedef struct demuxer {
char **info; // metadata char **info; // metadata
struct MPOpts *opts; struct MPOpts *opts;
struct demuxer_params *params; struct demuxer_params *params;
int new_stream_id;
} demuxer_t; } demuxer_t;
typedef struct { typedef struct {
@ -295,6 +298,8 @@ struct demuxer *new_demuxer(struct MPOpts *opts, struct stream *stream,
char *filename); char *filename);
void free_demuxer(struct demuxer *demuxer); 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_add_packet(struct demux_stream *ds, struct demux_packet *dp);
void ds_read_packet(struct demux_stream *ds, struct stream *stream, int len, void ds_read_packet(struct demux_stream *ds, struct stream *stream, int len,
double pts, off_t pos, bool keyframe); 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_info_print(struct demuxer *demuxer);
int demux_control(struct demuxer *demuxer, int cmd, void *arg); int demux_control(struct demuxer *demuxer, int cmd, void *arg);
int demuxer_switch_audio(struct demuxer *demuxer, int index); void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
int demuxer_switch_video(struct demuxer *demuxer, int index); struct sh_stream *stream);
int demuxer_type_by_filename(char *filename); int demuxer_type_by_filename(char *filename);
@ -398,19 +403,8 @@ int demuxer_set_angle(struct demuxer *demuxer, int angle);
/// Get number of angles. /// Get number of angles.
int demuxer_angles_count(struct demuxer *demuxer); int demuxer_angles_count(struct demuxer *demuxer);
/* Get the index of a track. struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
* lang is a string list, NULL is same as empty list enum stream_type t, int id);
* 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);
char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *s); char *demuxer_stream_lang(demuxer_t *d, struct sh_stream *s);

View File

@ -27,22 +27,24 @@ struct MPOpts;
struct demuxer; struct demuxer;
enum stream_type { enum stream_type {
STREAM_VIDEO = 1, STREAM_VIDEO,
STREAM_AUDIO, STREAM_AUDIO,
STREAM_SUB, STREAM_SUB,
STREAM_TYPE_COUNT,
}; };
// Stream headers: // Stream headers:
struct sh_stream { struct sh_stream {
enum stream_type type; enum stream_type type;
struct demuxer *demuxer;
// Index into demuxer->streams. // Index into demuxer->streams.
int index; int index;
// The (possibly) type specific id, e.g. aid or sid. // The (possibly) type specific id, e.g. aid or sid.
int tid; int tid;
// Index into stream array (currently one array per type, e.g. a_streams). // Index into stream array (currently one array per type, e.g. a_streams).
int stream_index; int stream_index;
// Demuxer specific ID (-1 if unknown, otherwise >= 0). // Demuxer specific ID (always set, defaults to tid).
int demuxer_id; int demuxer_id;
// Abomination. // Abomination.
struct sh_common *common_header; struct sh_common *common_header;
@ -61,7 +63,6 @@ struct sh_stream {
#define SH_COMMON \ #define SH_COMMON \
int id; \
struct sh_stream *gsh; \ struct sh_stream *gsh; \
const char *demuxer_codecname; \ const char *demuxer_codecname; \
struct MPOpts *opts; \ struct MPOpts *opts; \

View File

@ -25,6 +25,7 @@
#include "mixer.h" #include "mixer.h"
#include "sub/subreader.h" #include "sub/subreader.h"
#include "sub/find_subfiles.h" #include "sub/find_subfiles.h"
#include "libmpdemux/demuxer.h"
// definitions used internally by the core player code // definitions used internally by the core player code
@ -75,6 +76,46 @@ struct chapter {
char *name; 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 { typedef struct MPContext {
struct MPOpts opts; struct MPOpts opts;
struct m_config *mconfig; struct m_config *mconfig;
@ -83,7 +124,7 @@ typedef struct MPContext {
struct osd_state *osd; struct osd_state *osd;
struct mp_osd_msg *osd_msg_stack; struct mp_osd_msg *osd_msg_stack;
char *terminal_osd_text; 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; bool add_osd_seek_info;
unsigned int osd_visible; unsigned int osd_visible;
@ -99,6 +140,7 @@ typedef struct MPContext {
struct demuxer **sources; struct demuxer **sources;
int num_sources; int num_sources;
struct timeline_part *timeline; struct timeline_part *timeline;
int num_timeline_parts; int num_timeline_parts;
int timeline_part; int timeline_part;
@ -110,11 +152,17 @@ typedef struct MPContext {
struct stream *stream; struct stream *stream;
struct demuxer *demuxer; struct demuxer *demuxer;
struct sh_audio *sh_audio;
struct sh_video *sh_video; struct track **tracks;
struct demux_stream *d_audio; int num_tracks;
struct demux_stream *d_video;
struct demux_stream *d_sub; // 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 // Uses: accessing metadata (consider ordered chapters case, where the main
// demuxer defines metadata), or special purpose demuxers like TV. // 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 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; struct ass_library *ass_library;
int file_format; int file_format;
@ -240,8 +278,10 @@ char *chapter_display_name(struct MPContext *mpctx, int chapter);
char *chapter_name(struct MPContext *mpctx, int chapter); char *chapter_name(struct MPContext *mpctx, int chapter);
double chapter_start_time(struct MPContext *mpctx, int chapter); double chapter_start_time(struct MPContext *mpctx, int chapter);
int get_chapter_count(struct MPContext *mpctx); 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 // timeline/tl_matroska.c
void build_ordered_chapter_timeline(struct MPContext *mpctx); void build_ordered_chapter_timeline(struct MPContext *mpctx);

755
mplayer.c

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,11 @@
#include "spudec.h" #include "spudec.h"
#include "av_sub.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) void reset_avsub(struct sh_sub *sh)
{ {
if (sh->context) { if (sh->context) {

View File

@ -26,5 +26,6 @@ struct sh_sub;
void reset_avsub(struct sh_sub *sh); void reset_avsub(struct sh_sub *sh);
int decode_avsub(struct sh_sub *sh, uint8_t *data, int size, int decode_avsub(struct sh_sub *sh, uint8_t *data, int size,
double pts, double endpts); double pts, double endpts);
bool is_av_sub(int type);
#endif /* MPLAYER_AV_SUB_H */ #endif /* MPLAYER_AV_SUB_H */

View File

@ -289,7 +289,8 @@ void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys,
if(obj->flags&OSDFLAG_VISIBLE){ if(obj->flags&OSDFLAG_VISIBLE){
switch(obj->type){ switch(obj->type){
case OSDTYPE_SPU: 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; break;
case OSDTYPE_OSD: case OSDTYPE_OSD:
case OSDTYPE_SUBTITLE: case OSDTYPE_SUBTITLE: