summaryrefslogtreecommitdiffstats
path: root/player/playloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/playloop.c')
-rw-r--r--player/playloop.c304
1 files changed, 163 insertions, 141 deletions
diff --git a/player/playloop.c b/player/playloop.c
index 66ce211322..12239d69ab 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -15,48 +15,47 @@
* 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 "filters/f_decoder_wrapper.h"
-#include "options/m_config.h"
-#include "options/m_property.h"
+#include "common/msg.h"
#include "common/playlist.h"
+#include "common/stats.h"
+#include "demux/demux.h"
+#include "filters/f_decoder_wrapper.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)
{
mp_client_send_property_changes(mpctx);
+ stats_event(mpctx->stats, "iterations");
+
bool sleeping = mpctx->sleeptime > 0;
if (sleeping)
MP_STATS(mpctx, "start sleep");
@@ -76,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);
}
}
@@ -107,23 +106,27 @@ void mp_core_unlock(struct MPContext *mpctx)
}
// Process any queued user input.
-void mp_process_input(struct MPContext *mpctx)
+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)
@@ -142,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)
{
@@ -149,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)
@@ -175,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);
@@ -196,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);
}
@@ -229,11 +231,15 @@ void reset_playback_state(struct MPContext *mpctx)
struct track *t = mpctx->tracks[n];
// (Often, but not always, this is redundant and also done elsewhere.)
if (t->dec)
- t->dec->play_dir = mpctx->play_dir;
+ mp_decoder_wrapper_set_play_dir(t->dec, mpctx->play_dir);
if (t->d_sub)
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;
@@ -244,7 +250,7 @@ void reset_playback_state(struct MPContext *mpctx)
mpctx->restart_complete = false;
mpctx->paused_for_cache = false;
mpctx->cache_buffer = 100;
- mpctx->seek_slave = NULL;
+ mpctx->cache_update_pts = MP_NOPTS_VALUE;
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
@@ -259,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)
@@ -285,19 +298,15 @@ 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;
- bool hr_seek = opts->correct_pts && seek.exact != MPSEEK_KEYFRAME &&
- ((opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE) ||
- opts->hr_seek > 0 || seek.exact >= MPSEEK_EXACT) &&
- seek_pts != MP_NOPTS_VALUE;
-
- if (seek.type == MPSEEK_FACTOR || seek.amount < 0 ||
- (seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts))
- mpctx->last_chapter_seek = -2;
+ bool hr_seek = seek.exact != MPSEEK_KEYFRAME && seek_pts != MP_NOPTS_VALUE &&
+ (seek.exact >= MPSEEK_EXACT || opts->hr_seek == 1 ||
+ (opts->hr_seek >= 0 && seek.type == MPSEEK_ABSOLUTE) ||
+ (opts->hr_seek == 2 && (!mpctx->vo_chain || mpctx->vo_chain->is_sparse)));
// Under certain circumstances, prefer SEEK_FACTOR.
if (seek.type == MPSEEK_FACTOR && !hr_seek &&
@@ -336,6 +345,8 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
if (!mpctx->demuxer->seekable)
demux_flags |= SEEK_CACHED;
+ demux_flags |= SEEK_BLOCK;
+
if (!demux_seek(mpctx->demuxer, demux_pts, demux_flags)) {
if (!mpctx->demuxer->seekable) {
MP_ERR(mpctx, "Cannot seek in this stream.\n");
@@ -347,8 +358,6 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
mpctx->play_dir = play_dir;
// Seek external, extra files too:
- bool has_video = false;
- struct track *external_audio = NULL;
for (int t = 0; t < mpctx->num_tracks; t++) {
struct track *track = mpctx->tracks[t];
if (track->selected && track->is_external && track->demuxer) {
@@ -357,33 +366,21 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
main_new_pos += get_track_seek_offset(mpctx, track);
if (demux_flags & SEEK_FACTOR)
main_new_pos = seek_pts;
- demux_seek(track->demuxer, main_new_pos, demux_flags & SEEK_SATAN);
- if (track->type == STREAM_AUDIO && !external_audio)
- external_audio = track;
+ demux_seek(track->demuxer, main_new_pos,
+ demux_flags & (SEEK_SATAN | SEEK_BLOCK));
}
- if (track->selected && !track->is_external && track->stream &&
- track->type == STREAM_VIDEO && !track->stream->attached_picture)
- has_video = true;
}
if (!(seek.flags & MPSEEK_FLAG_NOFLUSH))
clear_audio_output_buffers(mpctx);
reset_playback_state(mpctx);
- if (mpctx->recorder)
- mp_recorder_mark_discontinuity(mpctx->recorder);
-
- // When doing keyframe seeks (hr_seek=false) backwards (no SEEK_FORWARD),
- // then video can seek before the external audio track (because video seek
- // granularity is coarser than audio). The result would be playing video with
- // silence until the audio seek target is reached. Work around by blocking
- // the demuxer (decoders can't read) and seeking to video position later.
- if (has_video && external_audio && !hr_seek && mpctx->play_dir > 0 &&
- !(demux_flags & SEEK_FORWARD))
- {
- MP_VERBOSE(mpctx, "delayed seek for aid=%d\n", external_audio->user_tid);
- demux_block_reading(external_audio->demuxer, true);
- mpctx->seek_slave = external_audio;
+
+ demux_block_reading(mpctx->demuxer, false);
+ for (int t = 0; t < mpctx->num_tracks; t++) {
+ struct track *track = mpctx->tracks[t];
+ if (track->selected && track->demuxer)
+ demux_block_reading(track->demuxer, false);
}
/* Use the target time as "current position" for further relative
@@ -422,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.
@@ -451,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,
@@ -462,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)
@@ -516,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);
@@ -580,7 +574,7 @@ double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
int get_percent_pos(struct MPContext *mpctx)
{
double pos = get_current_pos_ratio(mpctx, false);
- return pos < 0 ? -1 : pos * 100;
+ return pos < 0 ? -1 : (int)round(pos * 100);
}
// -2 is no chapters, -1 is before first chapter
@@ -593,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)
@@ -661,7 +656,7 @@ static void handle_osd_redraw(struct MPContext *mpctx)
return;
}
// Don't redraw immediately during a seek (makes it significantly slower).
- bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_coverart;
+ bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_sparse;
if (use_video && mp_time_sec() - mpctx->start_timestamp < 0.1) {
mp_set_timeout(mpctx, 0.1);
return;
@@ -671,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)
@@ -762,6 +760,8 @@ static void handle_update_cache(struct MPContext *mpctx)
// Also update cache properties.
bool busy = !s.idle;
+ if (fabs(mpctx->cache_update_pts - mpctx->playback_pts) >= 1.0)
+ busy = true;
if (busy || mpctx->next_cache_update > 0) {
if (mpctx->next_cache_update <= now) {
mpctx->next_cache_update = busy ? now + 0.25 : 0;
@@ -792,8 +792,10 @@ static void handle_update_cache(struct MPContext *mpctx)
if (s.eof && !busy)
prefetch_next(mpctx);
- if (force_update)
+ if (force_update) {
+ mpctx->cache_update_pts = mpctx->playback_pts;
mp_notify(mpctx, MP_EVENT_CACHE_UPDATE, NULL);
+ }
}
int get_cache_buffering_percentage(struct MPContext *mpctx)
@@ -801,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;
@@ -849,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)
@@ -897,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);
@@ -940,18 +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->playback_pts = mpctx->last_vo_pts;
+ 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);
}
}
@@ -962,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);
}
}
@@ -1023,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);
@@ -1075,24 +1109,14 @@ static void handle_playback_time(struct MPContext *mpctx)
mpctx->audio_status < STATUS_EOF)
{
mpctx->playback_pts = playing_audio_pts(mpctx);
- }
-}
-
-static void handle_delayed_audio_seek(struct MPContext *mpctx)
-{
- if (mpctx->seek_slave) {
- if (mpctx->video_pts != MP_NOPTS_VALUE) {
- // We know the video position now, so seek external audio to the
- // correct position.
- double pts = mpctx->video_pts +
- get_track_seek_offset(mpctx, mpctx->seek_slave);
- demux_seek(mpctx->seek_slave->demuxer, pts, 0);
- mpctx->seek_slave = NULL;
- } else if (mpctx->video_status >= STATUS_EOF) {
- // We won't get a video position; don't stall the audio stream.
- demux_block_reading(mpctx->seek_slave->demuxer, false);
- mpctx->seek_slave = NULL;
- }
+ } else if (mpctx->video_status == STATUS_EOF &&
+ mpctx->audio_status == STATUS_EOF)
+ {
+ double apts = playing_audio_pts(mpctx);
+ double vpts = mpctx->video_pts;
+ double mpts = MP_PTS_MAX(apts, vpts);
+ if (mpts != MP_NOPTS_VALUE)
+ mpctx->playback_pts = mpts;
}
}
@@ -1102,13 +1126,6 @@ static void handle_playback_restart(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- // Do not wait for video stream if it only has sparse frames.
- if (mpctx->vo_chain && mpctx->vo_chain->is_sparse &&
- mpctx->video_status < STATUS_READY)
- {
- mpctx->video_status = STATUS_READY;
- }
-
if (mpctx->audio_status < STATUS_READY ||
mpctx->video_status < STATUS_READY)
return;
@@ -1119,6 +1136,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
mpctx->video_status = STATUS_PLAYING;
get_relative_time(mpctx);
mp_wakeup_core(mpctx);
+ MP_DBG(mpctx, "starting video playback\n");
}
if (mpctx->audio_status == STATUS_READY) {
@@ -1131,23 +1149,14 @@ static void handle_playback_restart(struct MPContext *mpctx)
return;
}
- // Video needed, but not started yet -> wait.
- if (mpctx->vo_chain &&
- !mpctx->vo_chain->is_coverart &&
- !mpctx->vo_chain->is_sparse &&
- mpctx->video_status <= STATUS_READY)
- return;
-
- MP_VERBOSE(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);
@@ -1163,21 +1172,38 @@ 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\n", mpctx->playback_pts);
+ 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 &&
+ mpctx->video_status == STATUS_EOF &&
+ mpctx->audio_status == STATUS_EOF)
+ mpctx->seek = (struct seek_params){0};
}
}
static void handle_eof(struct MPContext *mpctx)
{
if (mpctx->seek.type)
- return;
+ 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
@@ -1200,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;
}
@@ -1216,8 +1242,6 @@ void run_playloop(struct MPContext *mpctx)
fill_audio_out_buffers(mpctx);
write_video(mpctx);
- handle_delayed_audio_seek(mpctx);
-
handle_playback_restart(mpctx);
handle_playback_time(mpctx);
@@ -1225,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);
@@ -1247,7 +1271,7 @@ void run_playloop(struct MPContext *mpctx)
handle_osd_redraw(mpctx);
- if (mp_filter_run(mpctx->filter_root))
+ if (mp_filter_graph_run(mpctx->filter_root))
mp_wakeup_core(mpctx);
mp_wait_events(mpctx);
@@ -1279,9 +1303,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);