mirror of https://github.com/mpv-player/mpv
loadfile: avoid infinite playlist loading loops
There are a number of ways one can craft a playlist file that refers to itself or cleverly goes around in a loop to other playlist files. There is obviously no use for this, but mpv spins around forever trying to load the files so you have to just SIGTERM/SIGKILL it. We can be smarter about this and attempt to detect it. The condition for detecting this is surprisingly simple: the filename of the first entry in the playlist must match a previous playlist path we stored. If we get this, we can then log an error and stop playback. If there is a "real" file loaded at any point in time, then we know it's not an infinite loop and clear out the saved playlist paths. Fixes #3967.
This commit is contained in:
parent
f19bafc0e4
commit
0b4a36476d
|
@ -275,6 +275,8 @@ typedef struct MPContext {
|
|||
struct playlist_entry *playing; // currently playing file
|
||||
char *filename; // immutable copy of playing->filename (or NULL)
|
||||
char *stream_open_filename;
|
||||
char **playlist_paths; // used strictly for playlist validation
|
||||
int playlist_paths_len;
|
||||
enum stop_play_reason stop_play;
|
||||
bool playback_initialized; // playloop can be run/is running
|
||||
int error_playing;
|
||||
|
|
|
@ -1309,6 +1309,28 @@ void prefetch_next(struct MPContext *mpctx)
|
|||
}
|
||||
}
|
||||
|
||||
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.)
|
||||
|
@ -1660,6 +1682,11 @@ static void play_current_file(struct MPContext *mpctx)
|
|||
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");
|
||||
|
@ -1766,6 +1793,7 @@ static void play_current_file(struct MPContext *mpctx)
|
|||
mpctx->playback_initialized = true;
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue