summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/af.rst7
-rw-r--r--DOCS/man/options.rst3
-rw-r--r--audio/filter/af_drop.c114
-rw-r--r--filters/f_auto_filters.c55
-rw-r--r--filters/f_output_chain.c20
-rw-r--r--filters/f_output_chain.h2
-rw-r--r--filters/filter.h1
-rw-r--r--filters/user_filters.c1
-rw-r--r--filters/user_filters.h1
-rw-r--r--player/audio.c41
-rw-r--r--player/video.c2
-rw-r--r--wscript_build.py1
12 files changed, 192 insertions, 56 deletions
diff --git a/DOCS/man/af.rst b/DOCS/man/af.rst
index aebf76431a..5c2b066a6b 100644
--- a/DOCS/man/af.rst
+++ b/DOCS/man/af.rst
@@ -220,3 +220,10 @@ Available filters are:
broken filters. In practice, these broken filters will either cause slow
A/V desync over time (with some files), or break playback completely if
you seek or start playback from the middle of a file.
+
+``drop``
+ This filter drops or repeats audio frames to adapt to playback speed. It
+ always operates on full audio frames, because it was made to handle SPDIF
+ (compressed audio passthrough). This is used automatically if the
+ ``--video-sync=display-adrop`` option is used. Do not use this filter (or
+ the given option); they are extremely low quality.
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 370723aadb..4ed5944956 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -6403,7 +6403,8 @@ Miscellaneous
video. See ``--video-sync-adrop-size``. This mode will
cause severe audio artifacts if the real monitor
refresh rate is too different from the reported or
- forced rate.
+ forced rate. Sicne mpv 0.33.0, this acts on entire audio
+ frames, instead of single samples.
:display-desync: Sync video to display, and let audio play on its own.
:desync: Sync video according to system clock, and let audio play
on its own.
diff --git a/audio/filter/af_drop.c b/audio/filter/af_drop.c
new file mode 100644
index 0000000000..724c482720
--- /dev/null
+++ b/audio/filter/af_drop.c
@@ -0,0 +1,114 @@
+#include "audio/aframe.h"
+#include "audio/format.h"
+#include "common/common.h"
+#include "filters/f_autoconvert.h"
+#include "filters/filter_internal.h"
+#include "filters/user_filters.h"
+
+struct priv {
+ double speed;
+ double diff; // amount of too many additional samples in normal speed
+ struct mp_aframe *last; // for repeating
+};
+
+static void process(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ if (!mp_pin_in_needs_data(f->ppins[1]))
+ return;
+
+ struct mp_frame frame = {0};
+
+ double last_dur = p->last ? mp_aframe_duration(p->last) : 0;
+ if (p->last && p->diff < 0 && -p->diff > last_dur / 2) {
+ MP_VERBOSE(f, "repeat\n");
+ frame = MAKE_FRAME(MP_FRAME_AUDIO, p->last);
+ p->last = NULL;
+ } else {
+ frame = mp_pin_out_read(f->ppins[0]);
+
+ if (frame.type == MP_FRAME_AUDIO) {
+ last_dur = mp_aframe_duration(frame.data);
+ p->diff -= last_dur;
+ if (p->diff > last_dur / 2) {
+ MP_VERBOSE(f, "drop\n");
+ mp_frame_unref(&frame);
+ mp_filter_internal_mark_progress(f);
+ }
+ }
+ }
+
+ if (frame.type == MP_FRAME_AUDIO) {
+ struct mp_aframe *fr = frame.data;
+ talloc_free(p->last);
+ p->last = mp_aframe_new_ref(fr);
+ mp_aframe_mul_speed(fr, p->speed);
+ p->diff += mp_aframe_duration(fr);
+ mp_aframe_set_pts(p->last, mp_aframe_end_pts(fr));
+ } else if (frame.type == MP_FRAME_EOF) {
+ TA_FREEP(&p->last);
+ }
+ mp_pin_in_write(f->ppins[1], frame);
+}
+
+static bool command(struct mp_filter *f, struct mp_filter_command *cmd)
+{
+ struct priv *p = f->priv;
+
+ switch (cmd->type) {
+ case MP_FILTER_COMMAND_SET_SPEED:
+ p->speed = cmd->speed;
+ return true;
+ }
+
+ return false;
+}
+
+static void reset(struct mp_filter *f)
+{
+ struct priv *p = f->priv;
+
+ TA_FREEP(&p->last);
+ p->diff = 0;
+}
+
+static void destroy(struct mp_filter *f)
+{
+ reset(f);
+}
+
+static const struct mp_filter_info af_drop_filter = {
+ .name = "drop",
+ .priv_size = sizeof(struct priv),
+ .process = process,
+ .command = command,
+ .reset = reset,
+ .destroy = destroy,
+};
+
+static struct mp_filter *af_drop_create(struct mp_filter *parent, void *options)
+{
+ struct mp_filter *f = mp_filter_create(parent, &af_drop_filter);
+ if (!f) {
+ talloc_free(options);
+ return NULL;
+ }
+
+ mp_filter_add_pin(f, MP_PIN_IN, "in");
+ mp_filter_add_pin(f, MP_PIN_OUT, "out");
+
+ struct priv *p = f->priv;
+ p->speed = 1.0;
+
+ return f;
+}
+
+const struct mp_user_filter_entry af_drop = {
+ .desc = {
+ .description = "Change audio speed by dropping/repeating frames",
+ .name = "drop",
+ .priv_size = sizeof(struct priv),
+ },
+ .create = af_drop_create,
+};
diff --git a/filters/f_auto_filters.c b/filters/f_auto_filters.c
index 944fe89eab..a5394dd35a 100644
--- a/filters/f_auto_filters.c
+++ b/filters/f_auto_filters.c
@@ -1,5 +1,7 @@
#include <math.h>
+#include "audio/aframe.h"
+#include "audio/format.h"
#include "common/common.h"
#include "common/msg.h"
#include "options/m_config.h"
@@ -295,7 +297,8 @@ struct mp_filter *mp_autorotate_create(struct mp_filter *parent)
struct aspeed_priv {
struct mp_subfilter sub;
- double cur_speed;
+ double cur_speed, cur_speed_drop;
+ int current_filter;
};
static void aspeed_process(struct mp_filter *f)
@@ -305,26 +308,48 @@ static void aspeed_process(struct mp_filter *f)
if (!mp_subfilter_read(&p->sub))
return;
- if (fabs(p->cur_speed - 1.0) < 1e-8) {
+ if (!p->sub.filter)
+ p->current_filter = 0;
+
+ double speed = p->cur_speed * p->cur_speed_drop;
+
+ int req_filter = 0;
+ if (fabs(speed - 1.0) >= 1e-8) {
+ req_filter = p->cur_speed_drop == 1.0 ? 1 : 2;
+ if (p->sub.frame.type == MP_FRAME_AUDIO &&
+ !af_fmt_is_pcm(mp_aframe_get_format(p->sub.frame.data)))
+ req_filter = 2;
+ }
+
+ if (req_filter != p->current_filter) {
if (p->sub.filter)
- MP_VERBOSE(f, "removing scaletempo\n");
+ MP_VERBOSE(f, "removing audio speed filter\n");
if (!mp_subfilter_drain_destroy(&p->sub))
return;
- } else if (!p->sub.filter) {
- MP_VERBOSE(f, "adding scaletempo\n");
- p->sub.filter =
- mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO, "scaletempo", NULL);
- if (!p->sub.filter) {
- MP_ERR(f, "could not create scaletempo filter\n");
- mp_subfilter_continue(&p->sub);
- return;
+
+ if (req_filter) {
+ if (req_filter == 1) {
+ MP_VERBOSE(f, "adding scaletempo\n");
+ p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO,
+ "scaletempo", NULL);
+ } else if (req_filter == 2) {
+ MP_VERBOSE(f, "adding drop\n");
+ p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO,
+ "drop", NULL);
+ }
+ if (!p->sub.filter) {
+ MP_ERR(f, "could not create filter\n");
+ mp_subfilter_continue(&p->sub);
+ return;
+ }
+ p->current_filter = req_filter;
}
}
if (p->sub.filter) {
struct mp_filter_command cmd = {
.type = MP_FILTER_COMMAND_SET_SPEED,
- .speed = p->cur_speed,
+ .speed = speed,
};
mp_filter_command(p->sub.filter, &cmd);
}
@@ -341,6 +366,11 @@ static bool aspeed_command(struct mp_filter *f, struct mp_filter_command *cmd)
return true;
}
+ if (cmd->type == MP_FILTER_COMMAND_SET_SPEED_DROP) {
+ p->cur_speed_drop = cmd->speed;
+ return true;
+ }
+
if (cmd->type == MP_FILTER_COMMAND_IS_ACTIVE) {
cmd->is_active = !!p->sub.filter;
return true;
@@ -381,6 +411,7 @@ struct mp_filter *mp_autoaspeed_create(struct mp_filter *parent)
struct aspeed_priv *p = f->priv;
p->cur_speed = 1.0;
+ p->cur_speed_drop = 1.0;
p->sub.in = mp_filter_add_pin(f, MP_PIN_IN, "in");
p->sub.out = mp_filter_add_pin(f, MP_PIN_OUT, "out");
diff --git a/filters/f_output_chain.c b/filters/f_output_chain.c
index cf16dcbf47..468bfe6466 100644
--- a/filters/f_output_chain.c
+++ b/filters/f_output_chain.c
@@ -454,13 +454,12 @@ bool mp_output_chain_command(struct mp_output_chain *c, const char *target,
// supports it, reset *speed, then keep setting the speed on the other filters.
// The purpose of this is to make sure only 1 filter changes speed.
static void set_speed_any(struct mp_user_filter **filters, int num_filters,
- bool resample, double *speed)
+ int command, double *speed)
{
for (int n = num_filters - 1; n >= 0; n--) {
assert(*speed);
struct mp_filter_command cmd = {
- .type = resample ? MP_FILTER_COMMAND_SET_SPEED_RESAMPLE
- : MP_FILTER_COMMAND_SET_SPEED,
+ .type = command,
.speed = *speed,
};
if (mp_filter_command(filters[n]->f, &cmd))
@@ -469,17 +468,24 @@ static void set_speed_any(struct mp_user_filter **filters, int num_filters,
}
void mp_output_chain_set_audio_speed(struct mp_output_chain *c,
- double speed, double resample)
+ double speed, double resample, double drop)
{
struct chain *p = c->f->priv;
// We always resample with the final libavresample instance.
- set_speed_any(p->post_filters, p->num_post_filters, true, &resample);
+ set_speed_any(p->post_filters, p->num_post_filters,
+ MP_FILTER_COMMAND_SET_SPEED_RESAMPLE, &resample);
// If users have filters like "scaletempo" insert anywhere, use that,
// otherwise use the builtin ones.
- set_speed_any(p->user_filters, p->num_user_filters, false, &speed);
- set_speed_any(p->post_filters, p->num_post_filters, false, &speed);
+ set_speed_any(p->user_filters, p->num_user_filters,
+ MP_FILTER_COMMAND_SET_SPEED, &speed);
+ set_speed_any(p->post_filters, p->num_post_filters,
+ MP_FILTER_COMMAND_SET_SPEED, &speed);
+ set_speed_any(p->user_filters, p->num_user_filters,
+ MP_FILTER_COMMAND_SET_SPEED_DROP, &drop);
+ set_speed_any(p->post_filters, p->num_post_filters,
+ MP_FILTER_COMMAND_SET_SPEED_DROP, &drop);
}
double mp_output_get_measured_total_delay(struct mp_output_chain *c)
diff --git a/filters/f_output_chain.h b/filters/f_output_chain.h
index 980b117d00..f06769cdd9 100644
--- a/filters/f_output_chain.h
+++ b/filters/f_output_chain.h
@@ -77,7 +77,7 @@ bool mp_output_chain_update_filters(struct mp_output_chain *p,
// Desired audio speed, with resample being strict resampling.
void mp_output_chain_set_audio_speed(struct mp_output_chain *p,
- double speed, double resample);
+ double speed, double resample, double drop);
// Total delay incurred by the filter chain, as measured by the recent filtered
// frames. The intention is that this sums the measured delays for each filter,
diff --git a/filters/filter.h b/filters/filter.h
index ddd3f27c3c..34cfcf0f54 100644
--- a/filters/filter.h
+++ b/filters/filter.h
@@ -364,6 +364,7 @@ enum mp_filter_command_type {
MP_FILTER_COMMAND_GET_META,
MP_FILTER_COMMAND_SET_SPEED,
MP_FILTER_COMMAND_SET_SPEED_RESAMPLE,
+ MP_FILTER_COMMAND_SET_SPEED_DROP,
MP_FILTER_COMMAND_IS_ACTIVE,
};
diff --git a/filters/user_filters.c b/filters/user_filters.c
index 72a2ab892c..57021adc7c 100644
--- a/filters/user_filters.c
+++ b/filters/user_filters.c
@@ -39,6 +39,7 @@ const struct mp_user_filter_entry *af_list[] = {
&af_rubberband,
#endif
&af_lavcac3enc,
+ &af_drop,
};
static bool get_af_desc(struct m_obj_desc *dst, int index)
diff --git a/filters/user_filters.h b/filters/user_filters.h
index a79e17030b..cecf0b52cd 100644
--- a/filters/user_filters.h
+++ b/filters/user_filters.h
@@ -24,6 +24,7 @@ extern const struct mp_user_filter_entry af_scaletempo;
extern const struct mp_user_filter_entry af_format;
extern const struct mp_user_filter_entry af_rubberband;
extern const struct mp_user_filter_entry af_lavcac3enc;
+extern const struct mp_user_filter_entry af_drop;
extern const struct mp_user_filter_entry vf_lavfi;
extern const struct mp_user_filter_entry vf_lavfi_bridge;
diff --git a/player/audio.c b/player/audio.c
index cde444ffb4..7bc1b8ac86 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -54,13 +54,19 @@ static void update_speed_filters(struct MPContext *mpctx)
double speed = mpctx->opts->playback_speed;
double resample = mpctx->speed_factor_a;
+ double drop = 1.0;
if (!mpctx->opts->pitch_correction) {
resample *= speed;
speed = 1.0;
}
- mp_output_chain_set_audio_speed(ao_c->filter, speed, resample);
+ if (mpctx->display_sync_active && mpctx->opts->video_sync == VS_DISP_ADROP) {
+ drop *= speed * resample;
+ resample = speed = 1.0;
+ }
+
+ mp_output_chain_set_audio_speed(ao_c->filter, speed, resample, drop);
}
static int recreate_audio_filters(struct MPContext *mpctx)
@@ -878,24 +884,6 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
playsize = MPMAX(1, playsize + skip); // silence will be prepended
}
- int skip_duplicate = 0; // >0: skip, <0: duplicate
- double drop_limit =
- (opts->sync_max_audio_change + opts->sync_max_video_change) / 100;
- if (mpctx->display_sync_active && opts->video_sync == VS_DISP_ADROP &&
- fabs(mpctx->last_av_difference) >= opts->sync_audio_drop_size &&
- mpctx->audio_drop_throttle < drop_limit &&
- mpctx->audio_status == STATUS_PLAYING)
- {
- int samples = ceil(opts->sync_audio_drop_size * play_samplerate);
- samples = (samples + align / 2) / align * align;
-
- skip_duplicate = mpctx->last_av_difference >= 0 ? -samples : samples;
-
- playsize = MPMAX(playsize, samples);
-
- mpctx->audio_drop_throttle += 1 - drop_limit - samples / play_samplerate;
- }
-
playsize = playsize / align * align;
int status = mpctx->audio_status >= STATUS_DRAINING ? AD_EOF : AD_OK;
@@ -940,21 +928,6 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
end_sync = true;
}
- if (skip_duplicate) {
- int max = mp_audio_buffer_samples(ao_c->ao_buffer);
- if (abs(skip_duplicate) > max)
- skip_duplicate = skip_duplicate >= 0 ? max : -max;
- mpctx->last_av_difference += skip_duplicate / play_samplerate;
- if (skip_duplicate >= 0) {
- mp_audio_buffer_skip(ao_c->ao_buffer, skip_duplicate);
- MP_STATS(mpctx, "drop-audio");
- } else {
- mp_audio_buffer_duplicate(ao_c->ao_buffer, -skip_duplicate);
- MP_STATS(mpctx, "duplicate-audio");
- }
- MP_VERBOSE(mpctx, "audio skip_duplicate=%d\n", skip_duplicate);
- }
-
if (mpctx->audio_status == STATUS_SYNCING) {
if (end_sync)
mpctx->audio_status = STATUS_FILLING;
diff --git a/player/video.c b/player/video.c
index 5102ddf656..b82999f403 100644
--- a/player/video.c
+++ b/player/video.c
@@ -898,7 +898,7 @@ static void handle_display_sync_frame(struct MPContext *mpctx,
mpctx->past_frames[0].num_vsyncs = num_vsyncs;
mpctx->past_frames[0].av_diff = mpctx->last_av_difference;
- if (resample) {
+ if (resample || mode == VS_DISP_ADROP) {
adjust_audio_resample_speed(mpctx, vsync);
} else {
mpctx->speed_factor_a = 1.0;
diff --git a/wscript_build.py b/wscript_build.py
index a35d1039e5..34398c10cd 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -235,6 +235,7 @@ def build(ctx):
( "audio/chmap_sel.c" ),
( "audio/decode/ad_lavc.c" ),
( "audio/decode/ad_spdif.c" ),
+ ( "audio/filter/af_drop.c" ),
( "audio/filter/af_format.c" ),
( "audio/filter/af_lavcac3enc.c" ),
( "audio/filter/af_rubberband.c", "rubberband" ),