diff options
author | wm4 <wm4@nowhere> | 2013-12-17 00:53:22 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2013-12-17 00:53:22 +0100 |
commit | e44911142914783c9ec717f329bd9b6a8bb9b70e (patch) | |
tree | 92bb653f7d56553ffd3bb6e5a22ffc0db91142e8 /mpvcore/player/loadfile.c | |
parent | 7dc7b900c622235d595337c988a0c75280084b7c (diff) | |
download | mpv-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.c | 1423 |
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, ¶ms, 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_ |