summaryrefslogtreecommitdiffstats
path: root/mplayer.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-08-19 18:01:30 +0200
committerwm4 <wm4@nowhere>2012-09-18 21:04:45 +0200
commit0f155921b046c9e6cfed3fe601aa891c2d2a8b16 (patch)
treeced8b2b76d00f3461bf12d0b5063ce2cf6fb8fe8 /mplayer.c
parent13482fb397ce6bbcec8a9342015f21d48fb7efe6 (diff)
downloadmpv-0f155921b046c9e6cfed3fe601aa891c2d2a8b16.tar.bz2
mpv-0f155921b046c9e6cfed3fe601aa891c2d2a8b16.tar.xz
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.
Diffstat (limited to 'mplayer.c')
-rw-r--r--mplayer.c751
1 files changed, 464 insertions, 287 deletions
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) {