summaryrefslogtreecommitdiffstats
path: root/mplayer.c
diff options
context:
space:
mode:
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) {
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;
- upd