0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 03:52:22 +02:00

player: separate controls for user and video controlled speed

For video sync, we want separate playback speed controls for user-
requested speed and the "correction" speed for video timing. Further, we
use this separation to make sure only a resampler is inserted if
playback speed is only changed for video sync correction.

As of this commit, this is basically inactive code. It's just
preparation for the video sync code (the following commit).
This commit is contained in:
wm4 2015-08-10 18:40:16 +02:00
parent 3d1cc17ab2
commit fedaad8250
7 changed files with 64 additions and 22 deletions

View File

@ -20,6 +20,7 @@ Interface changes
::
--- mpv 0.10.0 will be released ---
- add "audio-speed-correction" and "video-speed-correction" properties
- remove --demuxer-readahead-packets and --demuxer-readahead-bytes
add --demuxer-max-packets and --demuxer-max-bytes
(the new options are not replacement and have very different semantics)

View File

@ -826,6 +826,10 @@ Property list
``speed`` (RW)
See ``--speed``.
``audio-speed-correction``, ``video-speed-correction``
Factor multiplied with ``speed`` at which the player attempts to play the
file. Usually it's exactly 1. (Display sync mode will make this useful.)
``filename``
Currently played file, with path stripped. If this is an URL, try to undo
percent encoding as well. (The result is not necessarily correct, but

View File

@ -45,9 +45,14 @@
static int update_playback_speed_filters(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
double speed = opts->playback_speed;
double speed = mpctx->audio_speed;
struct af_stream *afs = mpctx->d_audio->afilter;
// Use pitch correction only for speed adjustments by the user, not minor
// sync correction ones.
bool use_pitch_correction = opts->pitch_correction &&
opts->playback_speed != 1.0;
// Make sure only exactly one filter changes speed; resetting them all
// and setting 1 filter is the easiest way to achieve this.
af_control_all(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &(double){1});
@ -63,7 +68,7 @@ static int update_playback_speed_filters(struct MPContext *mpctx)
return 0;
int method = AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE;
if (opts->pitch_correction)
if (use_pitch_correction)
method = AF_CONTROL_SET_PLAYBACK_SPEED;
if (!af_control_any_rev(afs, method, &speed)) {
@ -88,6 +93,8 @@ static int recreate_audio_filters(struct MPContext *mpctx)
if (update_playback_speed_filters(mpctx) < 0) {
mpctx->opts->playback_speed = 1.0;
mpctx->speed_factor_a = 1.0;
mpctx->audio_speed = 1.0;
mp_notify(mpctx, MP_EVENT_CHANGE_ALL, NULL);
}
@ -117,11 +124,20 @@ int reinit_audio_filters(struct MPContext *mpctx)
return 1;
}
void set_playback_speed(struct MPContext *mpctx, double new_speed)
// Call this if opts->playback_speed or mpctx->speed_correction changes.
void update_playback_speed(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
opts->playback_speed = new_speed;
double old_speed_factor_a = mpctx->speed_factor_a;
double old_audio_speed = mpctx->audio_speed;
mpctx->audio_speed = opts->playback_speed * mpctx->speed_factor_a;
mpctx->video_speed = opts->playback_speed * mpctx->speed_factor_v;
if (mpctx->speed_factor_a == old_speed_factor_a &&
mpctx->audio_speed == old_audio_speed)
return;
if (!mpctx->d_audio || mpctx->d_audio->afilter->initialized < 1)
return;
@ -300,7 +316,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
if (recreate_audio_filters(mpctx) < 0)
goto init_error;
set_playback_speed(mpctx, opts->playback_speed);
update_playback_speed(mpctx);
return;
@ -349,9 +365,9 @@ double written_audio_pts(struct MPContext *mpctx)
// accept everything to internal buffers yet
buffered_output += mp_audio_buffer_seconds(mpctx->ao_buffer);
// Filters divide audio length by playback_speed, so multiply by it
// Filters divide audio length by audio_speed, so multiply by it
// to get the length in original units without speedup or slowdown
a_pts -= buffered_output * mpctx->opts->playback_speed;
a_pts -= buffered_output * mpctx->audio_speed;
return a_pts +
get_track_video_offset(mpctx, mpctx->current_track[0][STREAM_AUDIO]);
@ -363,7 +379,7 @@ double playing_audio_pts(struct MPContext *mpctx)
double pts = written_audio_pts(mpctx);
if (pts == MP_NOPTS_VALUE || !mpctx->ao)
return pts;
return pts - mpctx->opts->playback_speed * ao_get_delay(mpctx->ao);
return pts - mpctx->audio_speed * ao_get_delay(mpctx->ao);
}
static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags)
@ -378,7 +394,7 @@ static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags
#endif
if (data->samples == 0)
return 0;
double real_samplerate = out_format.rate / mpctx->opts->playback_speed;
double real_samplerate = out_format.rate / mpctx->audio_speed;
int played = ao_play(mpctx->ao, data->planes, data->samples, flags);
assert(played <= data->samples);
if (played > 0) {
@ -406,7 +422,7 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / opts->playback_speed;
double play_samplerate = out_format.rate / mpctx->audio_speed;
if (!opts->initial_audio_sync) {
mpctx->audio_status = STATUS_FILLING;
@ -483,7 +499,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / opts->playback_speed;
double play_samplerate = out_format.rate / mpctx->audio_speed;
// If audio is infinitely fast, somehow try keeping approximate A/V sync.
if (mpctx->audio_status == STATUS_PLAYING && ao_untimed(mpctx->ao) &&

View File

@ -279,9 +279,8 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop,
double speed = mpctx->opts->playback_speed;
switch (action) {
case M_PROPERTY_SET: {
double new_speed = *(double *)arg;
if (speed != new_speed)
set_playback_speed(mpctx, new_speed);
mpctx->opts->playback_speed = *(double *)arg;
update_playback_speed(mpctx);
return M_PROPERTY_OK;
}
case M_PROPERTY_PRINT:
@ -291,6 +290,18 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop,
return mp_property_generic_option(mpctx, prop, action, arg);
}
static int mp_property_av_speed_correction(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
char *type = prop->priv;
switch (type[0]) {
case 'a': return m_property_double_ro(action, arg, mpctx->speed_factor_a);
case 'v': return m_property_double_ro(action, arg, mpctx->speed_factor_v);
}
abort();
}
/// filename with path (RO)
static int mp_property_path(void *ctx, struct m_property *prop,
int action, void *arg)
@ -639,7 +650,7 @@ static int mp_property_playtime_remaining(void *ctx, struct m_property *prop,
if (!time_remaining(mpctx, &remaining))
return M_PROPERTY_UNAVAILABLE;
double speed = mpctx->opts->playback_speed;
double speed = mpctx->video_speed;
return property_time(action, arg, remaining / speed);
}
@ -3305,6 +3316,8 @@ static const struct m_property mp_properties[] = {
{"loop", mp_property_generic_option},
{"loop-file", mp_property_generic_option},
{"speed", mp_property_playback_speed},
{"audio-speed-correction", mp_property_av_speed_correction, .priv = "a"},
{"video-speed-correction", mp_property_av_speed_correction, .priv = "v"},
{"filename", mp_property_filename},
{"stream-open-filename", mp_property_stream_open_filename},
{"file-size", mp_property_file_size},
@ -3536,7 +3549,7 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_TICK, "time-pos", "stream-pos", "stream-time-pos", "avsync",
"percent-pos", "time-remaining", "playtime-remaining", "playback-time",
"estimated-vf-fps", "drop-frame-count", "vo-drop-frame-count",
"total-avsync-change"),
"total-avsync-change", "audio-speed-correction", "video-speed-correction"),
E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params",
"video-format", "video-codec", "video-bitrate", "dwidth", "dheight",
"width", "height", "fps", "aspect", "vo-configured", "current-vo",

View File

@ -238,6 +238,12 @@ typedef struct MPContext {
enum playback_status video_status, audio_status;
bool restart_complete;
// Factors to multiply with opts->playback_speed to get the total audio or
// video speed (usually 1.0, but can be set to by the sync code).
double speed_factor_v, speed_factor_a;
// Redundant values set from opts->playback_speed and speed_factor_*.
// update_playback_speed() updates them from the other fields.
double audio_speed, video_speed;
bool broken_fps_header;
/* Set if audio should be timed to start with video frame after seeking,
* not set when e.g. playing cover art */
@ -362,7 +368,7 @@ double playing_audio_pts(struct MPContext *mpctx);
void fill_audio_out_buffers(struct MPContext *mpctx, double endpts);
double written_audio_pts(struct MPContext *mpctx);
void clear_audio_output_buffers(struct MPContext *mpctx);
void set_playback_speed(struct MPContext *mpctx, double new_speed);
void update_playback_speed(struct MPContext *mpctx);
void uninit_audio_out(struct MPContext *mpctx);
void uninit_audio_chain(struct MPContext *mpctx);

View File

@ -1018,6 +1018,8 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->playing_msg_shown = false;
mpctx->backstep_active = false;
mpctx->max_frames = -1;
mpctx->video_speed = mpctx->audio_speed = opts->playback_speed;
mpctx->speed_factor_a = mpctx->speed_factor_v = 1.0;
mpctx->broken_fps_header = false;
mpctx->seek = (struct seek_params){ 0 };

View File

@ -581,7 +581,7 @@ static void handle_new_frame(struct MPContext *mpctx)
mpctx->video_next_pts = pts;
mpctx->delay -= frame_time;
if (mpctx->video_status >= STATUS_PLAYING) {
mpctx->time_frame += frame_time / mpctx->opts->playback_speed;
mpctx->time_frame += frame_time / mpctx->video_speed;
adjust_sync(mpctx, pts, frame_time);
}
mpctx->dropped_frames = 0;
@ -711,7 +711,7 @@ static void update_avsync_before_frame(struct MPContext *mpctx)
{
double buffered_audio = ao_get_delay(mpctx->ao);
double predicted = mpctx->delay / opts->playback_speed +
double predicted = mpctx->delay / mpctx->video_speed +
mpctx->time_frame;
double difference = buffered_audio - predicted;
MP_STATS(mpctx, "value %f audio-diff", difference);
@ -727,7 +727,7 @@ static void update_avsync_before_frame(struct MPContext *mpctx)
buffered_audio = predicted + difference / opts->autosync;
}
mpctx->time_frame = buffered_audio - mpctx->delay / opts->playback_speed;
mpctx->time_frame = buffered_audio - mpctx->delay / mpctx->video_speed;
} else {
/* If we're more than 200 ms behind the right playback
* position, don't try to speed up display of following
@ -756,7 +756,7 @@ static void update_avsync_after_frame(struct MPContext *mpctx)
mpctx->last_av_difference = a_pos - mpctx->video_pts + opts->audio_delay;
if (mpctx->time_frame > 0)
mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed;
mpctx->last_av_difference += mpctx->time_frame * mpctx->video_speed;
if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE) {
mpctx->last_av_difference = 0;
} else if (fabs(mpctx->last_av_difference) > 0.5 && !mpctx->drop_message_shown) {
@ -968,7 +968,7 @@ void write_video(struct MPContext *mpctx, double endpts)
double diff = get_frame_duration(mpctx, 0);
if (diff >= 0) {
// expected A/V sync correction is ignored
diff /= opts->playback_speed;
diff /= mpctx->video_speed;
if (mpctx->time_frame < 0)
diff += mpctx->time_frame;
frame->duration = MPCLAMP(diff, 0, 10) * 1e6;