summaryrefslogtreecommitdiffstats
path: root/player/playloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/playloop.c')
-rw-r--r--player/playloop.c197
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);