player: fix subtle idle mode differences on early program start

If the user manages to run a "loadfile x append" command before the loop
in mp_play_files() is entered, then the player could start playing
these. This isn't expected, because appending files to the playlist in
idle mode does not normally start playback. It could happen because
there is a short time window where commands are processed before the
loop is entered (such as running the command when a script is loaded).

The idle mode semantics are pretty weird: if files were provided in
advance (on the command line), then these should be played immediately.
But if idle mode was already entered, and something is appended to the
playlist using "append", i.e. without explicitly triggering playback,
then it should remain in idle mode.

Try to follow this by redefining PT_STOP to strictly mean idle mode.
Remove the playlist->current check from idle_loop(), since only the
stop_play field counts now (cf. what mp_set_playlist_entry() does).

This actually introduces the possibility that playlist->current, and
with it playlist-pos, are set to something, even though playback is not
active or being started. Previously, this was only possible during state
transitions, such as when changing playlist entries.

Very annoyingly, this means the current way MPV_EVENT_IDLE was sent
doesn't work anymore. Logically, idle mode can be "active" even if
idle_loop() was not entered yet (between the time after mp_initialize()
and before the loop in mp_play_files()). Instead of worrying about this,
redo the "idle-active" property, and deprecate the event.

See: #7543
This commit is contained in:
wm4 2020-03-21 14:01:38 +01:00
parent 2a85e8a186
commit 26ac6ead91
7 changed files with 26 additions and 22 deletions

View File

@ -33,6 +33,7 @@ API changes
::
--- mpv 0.33.0 ---
1.108 - Deprecate MPV_EVENT_IDLE
1.107 - Remove the deprecated qthelper.hpp. This was obviously not part of the
libmpv API, only an "additionally" provided helper, thus this is not
considered an API change. If you are maintaining a project that relies

View File

@ -232,7 +232,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 107)
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 108)
/**
* The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before
@ -1328,15 +1328,19 @@ typedef enum mpv_event_id {
* and might be removed in the far future.
*/
MPV_EVENT_TRACK_SWITCHED = 10,
#endif
/**
* Idle mode was entered. In this mode, no file is played, and the playback
* core waits for new commands. (The command line player normally quits
* instead of entering idle mode, unless --idle was specified. If mpv
* was started with mpv_create(), idle mode is enabled by default.)
*
* @deprecated This is equivalent to using mpv_observe_property() on the
* "idle-active" property. The event is redundant, and might be
* removed in the far future. As a further warning, this event
* is not necessarily sent at the right point anymore (at the
* start of the program), while the property behaves correctly.
*/
MPV_EVENT_IDLE = 11,
#if MPV_ENABLE_DEPRECATED
/**
* Playback was paused. This indicates the user pause state.
*

View File

@ -80,8 +80,6 @@ struct command_ctx {
// All properties, terminated with a {0} item.
struct m_property *properties;
bool is_idle;
double last_seek_time;
double last_seek_pts;
double marked_pts;
@ -1310,8 +1308,7 @@ static int mp_property_idle(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
struct command_ctx *cmd = mpctx->command_ctx;
return m_property_flag_ro(action, arg, cmd->is_idle);
return m_property_flag_ro(action, arg, mpctx->stop_play == PT_STOP);
}
static int mp_property_eof_reached(void *ctx, struct m_property *prop,
@ -6036,10 +6033,6 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
ctx->marked_pts = MP_NOPTS_VALUE;
}
if (event == MPV_EVENT_IDLE)
ctx->is_idle = true;
if (event == MPV_EVENT_START_FILE)
ctx->is_idle = false;
if (event == MPV_EVENT_END_FILE || event == MPV_EVENT_FILE_LOADED) {
// Update chapters - does nothing if something else is visible.
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);

View File

@ -43,7 +43,7 @@ enum stop_play_reason {
// also returned on unrecoverable playback errors
PT_NEXT_ENTRY, // prepare to play next entry in playlist
PT_CURRENT_ENTRY, // prepare to play mpctx->playlist->current
PT_STOP, // stop playback, or transient state when going to next
PT_STOP, // stop playback / idle mode
PT_QUIT, // stop playback, quit player
PT_ERROR, // play next playlist entry (due to an error)
};

View File

@ -1378,7 +1378,7 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->stop_play = 0;
process_hooks(mpctx, "on_before_start_file");
if (mpctx->stop_play)
if (mpctx->stop_play || !mpctx->playlist->current)
return;
mp_notify(mpctx, MPV_EVENT_START_FILE, NULL);
@ -1777,25 +1777,29 @@ void mp_play_files(struct MPContext *mpctx)
prepare_playlist(mpctx, mpctx->playlist);
for (;;) {
assert(mpctx->stop_play);
idle_loop(mpctx);
if (mpctx->stop_play == PT_QUIT)
break;
play_current_file(mpctx);
if (mpctx->playlist->current)
play_current_file(mpctx);
if (mpctx->stop_play == PT_QUIT)
break;
struct playlist_entry *new_entry = mpctx->playlist->current;
struct playlist_entry *new_entry = NULL;
if (mpctx->stop_play == PT_NEXT_ENTRY || mpctx->stop_play == PT_ERROR ||
mpctx->stop_play == AT_END_OF_FILE || mpctx->stop_play == PT_STOP)
mpctx->stop_play == AT_END_OF_FILE)
{
new_entry = mp_next_file(mpctx, +1, false, true);
} else if (mpctx->stop_play == PT_CURRENT_ENTRY) {
new_entry = mpctx->playlist->current;
}
mpctx->playlist->current = new_entry;
mpctx->playlist->current_was_replaced = false;
mpctx->stop_play = PT_STOP;
mpctx->stop_play = new_entry ? PT_NEXT_ENTRY : PT_STOP;
if (!mpctx->playlist->current && mpctx->opts->player_idle_mode < 2)
break;

View File

@ -267,7 +267,7 @@ struct MPContext *mp_create(void)
.dispatch = mp_dispatch_create(mpctx),
.playback_abort = mp_cancel_new(mpctx),
.thread_pool = mp_thread_pool_create(mpctx, 0, 1, 30),
.stop_play = PT_STOP,
.stop_play = PT_NEXT_ENTRY,
.play_dir = 1,
};
@ -407,6 +407,10 @@ int mp_initialize(struct MPContext *mpctx, char **options)
if (opts->force_vo == 2 && handle_force_window(mpctx, false) < 0)
return -1;
// Needed to properly enter _initial_ idle mode if playlist empty.
if (mpctx->opts->player_idle_mode && !mpctx->playlist->num_entries)
mpctx->stop_play = PT_STOP;
MP_STATS(mpctx, "end init");
return 0;

View File

@ -1253,9 +1253,7 @@ void idle_loop(struct MPContext *mpctx)
{
// ================= idle loop (STOP state) =========================
bool need_reinit = true;
while (mpctx->opts->player_idle_mode && !mpctx->playlist->current
&& mpctx->stop_play != PT_QUIT)
{
while (mpctx->opts->player_idle_mode && mpctx->stop_play == PT_STOP) {
if (need_reinit) {
uninit_audio_out(mpctx);
handle_force_window(mpctx, true);