summaryrefslogtreecommitdiffstats
path: root/mpvcore/player/loadfile.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-12-17 00:53:22 +0100
committerwm4 <wm4@nowhere>2013-12-17 00:53:22 +0100
commite44911142914783c9ec717f329bd9b6a8bb9b70e (patch)
tree92bb653f7d56553ffd3bb6e5a22ffc0db91142e8 /mpvcore/player/loadfile.c
parent7dc7b900c622235d595337c988a0c75280084b7c (diff)
downloadmpv-e44911142914783c9ec717f329bd9b6a8bb9b70e.tar.bz2
mpv-e44911142914783c9ec717f329bd9b6a8bb9b70e.tar.xz
Move mpvcore/player/ to player/
Diffstat (limited to 'mpvcore/player/loadfile.c')
-rw-r--r--mpvcore/player/loadfile.c1423
1 files changed, 0 insertions, 1423 deletions
diff --git a/mpvcore/player/loadfile.c b/mpvcore/player/loadfile.c
deleted file mode 100644
index 08b245a0ff..0000000000
--- a/mpvcore/player/loadfile.c
+++ /dev/null
@@ -1,1423 +0,0 @@
-/*
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <inttypes.h>
-#include <assert.h>
-
-#include <libavutil/avutil.h>
-
-#include "config.h"
-#include "talloc.h"
-
-#include "osdep/getch2.h"
-#include "osdep/io.h"
-#include "osdep/timer.h"
-
-#include "mpvcore/mp_msg.h"
-#include "mpvcore/path.h"
-#include "mpvcore/m_config.h"
-#include "mpvcore/parser-cfg.h"
-#include "mpvcore/playlist.h"
-#include "mpvcore/options.h"
-#include "mpvcore/m_property.h"
-#include "mpvcore/mp_common.h"
-#include "mpvcore/resolve.h"
-#include "mpvcore/encode.h"
-#include "mpvcore/input/input.h"
-
-#include "audio/mixer.h"
-#include "audio/audio.h"
-#include "audio/audio_buffer.h"
-#include "audio/decode/dec_audio.h"
-#include "audio/out/ao.h"
-#include "demux/demux.h"
-#include "stream/stream.h"
-#include "sub/ass_mp.h"
-#include "sub/dec_sub.h"
-#include "sub/find_subfiles.h"
-#include "video/decode/dec_video.h"
-#include "video/out/vo.h"
-
-#include "mp_core.h"
-#include "command.h"
-
-#if HAVE_DVBIN
-#include "stream/dvbin.h"
-#endif
-
-void uninit_player(struct MPContext *mpctx, unsigned int mask)
-{
- struct MPOpts *opts = mpctx->opts;
-
- mask &= mpctx->initialized_flags;
-
- MP_DBG(mpctx, "\n*** uninit(0x%X)\n", mask);
-
- if (mask & INITIALIZED_ACODEC) {
- mpctx->initialized_flags &= ~INITIALIZED_ACODEC;
- mixer_uninit_audio(mpctx->mixer);
- audio_uninit(mpctx->d_audio);
- mpctx->d_audio = NULL;
- cleanup_demux_stream(mpctx, STREAM_AUDIO);
- }
-
- if (mask & INITIALIZED_SUB) {
- mpctx->initialized_flags &= ~INITIALIZED_SUB;
- if (mpctx->d_sub)
- sub_reset(mpctx->d_sub);
- cleanup_demux_stream(mpctx, STREAM_SUB);
- mpctx->d_sub = NULL; // Note: not free'd.
- mpctx->osd->dec_sub = NULL;
- reset_subtitles(mpctx);
- }
-
- if (mask & INITIALIZED_LIBASS) {
- mpctx->initialized_flags &= ~INITIALIZED_LIBASS;
-#if HAVE_LIBASS
- if (mpctx->ass_renderer)
- ass_renderer_done(mpctx->ass_renderer);
- mpctx->ass_renderer = NULL;
- ass_clear_fonts(mpctx->ass_library);
-#endif
- }
-
- if (mask & INITIALIZED_VCODEC) {
- mpctx->initialized_flags &= ~INITIALIZED_VCODEC;
- if (mpctx->d_video)
- video_uninit(mpctx->d_video);
- mpctx->d_video = NULL;
- cleanup_demux_stream(mpctx, STREAM_VIDEO);
- mpctx->sync_audio_to_video = false;
- }
-
- if (mask & INITIALIZED_DEMUXER) {
- mpctx->initialized_flags &= ~INITIALIZED_DEMUXER;
- assert(!(mpctx->initialized_flags &
- (INITIALIZED_VCODEC | INITIALIZED_ACODEC | INITIALIZED_SUB)));
- for (int i = 0; i < mpctx->num_tracks; i++) {
- talloc_free(mpctx->tracks[i]);
- }
- mpctx->num_tracks = 0;
- for (int t = 0; t < STREAM_TYPE_COUNT; t++)
- mpctx->current_track[t] = NULL;
- assert(!mpctx->d_video && !mpctx->d_audio && !mpctx->d_sub);
- mpctx->master_demuxer = NULL;
- for (int i = 0; i < mpctx->num_sources; i++) {
- uninit_subs(mpctx->sources[i]);
- struct demuxer *demuxer = mpctx->sources[i];
- struct stream *stream = demuxer->stream;
- free_demuxer(demuxer);
- if (stream != mpctx->stream)
- free_stream(stream);
- }
- talloc_free(mpctx->sources);
- mpctx->sources = NULL;
- mpctx->demuxer = NULL;
- mpctx->num_sources = 0;
- talloc_free(mpctx->timeline);
- mpctx->timeline = NULL;
- mpctx->num_timeline_parts = 0;
- talloc_free(mpctx->chapters);
- mpctx->chapters = NULL;
- mpctx->num_chapters = 0;
- mpctx->video_offset = 0;
- }
-
- // kill the cache process:
- if (mask & INITIALIZED_STREAM) {
- mpctx->initialized_flags &= ~INITIALIZED_STREAM;
- if (mpctx->stream)
- free_stream(mpctx->stream);
- mpctx->stream = NULL;
- }
-
- if (mask & INITIALIZED_VO) {
- mpctx->initialized_flags &= ~INITIALIZED_VO;
- vo_destroy(mpctx->video_out);
- mpctx->video_out = NULL;
- }
-
- if (mask & INITIALIZED_AO) {
- struct ao *ao = mpctx->ao;
- mpctx->initialized_flags &= ~INITIALIZED_AO;
- if (ao) {
- bool drain = false;
- // Note: with gapless_audio, stop_play is not correctly set
- if (opts->gapless_audio || mpctx->stop_play == AT_END_OF_FILE) {
- drain = true;
- struct mp_audio data;
- mp_audio_buffer_peek(ao->buffer, &data);
- int samples = ao->buffer_playable_samples;
- assert(samples <= data.samples);
- if (samples > 0) {
- int played = ao_play(ao, data.planes, samples,
- AOPLAY_FINAL_CHUNK);
- if (played < samples)
- MP_WARN(ao, "Audio output truncated at end.\n");
- }
- }
- ao_uninit(ao, drain);
- }
- mpctx->ao = NULL;
- }
-
- if (mask & INITIALIZED_PLAYBACK)
- mpctx->initialized_flags &= ~INITIALIZED_PLAYBACK;
-}
-
-static void print_stream(struct MPContext *mpctx, struct track *t)
-{
- struct sh_stream *s = t->stream;
- const char *tname = "?";
- const char *selopt = "?";
- const char *langopt = "?";
- const char *iid = NULL;
- switch (t->type) {
- case STREAM_VIDEO:
- tname = "Video"; selopt = "vid"; langopt = NULL; iid = "VID";
- break;
- case STREAM_AUDIO:
- tname = "Audio"; selopt = "aid"; langopt = "alang"; iid = "AID";
- break;
- case STREAM_SUB:
- tname = "Subs"; selopt = "sid"; langopt = "slang"; iid = "SID";
- break;
- }
- MP_INFO(mpctx, "[stream] %-5s %3s",
- tname, mpctx->current_track[t->type] == t ? "(+)" : "");
- MP_INFO(mpctx, " --%s=%d", selopt, t->user_tid);
- if (t->lang && langopt)
- MP_INFO(mpctx, " --%s=%s", langopt, t->lang);
- if (t->default_track)
- MP_INFO(mpctx, " (*)");
- if (t->attached_picture)
- MP_INFO(mpctx, " [P]");
- if (t->title)
- MP_INFO(mpctx, " '%s'", t->title);
- const char *codec = s ? s->codec : NULL;
- MP_INFO(mpctx, " (%s)", codec ? codec : "<unknown>");
- if (t->is_external)
- MP_INFO(mpctx, " (external)");
- MP_INFO(mpctx, "\n");
- // legacy compatibility
- if (!iid)
- return;
- int id = t->user_tid;
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_%s_ID=%d\n", iid, id);
- if (t->title)
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_%s_%d_NAME=%s\n", iid, id, t->title);
- if (t->lang)
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_%s_%d_LANG=%s\n", iid, id, t->lang);
-}
-
-static void print_file_properties(struct MPContext *mpctx)
-{
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILENAME=%s\n", mpctx->filename);
- mp_msg(MSGT_IDENTIFY, MSGL_INFO,
- "ID_LENGTH=%.2f\n", get_time_length(mpctx));
- int chapter_count = get_chapter_count(mpctx);
- if (chapter_count >= 0) {
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTERS=%d\n", chapter_count);
- for (int i = 0; i < chapter_count; i++) {
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_ID=%d\n", i);
- // print in milliseconds
- double time = chapter_start_time(mpctx, i) * 1000.0;
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_START=%"PRId64"\n",
- i, (int64_t)(time < 0 ? -1 : time));
- char *name = chapter_name(mpctx, i);
- if (name) {
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CHAPTER_%d_NAME=%s\n", i,
- name);
- talloc_free(name);
- }
- }
- }
- struct demuxer *demuxer = mpctx->master_demuxer;
- if (demuxer->num_editions > 1)
- MP_INFO(mpctx, "Playing edition %d of %d (--edition=%d).\n",
- demuxer->edition + 1, demuxer->num_editions, demuxer->edition);
- 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]);
- }
-}
-
-struct sh_stream *init_demux_stream(struct MPContext *mpctx,
- enum stream_type type)
-{
- struct track *track = mpctx->current_track[type];
- struct sh_stream *stream = track ? track->stream : NULL;
- mpctx->sh[type] = stream;
- if (stream) {
- demuxer_switch_track(stream->demuxer, type, stream);
- if (track->is_external) {
- double pts = get_main_demux_pts(mpctx);
- demux_seek(stream->demuxer, pts, SEEK_ABSOLUTE);
- }
- }
- return stream;
-}
-
-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);
- mpctx->sh[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++) {
- struct track *track = mpctx->current_track[type];
- if (!(track && track->demuxer == mpctx->sources[n] &&
- demuxer_stream_is_selected(track->demuxer, track->stream)))
- 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 struct sh_stream *select_fallback_stream(struct demuxer *d,
- enum stream_type type,
- int index)
-{
- struct sh_stream *best_stream = NULL;
- for (int n = 0; n < d->num_streams; n++) {
- struct sh_stream *s = d->streams[n];
- if (s->type == type) {
- best_stream = s;
- if (index == 0)
- break;
- index -= 1;
- }
- }
- return best_stream;
-}
-
-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 && !force)
- return false;
- enum stop_play_reason orig_stop_play = mpctx->stop_play;
- if (!mpctx->d_video && mpctx->stop_play == KEEP_PLAYING)
- mpctx->stop_play = AT_END_OF_FILE; // let audio uninit drain data
- uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts->fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts->gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_ACODEC | INITIALIZED_SUB);
- mpctx->stop_play = orig_stop_play;
-
- mpctx->demuxer = n->source;
- 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 x = 0; x < mpctx->num_tracks; x++) {
- struct track *track = mpctx->tracks[x];
- if (track->under_timeline) {
- track->demuxer = mpctx->demuxer;
- track->stream = demuxer_stream_by_demuxer_id(track->demuxer,
- track->type,
- track->demuxer_id);
- // EDL can have mismatched files in the same timeline
- if (!track->stream) {
- track->stream = select_fallback_stream(track->demuxer,
- track->type,
- track->user_tid - 1);
- }
- }
- }
- preselect_demux_streams(mpctx);
-
- return true;
-}
-
-// Given pts, switch playback to the corresponding part.
-// Return offset within that part.
-double timeline_set_from_time(struct MPContext *mpctx, double pts, bool *need_reset)
-{
- if (pts < 0)
- pts = 0;
- 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, false);
- return pts - p->start + p->source_start;
- }
- }
- return -1;
-}
-
-// Map stream number (as used by libdvdread) to MPEG IDs (as used by demuxer).
-static int map_id_from_demuxer(struct demuxer *d, enum stream_type type, int id)
-{
- if (d->stream->uncached_type == STREAMTYPE_DVD && type == STREAM_SUB)
- id = id & 0x1F;
- return id;
-}
-
-static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
-{
- int new_id = 0;
- for (int i = 0; i < mpctx->num_tracks; i++) {
- struct track *track = mpctx->tracks[i];
- if (track->type == t)
- new_id = MPMAX(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;
- // DVD subtitle track that was added later
- if (stream->type == STREAM_SUB && track->type == STREAM_SUB &&
- map_id_from_demuxer(stream->demuxer, stream->type,
- stream->demuxer_id) == track->demuxer_id
- && !track->stream)
- {
- track->stream = stream;
- track->demuxer_id = stream->demuxer_id;
- // Initialize lazily selected track
- bool selected = track == mpctx->current_track[STREAM_SUB];
- demuxer_select_track(track->demuxer, stream, selected);
- if (selected)
- reinit_subs(mpctx);
- 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,
- .attached_picture = stream->attached_picture != NULL,
- .lang = stream->lang,
- .under_timeline = under_timeline,
- .demuxer = stream->demuxer,
- .stream = stream,
- };
- MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
-
- if (stream->type == STREAM_SUB)
- track->preloaded = !!stream->sub->track;
-
- // Needed for DVD and Blu-ray.
- if (!track->lang) {
- struct stream_lang_req req = {
- .type = track->type,
- .id = map_id_from_demuxer(track->demuxer, track->type,
- track->demuxer_id)
- };
- stream_control(track->demuxer->stream, STREAM_CTRL_GET_LANG, &req);
- if (req.name[0])
- track->lang = talloc_strdup(track, req.name);
- }
-
- demuxer_select_track(track->demuxer, stream, false);
-
- mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
-
- return track;
-}
-
-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);
-}
-
-static void add_dvd_tracks(struct MPContext *mpctx)
-{
- struct demuxer *demuxer = mpctx->demuxer;
- struct stream *stream = demuxer->stream;
- struct stream_dvd_info_req info;
- if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
- for (int n = 0; n < info.num_subs; n++) {
- struct track *track = talloc_ptrtype(NULL, track);
- *track = (struct track) {
- .type = STREAM_SUB,
- .user_tid = find_new_tid(mpctx, STREAM_SUB),
- .demuxer_id = n,
- .demuxer = mpctx->demuxer,
- };
- MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
-
- struct stream_lang_req req = {.type = STREAM_SUB, .id = n};
- stream_control(stream, STREAM_CTRL_GET_LANG, &req);
- track->lang = talloc_strdup(track, req.name);
-
- mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
- }
- }
- demuxer_enable_autoselect(demuxer);
-}
-
-// Result numerically higher => better match. 0 == no match.
-static int match_lang(char **langs, char *lang)
-{
- for (int idx = 0; langs && langs[idx]; idx++) {
- if (lang && strcmp(langs[idx], lang) == 0)
- return INT_MAX - idx;
- }
- return 0;
-}
-
-/* Get the track wanted by the user.
- * tid is the track ID requested by the user (-2: deselect, -1: default)
- * lang is a string list, NULL is same as empty list
- * Sort tracks based on the following criteria, and pick the first:
- * 0) track matches tid (always wins)
- * 1) track is external
- * 1b) track was passed explicitly (is not an auto-loaded subtitle)
- * 2) earlier match in lang list
- * 3) track is marked default
- * 4) lower track number
- * If select_fallback is not set, 4) is only used to determine whether a
- * matching track is preferred over another track. Otherwise, always pick a
- * track (if nothing else matches, return the track with lowest ID).
- */
-// Return whether t1 is preferred over t2
-static bool compare_track(struct track *t1, struct track *t2, char **langs)
-{
- if (t1->is_external != t2->is_external)
- return t1->is_external;
- if (t1->auto_loaded != t2->auto_loaded)
- return !t1->auto_loaded;
- int l1 = match_lang(langs, t1->lang), l2 = match_lang(langs, t2->lang);
- if (l1 != l2)
- return l1 > l2;
- if (t1->default_track != t2->default_track)
- return t1->default_track;
- if (t1->attached_picture != t2->attached_picture)
- return !t1->attached_picture;
- return t1->user_tid <= t2->user_tid;
-}
-static struct track *select_track(struct MPContext *mpctx,
- enum stream_type type, int tid, char **langs)
-{
- if (tid == -2)
- return NULL;
- bool select_fallback = type == STREAM_VIDEO || type == STREAM_AUDIO;
- struct track *pick = NULL;
- for (int n = 0; n < mpctx->num_tracks; n++) {
- struct track *track = mpctx->tracks[n];
- if (track->type != type)
- continue;
- if (track->user_tid == tid)
- return track;
- if (!pick || compare_track(track, pick, langs))
- pick = track;
- }
- if (pick && !select_fallback && !pick->is_external
- && !match_lang(langs, pick->lang) && !pick->default_track)
- pick = NULL;
- if (pick && pick->attached_picture && !mpctx->opts->audio_display)
- pick = NULL;
- return pick;
-}
-
-static char *track_layout_hash(struct MPContext *mpctx)
-{
- char *h = talloc_strdup(NULL, "");
- for (int type = 0; type < STREAM_TYPE_COUNT; type++) {
- for (int n = 0; n < mpctx->num_tracks; n++) {
- struct track *track = mpctx->tracks[n];
- if (track->type != type)
- continue;
- h = talloc_asprintf_append_buffer(h, "%d-%d-%d-%d-%s\n", type,
- track->user_tid, track->default_track, track->is_external,
- track->lang ? track->lang : "");
- }
- }
- return h;
-}
-
-// Normally, video/audio/sub track selection is persistent across files. This
-// code resets track selection if the new file has a different track layout.
-static void check_previous_track_selection(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
-
- if (!mpctx->track_layout_hash)
- return;
-
- char *h = track_layout_hash(mpctx);
- if (strcmp(h, mpctx->track_layout_hash) != 0) {
- // Reset selection, but only if they're not "auto" or "off".
- if (opts->video_id >= 0)
- mpctx->opts->video_id = -1;
- if (opts->audio_id >= 0)
- mpctx->opts->audio_id = -1;
- if (opts->sub_id >= 0)
- mpctx->opts->sub_id = -1;
- talloc_free(mpctx->track_layout_hash);
- mpctx->track_layout_hash = NULL;
- }
- talloc_free(h);
-}
-
-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 (type == STREAM_VIDEO) {
- int uninit = INITIALIZED_VCODEC;
- if (!mpctx->opts->force_vo)
- uninit |= mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO;
- uninit_player(mpctx, uninit);
- } else if (type == STREAM_AUDIO) {
- uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
- } else if (type == STREAM_SUB) {
- uninit_player(mpctx, INITIALIZED_SUB);
- }
-
- 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);
- mp_notify_property(mpctx, "vid");
- } else if (type == STREAM_AUDIO) {
- mpctx->opts->audio_id = user_tid;
- reinit_audio_chain(mpctx);
- mp_notify_property(mpctx, "aid");
- } else if (type == STREAM_SUB) {
- mpctx->opts->sub_id = user_tid;
- reinit_subs(mpctx);
- mp_notify_property(mpctx, "sid");
- }
-
- talloc_free(mpctx->track_layout_hash);
- mpctx->track_layout_hash = talloc_steal(mpctx, track_layout_hash(mpctx));
-}
-
-struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
- int tid)
-{
- if (tid == -1)
- return mpctx->current_track[type];
- 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 NULL;
-}
-
-bool mp_remove_track(struct MPContext *mpctx, struct track *track)
-{
- if (track->under_timeline)
- return false;
- if (!track->is_external)
- return false;
-
- if (mpctx->current_track[track->type] == track) {
- mp_switch_track(mpctx, track->type, NULL);
- if (mpctx->current_track[track->type] == track)
- return false;
- }
-
- int index = 0;
- while (index < mpctx->num_tracks && mpctx->tracks[index] != track)
- index++;
- assert(index < mpctx->num_tracks);
- while (index + 1 < mpctx->num_tracks) {
- mpctx->tracks[index] = mpctx->tracks[index + 1];
- index++;
- }
- mpctx->num_tracks--;
- talloc_free(track);
-
- mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL);
-
- return true;
-}
-
-static void open_subtitles_from_options(struct MPContext *mpctx)
-{
- if (mpctx->opts->sub_name) {
- for (int i = 0; mpctx->opts->sub_name[i] != NULL; ++i)
- mp_add_subtitles(mpctx, mpctx->opts->sub_name[i]);
- }
- if (mpctx->opts->sub_auto) { // auto load sub file ...
- void *tmp = talloc_new(NULL);
- char *base_filename = mpctx->filename;
- char *stream_filename = NULL;
- if (stream_control(mpctx->stream, STREAM_CTRL_GET_BASE_FILENAME,
- &stream_filename) > 0)
- base_filename = talloc_steal(tmp, stream_filename);
- struct subfn *list = find_text_subtitles(mpctx->opts, base_filename);
- talloc_steal(tmp, list);
- for (int i = 0; list && list[i].fname; i++) {
- char *filename = list[i].fname;
- char *lang = list[i].lang;
- for (int n = 0; n < mpctx->num_sources; n++) {
- if (strcmp(mpctx->sources[n]->stream->url, filename) == 0)
- goto skip;
- }
- struct track *track = mp_add_subtitles(mpctx, filename);
- if (track) {
- track->auto_loaded = true;
- if (!track->lang)
- track->lang = talloc_strdup(track, lang);
- }
- skip:;
- }
- talloc_free(tmp);
- }
-}
-
-static struct track *open_external_file(struct MPContext *mpctx, char *filename,
- char *demuxer_name, int stream_cache,
- enum stream_type filter)
-{
- struct MPOpts *opts = mpctx->opts;
- if (!filename)
- return NULL;
- char *disp_filename = filename;
- if (strncmp(disp_filename, "memory://", 9) == 0)
- disp_filename = "memory://"; // avoid noise
- struct stream *stream = stream_open(filename, mpctx->opts);
- if (!stream)
- goto err_out;
- stream_enable_cache_percent(&stream, stream_cache,
- opts->stream_cache_def_size,
- opts->stream_cache_min_percent,
- opts->stream_cache_seek_min_percent);
- struct demuxer_params params = {
- .ass_library = mpctx->ass_library, // demux_libass requires it
- };
- struct demuxer *demuxer =
- demux_open(stream, demuxer_name, &params, mpctx->opts);
- if (!demuxer) {
- free_stream(stream);
- goto err_out;
- }
- struct track *first = NULL;
- for (int n = 0; n < demuxer->num_streams; n++) {
- struct sh_stream *sh = demuxer->streams[n];
- if (sh->type == filter) {
- struct track *t = add_stream_track(mpctx, sh, false);
- t->is_external = true;
- t->title = talloc_strdup(t, disp_filename);
- t->external_filename = talloc_strdup(t, filename);
- first = t;
- }
- }
- if (!first) {
- free_demuxer(demuxer);
- free_stream(stream);
- MP_WARN(mpctx, "No streams added from file %s.\n",
- disp_filename);
- goto err_out;
- }
- MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
- return first;
-
-err_out:
- MP_ERR(mpctx, "Can not open external file %s.\n",
- disp_filename);
- return false;
-}
-
-static void open_audiofiles_from_options(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
- open_external_file(mpctx, opts->audio_stream, opts->audio_demuxer_name,
- opts->audio_stream_cache, STREAM_AUDIO);
-}
-
-struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename)
-{
- struct MPOpts *opts = mpctx->opts;
- return open_external_file(mpctx, filename, opts->sub_demuxer_name, 0,
- STREAM_SUB);
-}
-
-static void open_subtitles_from_resolve(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
- struct mp_resolve_result *res = mpctx->resolve_result;
- if (!res)
- return;
- for (int n = 0; n < res->num_subs; n++) {
- struct mp_resolve_sub *sub = res->subs[n];
- char *s = talloc_strdup(NULL, sub->url);
- if (!s)
- s = talloc_asprintf(NULL, "memory://%s", sub->data);
- struct track *t =
- open_external_file(mpctx, s, opts->sub_demuxer_name, 0, STREAM_SUB);
- talloc_free(s);
- if (t)
- t->lang = talloc_strdup(t, sub->lang);
- }
-}
-
-static bool attachment_is_font(struct demux_attachment *att)
-{
- if (!att->name || !att->type || !att->data || !att->data_size)
- return false;
- // match against MIME types
- if (strcmp(att->type, "application/x-truetype-font") == 0
- || strcmp(att->type, "application/x-font") == 0)
- return true;
- // fallback: match against file extension
- if (strlen(att->name) > 4) {
- char *ext = att->name + strlen(att->name) - 4;
- if (strcasecmp(ext, ".ttf") == 0 || strcasecmp(ext, ".ttc") == 0
- || strcasecmp(ext, ".otf") == 0)
- return true;
- }
- return false;
-}
-
-static void add_subtitle_fonts_from_sources(struct MPContext *mpctx)
-{
-#if HAVE_LIBASS
- if (mpctx->opts->ass_enabled) {
- for (int j = 0; j < mpctx->num_sources; j++) {
- struct demuxer *d = mpctx->sources[j];
- for (int i = 0; i < d->num_attachments; i++) {
- struct demux_attachment *att = d->attachments + i;
- if (mpctx->opts->use_embedded_fonts && attachment_is_font(att))
- ass_add_font(mpctx->ass_library, att->name, att->data,
- att->data_size);
- }
- }
- }
-#endif
-}
-
-static void init_sub_renderer(struct MPContext *mpctx)
-{
-#if HAVE_LIBASS
- assert(!(mpctx->initialized_flags & INITIALIZED_LIBASS));
- assert(!mpctx->ass_renderer);
-
- mpctx->ass_renderer = ass_renderer_init(mpctx->ass_library);
- if (mpctx->ass_renderer)
- mp_ass_configure_fonts(mpctx->ass_renderer, mpctx->opts->sub_text_style);
- mpctx->initialized_flags |= INITIALIZED_LIBASS;
-#endif
-}
-
-static struct mp_resolve_result *resolve_url(const char *filename,
- struct MPOpts *opts)
-{
- if (!mp_is_url(bstr0(filename)))
- return NULL;
-#if HAVE_LIBQUVI
- return mp_resolve_quvi(filename, opts);
-#else
- return NULL;
-#endif
-}
-
-static void print_resolve_contents(struct mp_log *log,
- struct mp_resolve_result *res)
-{
- mp_msg_log(log, MSGL_V, "Resolve:\n");
- mp_msg_log(log, MSGL_V, " title: %s\n", res->title);
- mp_msg_log(log, MSGL_V, " url: %s\n", res->url);
- for (int n = 0; n < res->num_srcs; n++) {
- mp_msg_log(log, MSGL_V, " source %d:\n", n);
- if (res->srcs[n]->url)
- mp_msg_log(log, MSGL_V, " url: %s\n", res->srcs[n]->url);
- if (res->srcs[n]->encid)
- mp_msg_log(log, MSGL_V, " encid: %s\n", res->srcs[n]->encid);
- }
- for (int n = 0; n < res->num_subs; n++) {
- mp_msg_log(log, MSGL_V, " subtitle %d:\n", n);
- if (res->subs[n]->url)
- mp_msg_log(log, MSGL_V, " url: %s\n", res->subs[n]->url);
- if (res->subs[n]->lang)
- mp_msg_log(log, MSGL_V, " lang: %s\n", res->subs[n]->lang);
- if (res->subs[n]->data) {
- mp_msg_log(log, MSGL_V, " data: %zd bytes\n",
- strlen(res->subs[n]->data));
- }
- }
- if (res->playlist) {
- mp_msg_log(log, MSGL_V, " playlist with %d entries\n",
- playlist_entry_count(res->playlist));
- }
-}
-
-// Replace the current playlist entry with playlist contents. Moves the entries
-// from the given playlist pl, so the entries don't actually need to be copied.
-static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
-{
- if (pl->first) {
- playlist_transfer_entries(mpctx->playlist, pl);
- // current entry is replaced
- if (mpctx->playlist->current)
- playlist_remove(mpctx->playlist, mpctx->playlist->current);
- } else {
- MP_WARN(mpctx, "Empty playlist!\n");
- }
-}
-
-static void print_timeline(struct MPContext *mpctx)
-{
- if (mpctx->timeline) {
- int part_count = mpctx->num_timeline_parts;
- MP_VERBOSE(mpctx, "Timeline contains %d parts from %d "
- "sources. Total length %.3f seconds.\n", part_count,
- mpctx->num_sources, mpctx->timeline[part_count].start);
- MP_VERBOSE(mpctx, "Source files:\n");
- for (int i = 0; i < mpctx->num_sources; i++)
- MP_VERBOSE(mpctx, "%d: %s\n", i,
- mpctx->sources[i]->filename);
- MP_VERBOSE(mpctx, "Timeline parts: (number, start, "
- "source_start, source):\n");
- for (int i = 0; i < part_count; i++) {
- struct timeline_part *p = mpctx->timeline + i;
- MP_VERBOSE(mpctx, "%3d %9.3f %9.3f %p/%s\n", i, p->start,
- p->source_start, p->source, p->source->filename);
- }
- MP_VERBOSE(mpctx, "END %9.3f\n",
- mpctx->timeline[part_count].start);
- }
-}
-
-/* When demux performs a blocking operation (network connection or
- * cache filling) if the operation fails we use this function to check
- * if it was interrupted by the user.
- * The function returns whether it was interrupted. */
-static bool demux_was_interrupted(struct MPContext *mpctx)
-{
- for (;;) {
- if (mpctx->stop_play != KEEP_PLAYING
- && mpctx->stop_