summaryrefslogtreecommitdiffstats
path: root/player/loadfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/loadfile.c')
-rw-r--r--player/loadfile.c428
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);
-}
-