diff options
Diffstat (limited to 'player/loadfile.c')
-rw-r--r-- | player/loadfile.c | 428 |
1 files changed, 246 insertions, 182 deletions
diff --git a/player/loadfile.c b/player/loadfile.c index 0adc8e351d..b8ccf6723e 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -23,7 +23,6 @@ #include <libavutil/avutil.h> -#include "config.h" #include "mpv_talloc.h" #include "misc/thread_pool.h" @@ -45,9 +44,9 @@ #include "options/m_property.h" #include "common/common.h" #include "common/encode.h" -#include "common/recorder.h" #include "common/stats.h" #include "input/input.h" +#include "misc/language.h" #include "audio/out/ao.h" #include "filters/f_decoder_wrapper.h" @@ -75,7 +74,7 @@ void mp_abort_playback_async(struct MPContext *mpctx) { mp_cancel_trigger(mpctx->playback_abort); - pthread_mutex_lock(&mpctx->abort_lock); + mp_mutex_lock(&mpctx->abort_lock); for (int n = 0; n < mpctx->num_abort_list; n++) { struct mp_abort_entry *abort = mpctx->abort_list[n]; @@ -83,25 +82,25 @@ void mp_abort_playback_async(struct MPContext *mpctx) mp_abort_trigger_locked(mpctx, abort); } - pthread_mutex_unlock(&mpctx->abort_lock); + mp_mutex_unlock(&mpctx->abort_lock); } // Add it to the global list, and allocate required data structures. void mp_abort_add(struct MPContext *mpctx, struct mp_abort_entry *abort) { - pthread_mutex_lock(&mpctx->abort_lock); + mp_mutex_lock(&mpctx->abort_lock); assert(!abort->cancel); abort->cancel = mp_cancel_new(NULL); MP_TARRAY_APPEND(NULL, mpctx->abort_list, mpctx->num_abort_list, abort); mp_abort_recheck_locked(mpctx, abort); - pthread_mutex_unlock(&mpctx->abort_lock); + mp_mutex_unlock(&mpctx->abort_lock); } // Remove Add it to the global list, and free/clear required data structures. // Does not deallocate the abort value itself. void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort) { - pthread_mutex_lock(&mpctx->abort_lock); + mp_mutex_lock(&mpctx->abort_lock); for (int n = 0; n < mpctx->num_abort_list; n++) { if (mpctx->abort_list[n] == abort) { MP_TARRAY_REMOVE_AT(mpctx->abort_list, mpctx->num_abort_list, n); @@ -111,7 +110,7 @@ void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort) } } assert(!abort); // should have been in the list - pthread_mutex_unlock(&mpctx->abort_lock); + mp_mutex_unlock(&mpctx->abort_lock); } // Verify whether the abort needs to be signaled after changing certain fields @@ -217,7 +216,6 @@ static void uninit_demuxer(struct MPContext *mpctx) assert(!track->dec && !track->d_sub); assert(!track->vo_c && !track->ao_c); assert(!track->sink); - assert(!track->remux_sink); // Demuxers can be added in any order (if they appear mid-stream), and // we can't know which tracks uses which, so here's some O(n^2) trash. @@ -258,7 +256,13 @@ static void print_stream(struct MPContext *mpctx, struct track *t) break; } char b[2048] = {0}; - APPEND(b, " %3s %-5s", t->selected ? "(+)" : "", tname); + bool forced_only = false; + if (t->type == STREAM_SUB) { + bool forced_opt = mpctx->opts->subs_rend->sub_forced_events_only; + if (forced_opt) + forced_only = t->selected; + } + APPEND(b, " %3s %-5s", t->selected ? (forced_only ? "(*)" : "(+)") : "", tname); APPEND(b, " --%s=%d", selopt, t->user_tid); if (t->lang && langopt) APPEND(b, " --%s=%s", langopt, t->lang); @@ -268,6 +272,8 @@ static void print_stream(struct MPContext *mpctx, struct track *t) APPEND(b, " (f)"); if (t->attached_picture) APPEND(b, " [P]"); + if (forced_only) + APPEND(b, " [F]"); if (t->title) APPEND(b, " '%s'", t->title); const char *codec = s ? s->codec->codec : NULL; @@ -284,7 +290,7 @@ static void print_stream(struct MPContext *mpctx, struct track *t) APPEND(b, " %dHz", s->codec->samplerate); } APPEND(b, ")"); - if (s->hls_bitrate > 0) + if (s && s->hls_bitrate > 0) APPEND(b, " (%d kbps)", (s->hls_bitrate + 500) / 1000); if (t->is_external) APPEND(b, " (external)"); @@ -353,7 +359,7 @@ void update_demuxer_properties(struct MPContext *mpctx) } talloc_free(mpctx->filtered_tags); mpctx->filtered_tags = info; - mp_notify(mpctx, MPV_EVENT_METADATA_UPDATE, NULL); + mp_notify(mpctx, MP_EVENT_METADATA_UPDATE, NULL); } if (events & DEMUX_EVENT_DURATION) mp_notify(mpctx, MP_EVENT_DURATION_UPDATE, NULL); @@ -362,7 +368,9 @@ void update_demuxer_properties(struct MPContext *mpctx) // Enables or disables the stream for the given track, according to // track->selected. -void reselect_demux_stream(struct MPContext *mpctx, struct track *track) +// With refresh_only=true, refreshes the stream if it's enabled. +void reselect_demux_stream(struct MPContext *mpctx, struct track *track, + bool refresh_only) { if (!track->stream) return; @@ -372,7 +380,10 @@ void reselect_demux_stream(struct MPContext *mpctx, struct track *track) if (track->type == STREAM_SUB) pts -= 10.0; } - demuxer_select_track(track->demuxer, track->stream, pts, track->selected); + if (refresh_only) + demuxer_refresh_track(track->demuxer, track->stream, pts); + else + demuxer_select_track(track->demuxer, track->stream, pts, track->selected); } static void enable_demux_thread(struct MPContext *mpctx, struct demuxer *demux) @@ -410,12 +421,15 @@ static struct track *add_stream_track(struct MPContext *mpctx, .user_tid = find_new_tid(mpctx, stream->type), .demuxer_id = stream->demuxer_id, .ff_index = stream->ff_index, + .hls_bitrate = stream->hls_bitrate, + .program_id = stream->program_id, .title = stream->title, .default_track = stream->default_track, .forced_track = stream->forced_track, .dependent_track = stream->dependent_track, .visual_impaired_track = stream->visual_impaired_track, .hearing_impaired_track = stream->hearing_impaired_track, + .image = stream->image, .attached_picture = stream->attached_picture != NULL, .lang = stream->lang, .demuxer = demuxer, @@ -423,7 +437,7 @@ static struct track *add_stream_track(struct MPContext *mpctx, }; MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track); - mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); + mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL); return track; } @@ -435,11 +449,14 @@ void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer) } // Result numerically higher => better match. 0 == no match. -static int match_lang(char **langs, char *lang) +static int match_lang(char **langs, const char *lang) { + if (!lang) + return 0; for (int idx = 0; langs && langs[idx]; idx++) { - if (lang && strcasecmp(langs[idx], lang) == 0) - return INT_MAX - idx; + int score = mp_match_lang_single(langs[idx], lang); + if (score > 0) + return INT_MAX - (idx + 1) * LANGUAGE_SCORE_MAX + score - 1; } return 0; } @@ -452,10 +469,12 @@ static int match_lang(char **langs, char *lang) * 0b) track is not from --external-file * 1) track is external (no_default cancels this) * 1b) track was passed explicitly (is not an auto-loaded subtitle) - * 2) earlier match in lang list + * 1c) track matches the program ID of the video + * 2) earlier match in lang list but not if we're using os_langs * 3a) track is marked forced and we're preferring forced tracks * 3b) track is marked non-forced and we're preferring non-forced tracks * 3c) track is marked default + * 3d) match in lang list with os_langs * 4) attached picture, HLS bitrate * 5) lower track number * If select_fallback is not set, 5) is only used to determine whether a @@ -464,24 +483,39 @@ static int match_lang(char **langs, char *lang) * Forced tracks are preferred when the user prefers not to display subtitles */ // Return whether t1 is preferred over t2 -static bool compare_track(struct track *t1, struct track *t2, char **langs, - int prefer_forced, struct MPOpts *opts) +static bool compare_track(struct track *t1, struct track *t2, char **langs, bool os_langs, + bool forced, struct MPOpts *opts, int preferred_program) { + bool sub = t2->type == STREAM_SUB; if (!opts->autoload_files && t1->is_external != t2->is_external) return !t1->is_external; bool ext1 = t1->is_external && !t1->no_default; bool ext2 = t2->is_external && !t2->no_default; - if (ext1 != ext2) + if (ext1 != ext2) { + if (t1->attached_picture && t2->attached_picture + && opts->audio_display == 1) + return !ext1; return ext1; + } if (t1->auto_loaded != t2->auto_loaded) return !t1->auto_loaded; + if (preferred_program != -1 && t1->program_id != -1 && t2->program_id != -1) { + if ((t1->program_id == preferred_program) != + (t2->program_id == preferred_program)) + return t1->program_id == preferred_program; + } int l1 = match_lang(langs, t1->lang), l2 = match_lang(langs, t2->lang); - if (l1 != l2) + t1->forced_select = sub && forced && t1->forced_track; + if (!os_langs && l1 != l2) return l1 > l2; - if (t1->forced_track != t2->forced_track) - return prefer_forced ? t1->forced_track : !t1->forced_track; - if (t1->default_track != t2->default_track) + if (forced) + return t1->forced_track; + if (sub && !t2->forced_select && t2->forced_track) + return !t1->forced_track; + if (t1->default_track != t2->default_track && !t2->forced_select) return t1->default_track; + if (os_langs && l1 != l2) + return l1 > l2; if (t1->attached_picture != t2->attached_picture) return !t1->attached_picture; if (t1->stream && t2->stream && opts->hls_bitrate >= 0 && @@ -508,46 +542,132 @@ static bool duplicate_track(struct MPContext *mpctx, int order, return false; } +static bool append_lang(size_t *nb, char ***out, char *in) +{ + if (!in) + return false; + MP_TARRAY_GROW(NULL, *out, *nb + 1); + (*out)[(*nb)++] = in; + (*out)[*nb] = NULL; + ta_set_parent(in, *out); + return true; +} + +static char **add_os_langs(void) +{ + size_t nb = 0; + char **out = NULL; + char **autos = mp_get_user_langs(); + for (int i = 0; autos && autos[i]; i++) { + if (!append_lang(&nb, &out, autos[i])) + goto cleanup; + } + +cleanup: + talloc_free(autos); + return out; +} + +static char **process_langs(char **in) +{ + size_t nb = 0; + char **out = NULL; + for (int i = 0; in && in[i]; i++) { + if (!append_lang(&nb, &out, talloc_strdup(NULL, in[i]))) + break; + } + return out; +} + +static const char *get_audio_lang(struct MPContext *mpctx) +{ + // If we have a single current audio track, this is simple. + if (mpctx->current_track[0][STREAM_AUDIO]) + return mpctx->current_track[0][STREAM_AUDIO]->lang; + + const char *ret = NULL; + + // Otherwise, we may be using a filter with multiple inputs. + // Iterate over the tracks and find the ones in use. + for (int i = 0; i < mpctx->num_tracks; i++) { + const struct track *t = mpctx->tracks[i]; + if (t->type != STREAM_AUDIO || !t->selected) + continue; + + // If we have input in multiple audio languages, bail out; + // we don't have a meaningful single language. + // Partial matches (e.g. en-US vs en-GB) are acceptable here. + if (ret && t->lang && !mp_match_lang_single(t->lang, ret)) + return NULL; + + // We'll return the first non-null tag we see + if (!ret) + ret = t->lang; + } + + return ret; +} + struct track *select_default_track(struct MPContext *mpctx, int order, enum stream_type type) { struct MPOpts *opts = mpctx->opts; int tid = opts->stream_id[order][type]; - char **langs = opts->stream_lang[type]; - int prefer_forced = type != STREAM_SUB || - (!opts->subs_with_matching_audio && - mpctx->current_track[0][STREAM_AUDIO] && - match_lang(langs, mpctx->current_track[0][STREAM_AUDIO]->lang)); + int preferred_program = (type != STREAM_VIDEO && mpctx->current_track[0][STREAM_VIDEO]) ? + mpctx->current_track[0][STREAM_VIDEO]->program_id : -1; if (tid == -2) return NULL; - bool select_fallback = type == STREAM_VIDEO || type == STREAM_AUDIO; + char **langs = process_langs(opts->stream_lang[type]); + bool os_langs = false; + // Try to add OS languages if enabled by the user and we don't already have a lang from slang. + if (type == STREAM_SUB && (!langs || !strcmp(langs[0], "")) && opts->subs_match_os_language) { + talloc_free(langs); + langs = add_os_langs(); + os_langs = true; + } + const char *audio_lang = get_audio_lang(mpctx); + bool sub = type == STREAM_SUB; 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 (track->user_tid == tid) { + pick = track; + goto cleanup; + } if (tid >= 0) continue; if (track->no_auto_select) continue; if (duplicate_track(mpctx, order, type, track)) continue; - if (!pick || compare_track(track, pick, langs, prefer_forced, mpctx->opts)) + if (sub) { + // Subtitle specific auto-selecting crap. + bool audio_matches = mp_match_lang_single(audio_lang, track->lang); + bool forced = track->forced_track && (opts->subs_fallback_forced == 2 || + (audio_matches && opts->subs_fallback_forced == 1)); + bool lang_match = !os_langs && match_lang(langs, track->lang) > 0; + bool subs_fallback = (track->is_external && !track->no_default) || opts->subs_fallback == 2 || + (opts->subs_fallback == 1 && track->default_track); + bool subs_matching_audio = (!match_lang(langs, audio_lang) || opts->subs_with_matching_audio == 2 || + (opts->subs_with_matching_audio == 1 && track->forced_track)); + if (subs_matching_audio && ((!pick && (forced || lang_match || subs_fallback)) || + (pick && compare_track(track, pick, langs, os_langs, forced, mpctx->opts, preferred_program)))) + { + pick = track; + } + } else if (!pick || compare_track(track, pick, langs, os_langs, false, mpctx->opts, preferred_program)) { pick = track; + } } - if (pick && !select_fallback && !(pick->is_external && !pick->no_default) - && !match_lang(langs, pick->lang) && !pick->default_track - && !pick->forced_track) - pick = NULL; + if (pick && pick->attached_picture && !mpctx->opts->audio_display) pick = NULL; if (pick && !opts->autoload_files && pick->is_external) pick = NULL; - if (pick && type == STREAM_SUB && prefer_forced && !pick->forced_track && - opts->subs_rend->forced_subs_only == -1) - opts->subs_rend->forced_subs_only_current = 1; +cleanup: + talloc_free(langs); return pick; } @@ -655,17 +775,15 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type uninit_sub(mpctx, current); if (current) { - if (current->remux_sink) - close_recorder_and_error(mpctx); current->selected = false; - reselect_demux_stream(mpctx, current); + reselect_demux_stream(mpctx, current, false); } mpctx->current_track[order][type] = track; if (track) { track->selected = true; - reselect_demux_stream(mpctx, track); + reselect_demux_stream(mpctx, track, false); } if (type == STREAM_VIDEO && order == 0) { @@ -676,7 +794,7 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type reinit_sub(mpctx, track); } - mp_notify(mpctx, MPV_EVENT_TRACK_SWITCHED, NULL); + mp_notify(mpctx, MP_EVENT_TRACK_SWITCHED, NULL); mp_wakeup_core(mpctx); talloc_free(mpctx->track_layout_hash); @@ -697,6 +815,8 @@ void mp_deselect_track(struct MPContext *mpctx, struct track *track) { if (track && track->selected) { for (int t = 0; t < num_ptracks[track->type]; t++) { + if (mpctx->current_track[t][track->type] != track) + continue; mp_switch_track_n(mpctx, t, track->type, NULL, 0); mark_track_selection(mpctx, t, track->type, -1); // default } @@ -742,7 +862,7 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track) if (!in_use) demux_cancel_and_free(d); - mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); + mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL); return true; } @@ -753,7 +873,8 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track) // cancel will generally be used to abort the loading process, but on success // the demuxer is changed to be slaved to mpctx->playback_abort instead. int mp_add_external_file(struct MPContext *mpctx, char *filename, - enum stream_type filter, struct mp_cancel *cancel) + enum stream_type filter, struct mp_cancel *cancel, + bool cover_art) { struct MPOpts *opts = mpctx->opts; if (!filename || mp_cancel_test(cancel)) @@ -827,8 +948,8 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename, t->external_filename = talloc_strdup(t, filename); t->no_default = sh->type != filter; t->no_auto_select = t->no_default; - // filter==STREAM_VIDEO always means cover art. - t->attached_picture = t->type == STREAM_VIDEO && filter == STREAM_VIDEO; + // if we found video, and we are loading cover art, flag as such. + t->attached_picture = t->type == STREAM_VIDEO && cover_art; if (first_num < 0 && (filter == STREAM_TYPE_COUNT || sh->type == filter)) first_num = mpctx->num_tracks - 1; } @@ -853,7 +974,9 @@ static void open_external_files(struct MPContext *mpctx, char **files, files = mp_dup_str_array(tmp, files); for (int n = 0; files && files[n]; n++) - mp_add_external_file(mpctx, files[n], filter, mpctx->playback_abort); + // when given filter is set to video, we are loading up cover art + mp_add_external_file(mpctx, files[n], filter, mpctx->playback_abort, + filter == STREAM_VIDEO); talloc_free(tmp); } @@ -892,15 +1015,16 @@ void autoload_external_files(struct MPContext *mpctx, struct mp_cancel *cancel) goto skip; if (e->type == STREAM_VIDEO && (sc[STREAM_VIDEO] || !sc[STREAM_AUDIO])) goto skip; - int first = mp_add_external_file(mpctx, e->fname, e->type, cancel); + + // when given filter is set to video, we are loading up cover art + int first = mp_add_external_file(mpctx, e->fname, e->type, cancel, + e->type == STREAM_VIDEO); if (first < 0) goto skip; for (int n = first; n < mpctx->num_tracks; n++) { struct track *t = mpctx->tracks[n]; t->auto_loaded = true; - t->attached_picture = - t->type == STREAM_VIDEO && e->type == STREAM_VIDEO; if (!t->lang) t->lang = talloc_strdup(t, e->lang); } @@ -942,8 +1066,6 @@ static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl, if (pl->num_entries) { prepare_playlist(mpctx, pl); struct playlist_entry *new = pl->current; - if (mpctx->playlist->current) - playlist_add_redirect(pl, mpctx->playlist->current->filename); *num_new_entries = pl->num_entries; *start_id = playlist_transfer_entries(mpctx->playlist, pl); // current entry is replaced @@ -1015,11 +1137,11 @@ static void load_per_file_options(m_config_t *conf, } } -static void *open_demux_thread(void *ctx) +static MP_THREAD_VOID open_demux_thread(void *ctx) { struct MPContext *mpctx = ctx; - mpthread_set_name("opener"); + mp_thread_set_name("opener"); struct demuxer_params p = { .force_format = mpctx->open_format, @@ -1057,7 +1179,7 @@ static void *open_demux_thread(void *ctx) atomic_store(&mpctx->open_done, true); mp_wakeup_core(mpctx); - return NULL; + MP_THREAD_RETURN(); } static void cancel_open(struct MPContext *mpctx) @@ -1066,7 +1188,7 @@ static void cancel_open(struct MPContext *mpctx) mp_cancel_trigger(mpctx->open_cancel); if (mpctx->open_active) - pthread_join(mpctx->open_thread, NULL); + mp_thread_join(mpctx->open_thread); mpctx->open_active = false; if (mpctx->open_res_demuxer) @@ -1097,7 +1219,7 @@ static void start_open(struct MPContext *mpctx, char *url, int url_flags, mpctx->open_url_flags = url_flags; mpctx->open_for_prefetch = for_prefetch && mpctx->opts->demuxer_thread; - if (pthread_create(&mpctx->open_thread, NULL, open_demux_thread, mpctx)) { + if (mp_thread_create(&mpctx->open_thread, open_demux_thread, mpctx)) { cancel_open(mpctx); return; } @@ -1158,13 +1280,35 @@ void prefetch_next(struct MPContext *mpctx) if (!mpctx->opts->prefetch_open) return; - struct playlist_entry *new_entry = mp_next_file(mpctx, +1, false, false); + struct playlist_entry *new_entry = mp_next_file(mpctx, +1, false); if (new_entry && !mpctx->open_active && new_entry->filename) { MP_VERBOSE(mpctx, "Prefetching: %s\n", new_entry->filename); start_open(mpctx, new_entry->filename, new_entry->stream_flags, true); } } +static void clear_playlist_paths(struct MPContext *mpctx) +{ + TA_FREEP(&mpctx->playlist_paths); + mpctx->playlist_paths_len = 0; +} + +static bool infinite_playlist_loading_loop(struct MPContext *mpctx, struct playlist *pl) +{ + if (pl->num_entries) { + struct playlist_entry *e = pl->entries[0]; + for (int n = 0; n < mpctx->playlist_paths_len; n++) { + if (strcmp(mpctx->playlist_paths[n], e->filename) == 0) { + clear_playlist_paths(mpctx); + return true; + } + } + } + MP_TARRAY_APPEND(mpctx, mpctx->playlist_paths, mpctx->playlist_paths_len, + talloc_strdup(mpctx->playlist_paths, mpctx->filename)); + return false; +} + // Destroy the complex filter, and remove the references to the filter pads. // (Call cleanup_deassociated_complex_filters() to close decoders/VO/AO // that are not connected anymore due to this.) @@ -1243,7 +1387,7 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit) } struct mp_lavfi *l = - mp_lavfi_create_graph(mpctx->filter_root, 0, false, NULL, graph); + mp_lavfi_create_graph(mpctx->filter_root, 0, false, NULL, NULL, graph); if (!l) goto done; mpctx->lavfi = l->f; @@ -1341,10 +1485,10 @@ done: if (mpctx->playback_initialized) { for (int n = 0; n < mpctx->num_tracks; n++) - reselect_demux_stream(mpctx, mpctx->tracks[n]); + reselect_demux_stream(mpctx, mpctx->tracks[n], false); } - mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); + mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL); return success ? 1 : -1; } @@ -1405,7 +1549,6 @@ static void load_external_opts(struct MPContext *mpctx) static void play_current_file(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; - double playback_start = -1e100; assert(mpctx->stop_play); mpctx->stop_play = 0; @@ -1430,7 +1573,7 @@ static void play_current_file(struct MPContext *mpctx) mpctx->shown_aframes = 0; mpctx->shown_vframes = 0; mpctx->last_chapter_seek = -2; - mpctx->last_chapter_pts = MP_NOPTS_VALUE; + mpctx->last_chapter_flag = false; mpctx->last_chapter = -2; mpctx->paused = false; mpctx->playing_msg_shown = false; @@ -1473,7 +1616,7 @@ static void play_current_file(struct MPContext *mpctx) mp_load_auto_profiles(mpctx); - mp_load_playback_resume(mpctx, mpctx->filename); + bool watch_later = mp_load_playback_resume(mpctx, mpctx->filename); load_per_file_options(mpctx->mconfig, mpctx->playing->params, mpctx->playing->num_params); @@ -1483,7 +1626,7 @@ static void play_current_file(struct MPContext *mpctx) handle_force_window(mpctx, false); if (mpctx->playlist->num_entries > 1 || - mpctx->playing->num_redirects) + mpctx->playing->playlist_path) MP_INFO(mpctx, "Playing: %s\n", mpctx->filename); assert(mpctx->demuxer == NULL); @@ -1512,7 +1655,15 @@ static void play_current_file(struct MPContext *mpctx) goto terminate_playback; if (mpctx->demuxer->playlist) { + if (watch_later) + mp_delete_watch_later_conf(mpctx, mpctx->filename); struct playlist *pl = mpctx->demuxer->playlist; + playlist_populate_playlist_path(pl, mpctx->filename); + if (infinite_playlist_loading_loop(mpctx, pl)) { + mpctx->stop_play = PT_STOP; + MP_ERR(mpctx, "Infinite playlist loading loop detected.\n"); + goto terminate_playback; + } transfer_playlist(mpctx, pl, &end_event.playlist_insert_id, &end_event.playlist_insert_num_entries); mp_notify_property(mpctx, "playlist"); @@ -1539,8 +1690,6 @@ static void play_current_file(struct MPContext *mpctx) if (reinit_complex_filters(mpctx, false) < 0) goto terminate_playback; - opts->subs_rend->forced_subs_only_current = (opts->subs_rend->forced_subs_only == 1) ? 1 : 0; - for (int t = 0; t < STREAM_TYPE_COUNT; t++) { for (int i = 0; i < num_ptracks[t]; i++) { struct track *sel = NULL; @@ -1583,7 +1732,7 @@ static void play_current_file(struct MPContext *mpctx) } for (int n = 0; n < mpctx->num_tracks; n++) - reselect_demux_stream(mpctx, mpctx->tracks[n]); + reselect_demux_stream(mpctx, mpctx->tracks[n], false); update_demuxer_properties(mpctx); @@ -1610,7 +1759,7 @@ static void play_current_file(struct MPContext *mpctx) if (mpctx->vo_chain && mpctx->vo_chain->is_coverart) { MP_INFO(mpctx, - "Displaying attached picture. Use --no-audio-display to prevent this.\n"); + "Displaying cover art. Use --no-audio-display to prevent this.\n"); } if (!mpctx->vo_chain) @@ -1619,8 +1768,13 @@ static void play_current_file(struct MPContext *mpctx) MP_VERBOSE(mpctx, "Starting playback...\n"); mpctx->playback_initialized = true; + mpctx->playing->playlist_prev_attempt = false; mp_notify(mpctx, MPV_EVENT_FILE_LOADED, NULL); update_screensaver_state(mpctx); + clear_playlist_paths(mpctx); + + if (watch_later) + mp_delete_watch_later_conf(mpctx, mpctx->filename); if (mpctx->max_frames == 0) { if (!mpctx->stop_play) @@ -1656,9 +1810,6 @@ static void play_current_file(struct MPContext *mpctx) update_internal_pause_state(mpctx); - open_recorder(mpctx, true); - - playback_start = mp_time_sec(); mpctx->error_playing = 0; mpctx->in_playloop = true; while (!mpctx->stop_play) @@ -1678,14 +1829,12 @@ terminate_playback: update_core_idle_state(mpctx); if (mpctx->step_frames) { - opts->pause = 1; + opts->pause = true; m_config_notify_change_opt_ptr(mpctx->mconfig, &opts->pause); } process_hooks(mpctx, "on_unload"); - close_recorder(mpctx); - // time to uninit all, except global stuff: reinit_complex_filters(mpctx, true); uninit_audio_chain(mpctx); @@ -1707,7 +1856,7 @@ terminate_playback: talloc_free(mpctx->filtered_tags); mpctx->filtered_tags = NULL; - mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); + mp_notify(mpctx, MP_EVENT_TRACKS_CHANGED, NULL); if (encode_lavc_didfail(mpctx->encode_lavc_ctx)) mpctx->stop_play = PT_ERROR; @@ -1717,6 +1866,7 @@ terminate_playback: bool nothing_played = !mpctx->shown_aframes && !mpctx->shown_vframes && mpctx->error_playing <= 0; + bool playlist_prev_continue = false; switch (mpctx->stop_play) { case PT_ERROR: case AT_END_OF_FILE: @@ -1732,10 +1882,10 @@ terminate_playback: end_event.reason = MPV_END_FILE_REASON_EOF; } if (mpctx->playing) { - // Played/paused for longer than 1 second -> ok - mpctx->playing->playback_short = - playback_start < 0 || mp_time_sec() - playback_start < 1.0; mpctx->playing->init_failed = nothing_played; + playlist_prev_continue = mpctx->playing->playlist_prev_attempt && + nothing_played; + mpctx->playing->playlist_prev_attempt = false; } break; } @@ -1751,7 +1901,6 @@ terminate_playback: mpv_error_string(end_event.error), end_event.reason); if (end_event.error == MPV_ERROR_UNKNOWN_FORMAT) MP_ERR(mpctx, "Failed to recognize file format.\n"); - MP_INFO(mpctx, "\n"); if (mpctx->playing) playlist_entry_unref(mpctx->playing); @@ -1771,25 +1920,26 @@ terminate_playback: assert(mpctx->stop_play); process_hooks(mpctx, "on_after_end_file"); + + if (playlist_prev_continue) { + struct playlist_entry *e = mp_next_file(mpctx, -1, false); + if (e) { + mp_set_playlist_entry(mpctx, e); + play_current_file(mpctx); + } + } } // Determine the next file to play. Note that if this function returns non-NULL, // it can have side-effects and mutate mpctx. // direction: -1 (previous) or +1 (next) // force: if true, don't skip playlist entries marked as failed -// mutate: if true, change loop counters struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction, - bool force, bool mutate) + bool force) { struct playlist_entry *next = playlist_get_next(mpctx->playlist, direction); - if (next && direction < 0 && !force) { - // Don't jump to files that would immediately go to next file anyway - while (next && next->playback_short) - next = playlist_entry_get_rel(next, -1); - // Always allow jumping to first file - if (!next && mpctx->opts->loop_times == 1) - next = playlist_get_first(mpctx->playlist); - } + if (next && direction < 0 && !force) + next->playlist_prev_attempt = true; if (!next && mpctx->opts->loop_times != 1) { if (direction > 0) { if (mpctx->opts->shuffle) @@ -1802,9 +1952,6 @@ struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction, } } else { next = playlist_get_last(mpctx->playlist); - // Don't jump to files that would immediately go to next file anyway - while (next && next->playback_short) - next = playlist_entry_get_rel(next, -1); } bool ignore_failures = mpctx->opts->loop_times == -2; if (!force && next && next->init_failed && !ignore_failures) { @@ -1857,7 +2004,7 @@ void mp_play_files(struct MPContext *mpctx) if (mpctx->stop_play == PT_NEXT_ENTRY || mpctx->stop_play == PT_ERROR || mpctx->stop_play == AT_END_OF_FILE) { - new_entry = mp_next_file(mpctx, +1, false, true); + new_entry = mp_next_file(mpctx, +1, false); } else if (mpctx->stop_play == PT_CURRENT_ENTRY) { new_entry = mpctx->playlist->current; } @@ -1897,86 +2044,3 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e) mpctx->stop_play = e ? PT_CURRENT_ENTRY : PT_STOP; mp_wakeup_core(mpctx); } - -static void set_track_recorder_sink(struct track *track, - struct mp_recorder_sink *sink) -{ - if (track->d_sub) - sub_set_recorder_sink(track->d_sub, sink); - if (track->dec) - track->dec->recorder_sink = sink; - track->remux_sink = sink; -} - -void close_recorder(struct MPContext *mpctx) -{ - if (!mpctx->recorder) - return; - - for (int n = 0; n < mpctx->num_tracks; n++) - set_track_recorder_sink(mpctx->tracks[n], NULL); - - mp_recorder_destroy(mpctx->recorder); - mpctx->recorder = NULL; -} - -// Like close_recorder(), but also unset the option. Intended for use on errors. -void close_recorder_and_error(struct MPContext *mpctx) -{ - close_recorder(mpctx); - talloc_free(mpctx->opts->record_file); - mpctx->opts->record_file = NULL; - m_config_notify_change_opt_ptr(mpctx->mconfig, &mpctx->opts->record_file); - MP_ERR(mpctx, "Disabling stream recording.\n"); -} - -void open_recorder(struct MPContext *mpctx, bool on_init) -{ - if (!mpctx->playback_initialized) - return; - - close_recorder(mpctx); - - char *target = mpctx->opts->record_file; - if (!target || !target[0]) - return; - - struct sh_stream **streams = NULL; - int num_streams = 0; - - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *track = mpctx->tracks[n]; - if (track->stream && track->selected && (track->d_sub || track->dec)) - MP_TARRAY_APPEND(NULL, streams, num_streams, track->stream); - } - - mpctx->recorder = mp_recorder_create(mpctx->global, mpctx->opts->record_file, - streams, num_streams); - - if (!mpctx->recorder) { - talloc_free(streams); - close_recorder_and_error(mpctx); - return; - } - - if (!on_init) - mp_recorder_mark_discontinuity(mpctx->recorder); - - int n_stream = 0; - for (int n = 0; n < mpctx->num_tracks; n++) { - struct track *track = mpctx->tracks[n]; - if (n_stream >= num_streams) - break; - // (We expect track->stream not to be reused on other tracks.) - if (track->stream == streams[n_stream]) { - struct mp_recorder_sink * sink = - mp_recorder_get_sink(mpctx->recorder, streams[n_stream]); - assert(sink); - set_track_recorder_sink(track, sink); - n_stream++; - } - } - - talloc_free(streams); -} - |