diff options
Diffstat (limited to 'player/playloop.c')
-rw-r--r-- | player/playloop.c | 197 |
1 files changed, 122 insertions, 75 deletions
diff --git a/player/playloop.c b/player/playloop.c index 7b8b65e0f0..12239d69ab 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -15,43 +15,39 @@ * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ -#include <stddef.h> -#include <stdbool.h> +#include <assert.h> #include <inttypes.h> #include <math.h> -#include <assert.h> +#include <stdbool.h> +#include <stddef.h> -#include "config.h" +#include "client.h" +#include "command.h" +#include "core.h" #include "mpv_talloc.h" +#include "screenshot.h" -#include "common/msg.h" -#include "options/options.h" +#include "audio/out/ao.h" #include "common/common.h" #include "common/encode.h" -#include "common/recorder.h" +#include "common/msg.h" +#include "common/playlist.h" #include "common/stats.h" +#include "demux/demux.h" #include "filters/f_decoder_wrapper.h" -#include "options/m_config_frontend.h" -#include "options/m_property.h" -#include "common/playlist.h" +#include "filters/filter_internal.h" #include "input/input.h" - #include "misc/dispatch.h" +#include "options/m_config_frontend.h" +#include "options/m_property.h" +#include "options/options.h" #include "osdep/terminal.h" #include "osdep/timer.h" - -#include "audio/out/ao.h" -#include "demux/demux.h" #include "stream/stream.h" #include "sub/dec_sub.h" #include "sub/osd.h" #include "video/out/vo.h" -#include "core.h" -#include "client.h" -#include "command.h" -#include "screenshot.h" - // Wait until mp_wakeup_core() is called, since the last time // mp_wait_events() was called. void mp_wait_events(struct MPContext *mpctx) @@ -79,7 +75,7 @@ void mp_set_timeout(struct MPContext *mpctx, double sleeptime) { if (mpctx->sleeptime > sleeptime) { mpctx->sleeptime = sleeptime; - int64_t abstime = mp_add_timeout(mp_time_us(), sleeptime); + int64_t abstime = mp_time_ns_add(mp_time_ns(), sleeptime); mp_dispatch_adjust_timeout(mpctx->dispatch, abstime); } } @@ -112,21 +108,25 @@ void mp_core_unlock(struct MPContext *mpctx) // Process any queued user input. static void mp_process_input(struct MPContext *mpctx) { + int processed = 0; for (;;) { mp_cmd_t *cmd = mp_input_read_cmd(mpctx->input); if (!cmd) break; run_command(mpctx, cmd, NULL, NULL, NULL); + processed = 1; } mp_set_timeout(mpctx, mp_input_get_delay(mpctx->input)); + if (processed) + mp_notify(mpctx, MP_EVENT_INPUT_PROCESSED, NULL); } double get_relative_time(struct MPContext *mpctx) { - int64_t new_time = mp_time_us(); + int64_t new_time = mp_time_ns(); int64_t delta = new_time - mpctx->last_time; mpctx->last_time = new_time; - return delta * 0.000001; + return delta * 1e-9; } void update_core_idle_state(struct MPContext *mpctx) @@ -145,6 +145,11 @@ void update_core_idle_state(struct MPContext *mpctx) } } +bool get_internal_paused(struct MPContext *mpctx) +{ + return mpctx->opts->pause || mpctx->paused_for_cache; +} + // The value passed here is the new value for mpctx->opts->pause void set_pause_state(struct MPContext *mpctx, bool user_pause) { @@ -152,16 +157,13 @@ void set_pause_state(struct MPContext *mpctx, bool user_pause) opts->pause = user_pause; - bool internal_paused = opts->pause || mpctx->paused_for_cache; + bool internal_paused = get_internal_paused(mpctx); if (internal_paused != mpctx->paused) { mpctx->paused = internal_paused; - if (mpctx->ao && mpctx->ao_chain) { - if (internal_paused) { - ao_pause(mpctx->ao); - } else { - ao_resume(mpctx->ao); - } + if (mpctx->ao) { + bool eof = mpctx->audio_status == STATUS_EOF; + ao_set_paused(mpctx->ao, internal_paused, eof); } if (mpctx->video_out) @@ -178,10 +180,6 @@ void set_pause_state(struct MPContext *mpctx, bool user_pause) } else { (void)get_relative_time(mpctx); // ignore time that passed during pause } - - // For some reason, these events are supposed to be sent even if only - // the internal pause state changed (and "pause" property didn't)... OK. - mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0); } update_core_idle_state(mpctx); @@ -199,7 +197,8 @@ void update_screensaver_state(struct MPContext *mpctx) if (!mpctx->video_out) return; - bool saver_state = !mpctx->playback_active || !mpctx->opts->stop_screensaver; + bool saver_state = (!mpctx->playback_active || !mpctx->opts->stop_screensaver) && + mpctx->opts->stop_screensaver != 2; vo_control_async(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER : VOCTRL_KILL_SCREENSAVER, NULL); } @@ -237,6 +236,10 @@ void reset_playback_state(struct MPContext *mpctx) sub_set_play_dir(t->d_sub, mpctx->play_dir); } + // May need unpause first + if (mpctx->paused_for_cache) + update_internal_pause_state(mpctx); + mpctx->hrseek_active = false; mpctx->hrseek_lastframe = false; mpctx->hrseek_backstep = false; @@ -262,8 +265,15 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) if (!mpctx->demuxer || !seek.type || seek.amount == MP_NOPTS_VALUE) return; + if (seek.type == MPSEEK_CHAPTER) { + mpctx->last_chapter_flag = false; + seek.type = MPSEEK_ABSOLUTE; + } else { + mpctx->last_chapter_seek = -2; + } + bool hr_seek_very_exact = seek.exact == MPSEEK_VERY_EXACT; - double current_time = get_current_time(mpctx); + double current_time = get_playback_time(mpctx); if (current_time == MP_NOPTS_VALUE && seek.type == MPSEEK_RELATIVE) return; if (current_time == MP_NOPTS_VALUE) @@ -288,7 +298,7 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) if (len >= 0) seek_pts = seek.amount * len; break; - default: abort(); + default: MP_ASSERT_UNREACHABLE(); } double demux_pts = seek_pts; @@ -298,10 +308,6 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) (opts->hr_seek >= 0 && seek.type == MPSEEK_ABSOLUTE) || (opts->hr_seek == 2 && (!mpctx->vo_chain || mpctx->vo_chain->is_sparse))); - if (seek.type == MPSEEK_FACTOR || seek.amount < 0 || - (seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts)) - mpctx->last_chapter_seek = -2; - // Under certain circumstances, prefer SEEK_FACTOR. if (seek.type == MPSEEK_FACTOR && !hr_seek && (mpctx->demuxer->ts_resets_possible || seek_pts == MP_NOPTS_VALUE)) @@ -369,8 +375,6 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) clear_audio_output_buffers(mpctx); reset_playback_state(mpctx); - if (mpctx->recorder) - mp_recorder_mark_discontinuity(mpctx->recorder); demux_block_reading(mpctx->demuxer, false); for (int t = 0; t < mpctx->num_tracks; t++) { @@ -415,6 +419,7 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) update_ab_loop_clip(mpctx); mpctx->current_seek = seek; + redraw_subs(mpctx); } // This combines consecutive seek requests. @@ -444,6 +449,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, case MPSEEK_ABSOLUTE: case MPSEEK_FACTOR: case MPSEEK_BACKSTEP: + case MPSEEK_CHAPTER: *seek = (struct seek_params) { .type = type, .amount = amount, @@ -455,7 +461,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, *seek = (struct seek_params){ 0 }; return; } - abort(); + MP_ASSERT_UNREACHABLE(); } void execute_queued_seek(struct MPContext *mpctx) @@ -509,24 +515,19 @@ double get_start_time(struct MPContext *mpctx, int dir) double get_current_time(struct MPContext *mpctx) { - struct demuxer *demuxer = mpctx->demuxer; - if (demuxer) { - if (mpctx->playback_pts != MP_NOPTS_VALUE) - return mpctx->playback_pts * mpctx->play_dir; - if (mpctx->last_seek_pts != MP_NOPTS_VALUE) - return mpctx->last_seek_pts; - } - return MP_NOPTS_VALUE; + if (!mpctx->demuxer) + return MP_NOPTS_VALUE; + if (mpctx->playback_pts != MP_NOPTS_VALUE) + return mpctx->playback_pts * mpctx->play_dir; + return mpctx->last_seek_pts; } double get_playback_time(struct MPContext *mpctx) { double cur = get_current_time(mpctx); - if (cur == MP_NOPTS_VALUE) - return cur; // During seeking, the time corresponds to the last seek time - apply some // cosmetics to it. - if (mpctx->playback_pts == MP_NOPTS_VALUE) { + if (cur != MP_NOPTS_VALUE && mpctx->playback_pts == MP_NOPTS_VALUE) { double length = get_time_length(mpctx); if (length >= 0) cur = MPCLAMP(cur, 0, length); @@ -586,7 +587,8 @@ int get_current_chapter(struct MPContext *mpctx) for (i = 0; i < mpctx->num_chapters; i++) if (current_pts < mpctx->chapters[i].pts) break; - return MPMAX(mpctx->last_chapter_seek, i - 1); + return mpctx->last_chapter_flag ? + mpctx->last_chapter_seek : MPMAX(mpctx->last_chapter_seek, i - 1); } char *chapter_display_name(struct MPContext *mpctx, int chapter) @@ -664,6 +666,9 @@ static void handle_osd_redraw(struct MPContext *mpctx) if (!want_redraw) return; vo_redraw(mpctx->video_out); + // Even though we just redrew, it may need to be done again for certain + // cases of subtitles on an image. + redraw_subs(mpctx); } static void clear_underruns(struct MPContext *mpctx) @@ -798,6 +803,22 @@ int get_cache_buffering_percentage(struct MPContext *mpctx) return mpctx->demuxer ? mpctx->cache_buffer : -1; } +static void handle_update_subtitles(struct MPContext *mpctx) +{ + if (mpctx->video_status == STATUS_EOF) { + update_subtitles(mpctx, mpctx->playback_pts); + return; + } + + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + if (track->type == STREAM_SUB && !track->demuxer_ready) { + update_subtitles(mpctx, mpctx->playback_pts); + break; + } + } +} + static void handle_cursor_autohide(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; @@ -846,6 +867,8 @@ static void handle_vo_events(struct MPContext *mpctx) mp_notify(mpctx, MP_EVENT_WIN_STATE, NULL); if (events & VO_EVENT_DPI) mp_notify(mpctx, MP_EVENT_WIN_STATE2, NULL); + if (events & VO_EVENT_FOCUS) + mp_notify(mpctx, MP_EVENT_FOCUS, NULL); } static void handle_sstep(struct MPContext *mpctx) @@ -894,6 +917,11 @@ static void handle_loop_file(struct MPContext *mpctx) } if (target != MP_NOPTS_VALUE) { + if (!mpctx->shown_aframes && !mpctx->shown_vframes) { + MP_WARN(mpctx, "No media data to loop.\n"); + return; + } + mpctx->stop_play = KEEP_PLAYING; set_osd_function(mpctx, OSD_FFW); mark_seek(mpctx); @@ -937,17 +965,20 @@ static void handle_keep_open(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; if (opts->keep_open && mpctx->stop_play == AT_END_OF_FILE && - (opts->keep_open == 2 || !playlist_get_next(mpctx->playlist, 1)) && - opts->loop_times == 1) + (opts->keep_open == 2 || + (!playlist_get_next(mpctx->playlist, 1) && opts->loop_times == 1))) { mpctx->stop_play = KEEP_PLAYING; if (mpctx->vo_chain) { - if (!vo_has_frame(mpctx->video_out)) // EOF not reached normally + if (!vo_has_frame(mpctx->video_out)) { // EOF not reached normally seek_to_last_frame(mpctx); + mpctx->audio_status = STATUS_EOF; + mpctx->video_status = STATUS_EOF; + } } if (opts->keep_open_pause) { - if (mpctx->ao) - ao_drain(mpctx->ao); + if (mpctx->ao && ao_is_playing(mpctx->ao)) + return; set_pause_state(mpctx, true); } } @@ -958,7 +989,7 @@ static void handle_chapter_change(struct MPContext *mpctx) int chapter = get_current_chapter(mpctx); if (chapter != mpctx->last_chapter) { mpctx->last_chapter = chapter; - mp_notify(mpctx, MPV_EVENT_CHAPTER_CHANGE, NULL); + mp_notify(mpctx, MP_EVENT_CHAPTER_CHANGE, NULL); } } @@ -1019,15 +1050,22 @@ int handle_force_window(struct MPContext *mpctx, bool force) break; } } + + // Use a 16:9 aspect ratio so that fullscreen on a 16:9 screen will not + // have vertical margins, which can lead to a different size or position + // of subtitles than with 16:9 videos. int w = 960; - int h = 480; + int h = 540; struct mp_image_params p = { .imgfmt = config_format, .w = w, .h = h, .p_w = 1, .p_h = 1, + .force_window = true, }; if (vo_reconfig(vo, &p) < 0) goto err; + struct track *track = mpctx->current_track[0][STREAM_VIDEO]; + update_content_type(mpctx, track); update_screensaver_state(mpctx); vo_set_paused(vo, true); vo_redraw(vo); @@ -1074,8 +1112,7 @@ static void handle_playback_time(struct MPContext *mpctx) } else if (mpctx->video_status == STATUS_EOF && mpctx->audio_status == STATUS_EOF) { - double apts = - mpctx->ao_chain ? mpctx->ao_chain->last_out_pts : MP_NOPTS_VALUE; + double apts = playing_audio_pts(mpctx); double vpts = mpctx->video_pts; double mpts = MP_PTS_MAX(apts, vpts); if (mpts != MP_NOPTS_VALUE) @@ -1112,16 +1149,14 @@ static void handle_playback_restart(struct MPContext *mpctx) return; } - MP_DBG(mpctx, "starting audio playback\n"); - mpctx->audio_status = STATUS_PLAYING; - fill_audio_out_buffers(mpctx); // actually play prepared buffer - mp_wakeup_core(mpctx); + audio_start_ao(mpctx); } if (!mpctx->restart_complete) { mpctx->hrseek_active = false; mpctx->restart_complete = true; mpctx->current_seek = (struct seek_params){0}; + run_command_opts(mpctx); handle_playback_time(mpctx); mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL); update_core_idle_state(mpctx); @@ -1137,16 +1172,25 @@ static void handle_playback_restart(struct MPContext *mpctx) if (opts->osd_playing_msg && opts->osd_playing_msg[0]) { char *msg = mp_property_expand_escaped_string(mpctx, opts->osd_playing_msg); - set_osd_msg(mpctx, 1, opts->osd_duration, "%s", msg); + set_osd_msg(mpctx, 1, opts->osd_playing_msg_duration ? + opts->osd_playing_msg_duration : opts->osd_duration, + "%s", msg); talloc_free(msg); } } mpctx->playing_msg_shown = true; mp_wakeup_core(mpctx); update_ab_loop_clip(mpctx); - MP_VERBOSE(mpctx, "playback restart complete @ %f, audio=%s, video=%s\n", - mpctx->playback_pts, mp_status_str(mpctx->video_status), - mp_status_str(mpctx->audio_status)); + MP_VERBOSE(mpctx, "playback restart complete @ %f, audio=%s, video=%s%s\n", + mpctx->playback_pts, mp_status_str(mpctx->audio_status), + mp_status_str(mpctx->video_status), + get_internal_paused(mpctx) ? " (paused)" : ""); + + // To avoid strange effects when using relative seeks, especially if + // there are no proper audio & video timestamps (seeks after EOF). + double length = get_time_length(mpctx); + if (mpctx->last_seek_pts != MP_NOPTS_VALUE && length >= 0) + mpctx->last_seek_pts = MPCLAMP(mpctx->last_seek_pts, 0, length); // Continuous seeks past EOF => treat as EOF instead of repeating seek. if (mpctx->seek.type == MPSEEK_RELATIVE && mpctx->seek.amount > 0 && @@ -1158,6 +1202,9 @@ static void handle_playback_restart(struct MPContext *mpctx) static void handle_eof(struct MPContext *mpctx) { + if (mpctx->seek.type) + return; // for proper keep-open operation + /* Don't quit while paused and we're displaying the last video frame. On the * other hand, if we don't have a video frame, then the user probably seeked * outside of the video, and we do want to quit. */ @@ -1179,7 +1226,7 @@ static void handle_eof(struct MPContext *mpctx) void run_playloop(struct MPContext *mpctx) { if (encode_lavc_didfail(mpctx->encode_lavc_ctx)) { - mpctx->stop_play = PT_QUIT; + mpctx->stop_play = PT_ERROR; return; } @@ -1202,8 +1249,8 @@ void run_playloop(struct MPContext *mpctx) handle_dummy_ticks(mpctx); update_osd_msg(mpctx); - if (mpctx->video_status == STATUS_EOF) - update_subtitles(mpctx, mpctx->playback_pts); + + handle_update_subtitles(mpctx); handle_each_frame_screenshot(mpctx); |