From 65db3291b327797ac7d09ba2ea36db9ba5383f18 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 28 Oct 2014 16:19:07 +0100 Subject: client API: better error reporting Give somewhat more information on playback failure. --- DOCS/client-api-changes.rst | 2 ++ libmpv/client.h | 39 +++++++++++++++++++++++++++++++- player/audio.c | 1 + player/client.c | 5 +++++ player/core.h | 3 ++- player/loadfile.c | 55 ++++++++++++++++++++++++++++----------------- player/misc.c | 7 ++++-- player/video.c | 5 ++++- 8 files changed, 92 insertions(+), 25 deletions(-) diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst index b6b5dbc6ca..e868d554d3 100644 --- a/DOCS/client-api-changes.rst +++ b/DOCS/client-api-changes.rst @@ -26,6 +26,8 @@ API changes :: 1.9 - add enum mpv_end_file_reason for mpv_event_end_file.reason + - add MPV_END_FILE_REASON_ERROR and the mpv_event_end_file.error field + for slightly better error reporting on playback failure 1.8 - add qthelper.hpp 1.7 - add mpv_command_node(), mpv_command_node_async() 1.6 - modify "core-idle" property behavior diff --git a/libmpv/client.h b/libmpv/client.h index ed99715857..bc594cee6a 100644 --- a/libmpv/client.h +++ b/libmpv/client.h @@ -246,7 +246,30 @@ typedef enum mpv_error { /** * General error when running a command with mpv_command and similar. */ - MPV_ERROR_COMMAND = -12 + MPV_ERROR_COMMAND = -12, + /** + * Generic error on loading (used with mpv_event_end_file.error). + */ + MPV_ERROR_LOADING_FAILED = -13, + /** + * Initializing the audio output failed. + */ + MPV_ERROR_AO_INIT_FAILED = -14, + /** + * Initializing the video output failed. + */ + MPV_ERROR_VO_INIT_FAILED = -15, + /** + * There was no audio or video data to play. This also happens if the + * file was recognized, but did not contain any audio or video streams, + * or no streams were selected. + */ + MPV_ERROR_NOTHING_TO_PLAY = -16, + /** + * When trying to load the file, the file format could not be determined, + * or the file was too broken to open it. + */ + MPV_ERROR_UNKNOWN_FORMAT = -17 } mpv_error; /** @@ -1131,6 +1154,14 @@ typedef enum mpv_end_file_reason { * Playback was stopped by the quit command or player shutdown. */ MPV_END_FILE_REASON_QUIT = 3, + /** + * Some kind of error happened that lead to playback abort. Does not + * necessarily happen on incomplete or broken files (in these cases, both + * MPV_END_FILE_REASON_ERROR or MPV_END_FILE_REASON_EOF are possible). + * + * mpv_event_end_file.error will be set. + */ + MPV_END_FILE_REASON_ERROR = 4, } mpv_end_file_reason; typedef struct mpv_event_end_file { @@ -1141,6 +1172,12 @@ typedef struct mpv_event_end_file { * Unknown values should be treated as unknown. */ int reason; + /** + * If reason==MPV_END_FILE_REASON_ERROR, this contains a mpv error code + * (one of MPV_ERROR_...) giving an approximate reason why playback + * failed. In other cases, this field is 0 (no error). + */ + int error; } mpv_event_end_file; typedef struct mpv_event_script_input_dispatch { diff --git a/player/audio.c b/player/audio.c index 6774d415c3..8274399f67 100644 --- a/player/audio.c +++ b/player/audio.c @@ -255,6 +255,7 @@ void reinit_audio_chain(struct MPContext *mpctx) struct ao *ao = mpctx->ao; if (!ao) { MP_ERR(mpctx, "Could not open/initialize audio device -> no sound.\n"); + mpctx->error_playing = MPV_ERROR_AO_INIT_FAILED; goto init_error; } diff --git a/player/client.c b/player/client.c index 8376756a9a..2308555a1e 100644 --- a/player/client.c +++ b/player/client.c @@ -1479,6 +1479,11 @@ static const char *const err_table[] = { [-MPV_ERROR_PROPERTY_UNAVAILABLE] = "property unavailable", [-MPV_ERROR_PROPERTY_ERROR] = "error accessing property", [-MPV_ERROR_COMMAND] = "error running command", + [-MPV_ERROR_LOADING_FAILED] = "loading failed", + [-MPV_ERROR_AO_INIT_FAILED] = "audio output initialization failed", + [-MPV_ERROR_VO_INIT_FAILED] = "audio output initialization failed", + [-MPV_ERROR_NOTHING_TO_PLAY] = "the file has no audio or video data", + [-MPV_ERROR_UNKNOWN_FORMAT] = "unrecognized file format", }; const char *mpv_error_string(int error) diff --git a/player/core.h b/player/core.h index 9b9873e610..32c6b45db6 100644 --- a/player/core.h +++ b/player/core.h @@ -38,6 +38,7 @@ enum stop_play_reason { PT_STOP, // stop playback, clear playlist PT_RELOAD_DEMUXER, // restart playback, but keep stream open PT_QUIT, // stop playback, quit player + PT_ERROR, // play next playlist entry (due to an error) }; enum exit_reason { @@ -186,7 +187,7 @@ typedef struct MPContext { enum exit_reason quit_player_rc; int quit_custom_rc; bool has_quit_custom_rc; - bool error_playing; + int error_playing; char **resume_defaults; int64_t shown_vframes, shown_aframes; diff --git a/player/loadfile.c b/player/loadfile.c index 58b9cf9f68..46f41644c1 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -877,6 +877,7 @@ static void play_current_file(struct MPContext *mpctx) mp_cancel_reset(mpctx->playback_abort); + mpctx->error_playing = MPV_ERROR_LOADING_FAILED; mpctx->stop_play = 0; mpctx->filename = NULL; mpctx->shown_aframes = 0; @@ -973,6 +974,7 @@ goto_reopen_demuxer: ; mpctx->demuxer = open_demux_async(mpctx, mpctx->stream); if (!mpctx->demuxer) { MP_ERR(mpctx, "Failed to recognize file format.\n"); + mpctx->error_playing = MPV_ERROR_UNKNOWN_FORMAT; goto terminate_playback; } mpctx->master_demuxer = mpctx->demuxer; @@ -987,6 +989,7 @@ goto_reopen_demuxer: ; for (struct playlist_entry *e = pl->first; e; e = e->next) e->stream_flags |= entry_stream_flags; transfer_playlist(mpctx, pl); + mpctx->error_playing = 0; goto terminate_playback; } @@ -1092,6 +1095,7 @@ goto_reopen_demuxer: ; if (demux_stream_control(d, STREAM_CTRL_DVB_STEP_CHANNEL, &dir) > 0) mpctx->stop_play = PT_RELOAD_DEMUXER; } + mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY; goto terminate_playback; } @@ -1099,6 +1103,7 @@ goto_reopen_demuxer: ; if (mpctx->max_frames == 0) { mpctx->stop_play = PT_NEXT_ENTRY; + mpctx->error_playing = 0; goto terminate_playback; } @@ -1124,7 +1129,7 @@ goto_reopen_demuxer: ; mp_notify(mpctx, MPV_EVENT_FILE_LOADED, NULL); playback_start = mp_time_sec(); - mpctx->error_playing = false; + mpctx->error_playing = 0; while (!mpctx->stop_play) run_playloop(mpctx); @@ -1173,18 +1178,33 @@ terminate_playback: mpctx->playback_initialized = false; - if (mpctx->playing && mpctx->stop_play == AT_END_OF_FILE) { - // 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 = - mpctx->shown_aframes == 0 && mpctx->shown_vframes == 0; - } - mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL); struct mpv_event_end_file end_event = {0}; switch (mpctx->stop_play) { - case AT_END_OF_FILE: end_event.reason = MPV_END_FILE_REASON_EOF; break; + case PT_ERROR: + case AT_END_OF_FILE: + { + if (mpctx->error_playing >= 0 && + mpctx->shown_aframes == 0 && mpctx->shown_vframes == 0) + { + mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY; + } + end_event.error = mpctx->error_playing; + if (end_event.error < 0) { + end_event.reason = MPV_END_FILE_REASON_ERROR; + } else { + 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 = + end_event.error == MPV_ERROR_NOTHING_TO_PLAY; + } + break; + } + // Note that error_playing is meaningless in these cases. case PT_NEXT_ENTRY: case PT_CURRENT_ENTRY: case PT_STOP: end_event.reason = MPV_END_FILE_REASON_STOP; break; @@ -1259,9 +1279,8 @@ void mp_play_files(struct MPContext *mpctx) if (mpctx->stop_play == PT_QUIT) break; - mpctx->error_playing = true; play_current_file(mpctx); - if (mpctx->error_playing) { + if (mpctx->error_playing < 0) { if (!mpctx->quit_player_rc) { mpctx->quit_player_rc = EXIT_NOTPLAYED; } else if (mpctx->quit_player_rc == EXIT_PLAYED) { @@ -1275,15 +1294,11 @@ void mp_play_files(struct MPContext *mpctx) if (mpctx->stop_play == PT_QUIT) break; - if (!mpctx->stop_play || mpctx->stop_play == AT_END_OF_FILE) - mpctx->stop_play = PT_NEXT_ENTRY; - - struct playlist_entry *new_entry = NULL; - - if (mpctx->stop_play == PT_NEXT_ENTRY) { + struct playlist_entry *new_entry = mpctx->playlist->current; + if (mpctx->stop_play == PT_NEXT_ENTRY || mpctx->stop_play == PT_ERROR || + mpctx->stop_play == AT_END_OF_FILE || !mpctx->stop_play) + { new_entry = mp_next_file(mpctx, +1, false); - } else { - new_entry = mpctx->playlist->current; } mpctx->playlist->current = new_entry; diff --git a/player/misc.c b/player/misc.c index 5355e23f41..c178ec7b39 100644 --- a/player/misc.c +++ b/player/misc.c @@ -191,8 +191,11 @@ void error_on_track(struct MPContext *mpctx, struct track *track) MP_INFO(mpctx, "Video: no video\n"); if (!mpctx->current_track[0][STREAM_AUDIO] && !mpctx->current_track[0][STREAM_VIDEO]) - mpctx->stop_play = PT_NEXT_ENTRY; - mpctx->error_playing = true; + { + mpctx->stop_play = PT_ERROR; + if (mpctx->error_playing >= 0) + mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY; + } mpctx->sleeptime = 0; } } diff --git a/player/video.c b/player/video.c index db33406d63..7e4de2482f 100644 --- a/player/video.c +++ b/player/video.c @@ -273,6 +273,7 @@ int reinit_video_chain(struct MPContext *mpctx) if (!mpctx->video_out) { MP_FATAL(mpctx, "Error opening/initializing " "the selected video_out (-vo) device.\n"); + mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED; goto err_out; } mpctx->mouse_cursor_visible = true; @@ -766,8 +767,10 @@ void write_video(struct MPContext *mpctx, double endpts) MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description); int vo_r = vo_reconfig(vo, &p, 0); - if (vo_r < 0) + if (vo_r < 0) { + mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED; goto error; + } init_vo(mpctx); mpctx->time_frame = 0; // display immediately } -- cgit v1.2.3