diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/aconverter.c | 653 | ||||
-rw-r--r-- | audio/aconverter.h | 41 | ||||
-rw-r--r-- | audio/aframe.c | 130 | ||||
-rw-r--r-- | audio/aframe.h | 11 | ||||
-rw-r--r-- | audio/audio.c | 625 | ||||
-rw-r--r-- | audio/audio.h | 102 | ||||
-rw-r--r-- | audio/filter/af.c | 824 | ||||
-rw-r--r-- | audio/filter/af.h | 163 | ||||
-rw-r--r-- | audio/filter/af_format.c | 173 | ||||
-rw-r--r-- | audio/filter/af_lavcac3enc.c | 504 | ||||
-rw-r--r-- | audio/filter/af_lavfi.c | 413 | ||||
-rw-r--r-- | audio/filter/af_lavrresample.c | 187 | ||||
-rw-r--r-- | audio/filter/af_rubberband.c | 446 | ||||
-rw-r--r-- | audio/filter/af_scaletempo.c | 577 | ||||
-rw-r--r-- | audio/filter/tools.c | 72 | ||||
-rw-r--r-- | audio/format.c | 2 | ||||
-rw-r--r-- | audio/format.h | 1 |
17 files changed, 1119 insertions, 3805 deletions
diff --git a/audio/aconverter.c b/audio/aconverter.c deleted file mode 100644 index 2475df878d..0000000000 --- a/audio/aconverter.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <libavutil/opt.h> -#include <libavutil/common.h> -#include <libavutil/samplefmt.h> -#include <libavutil/channel_layout.h> -#include <libavutil/mathematics.h> - -#include "config.h" - -#include "common/common.h" -#include "common/av_common.h" -#include "common/msg.h" -#include "options/m_config.h" -#include "options/m_option.h" -#include "aconverter.h" -#include "aframe.h" -#include "fmt-conversion.h" -#include "format.h" - -#define HAVE_LIBSWRESAMPLE (!HAVE_LIBAV) -#define HAVE_LIBAVRESAMPLE HAVE_LIBAV - -#if HAVE_LIBAVRESAMPLE -#include <libavresample/avresample.h> -#elif HAVE_LIBSWRESAMPLE -#include <libswresample/swresample.h> -#define AVAudioResampleContext SwrContext -#define avresample_alloc_context swr_alloc -#define avresample_open swr_init -#define avresample_close(x) do { } while(0) -#define avresample_free swr_free -#define avresample_available(x) 0 -#define avresample_convert(ctx, out, out_planesize, out_samples, in, in_planesize, in_samples) \ - swr_convert(ctx, out, out_samples, (const uint8_t**)(in), in_samples) -#define avresample_set_channel_mapping swr_set_channel_mapping -#define avresample_set_compensation swr_set_compensation -#else -#error "config.h broken or no resampler found" -#endif - -struct mp_aconverter { - struct mp_log *log; - struct mpv_global *global; - double playback_speed; - bool is_resampling; - bool passthrough_mode; - struct AVAudioResampleContext *avrctx; - struct mp_aframe *avrctx_fmt; // output format of avrctx - struct mp_aframe *pool_fmt; // format used to allocate frames for avrctx output - struct mp_aframe *pre_out_fmt; // format before final conversion - struct AVAudioResampleContext *avrctx_out; // for output channel reordering - const struct mp_resample_opts *opts; // opts requested by the user - // At least libswresample keeps a pointer around for this: - int reorder_in[MP_NUM_CHANNELS]; - int reorder_out[MP_NUM_CHANNELS]; - struct mp_aframe_pool *reorder_buffer; - struct mp_aframe_pool *out_pool; - - int in_rate_user; // user input sample rate - int in_rate; // actual rate (used by lavr), adjusted for playback speed - int in_format; - struct mp_chmap in_channels; - int out_rate; - int out_format; - struct mp_chmap out_channels; - - struct mp_aframe *input; // queued input frame - bool input_eof; // queued input EOF - struct mp_aframe *output; // queued output frame - bool output_eof; // queued output EOF -}; - -#define OPT_BASE_STRUCT struct mp_resample_opts -const struct m_sub_options resample_config = { - .opts = (const m_option_t[]) { - OPT_INTRANGE("audio-resample-filter-size", filter_size, 0, 0, 32), - OPT_INTRANGE("audio-resample-phase-shift", phase_shift, 0, 0, 30), - OPT_FLAG("audio-resample-linear", linear, 0), - OPT_DOUBLE("audio-resample-cutoff", cutoff, M_OPT_RANGE, - .min = 0, .max = 1), - OPT_FLAG("audio-normalize-downmix", normalize, 0), - OPT_KEYVALUELIST("audio-swresample-o", avopts, 0), - {0} - }, - .size = sizeof(struct mp_resample_opts), - .defaults = &(const struct mp_resample_opts)MP_RESAMPLE_OPTS_DEF, - .change_flags = UPDATE_AUDIO, -}; - -#if HAVE_LIBAVRESAMPLE -static double get_delay(struct mp_aconverter *p) -{ - return avresample_get_delay(p->avrctx) / (double)p->in_rate + - avresample_available(p->avrctx) / (double)p->out_rate; -} -static int get_out_samples(struct mp_aconverter *p, int in_samples) -{ - return avresample_get_out_samples(p->avrctx, in_samples); -} -#else -static double get_delay(struct mp_aconverter *p) -{ - int64_t base = p->in_rate * (int64_t)p->out_rate; - return swr_get_delay(p->avrctx, base) / (double)base; -} -static int get_out_samples(struct mp_aconverter *p, int in_samples) -{ - return swr_get_out_samples(p->avrctx, in_samples); -} -#endif - -static void close_lavrr(struct mp_aconverter *p) -{ - if (p->avrctx) - avresample_close(p->avrctx); - avresample_free(&p->avrctx); - if (p->avrctx_out) - avresample_close(p->avrctx_out); - avresample_free(&p->avrctx_out); - - TA_FREEP(&p->pre_out_fmt); - TA_FREEP(&p->avrctx_fmt); - TA_FREEP(&p->pool_fmt); -} - -static int rate_from_speed(int rate, double speed) -{ - return lrint(rate * speed); -} - -static struct mp_chmap fudge_pairs[][2] = { - {MP_CHMAP2(BL, BR), MP_CHMAP2(SL, SR)}, - {MP_CHMAP2(SL, SR), MP_CHMAP2(BL, BR)}, - {MP_CHMAP2(SDL, SDR), MP_CHMAP2(SL, SR)}, - {MP_CHMAP2(SL, SR), MP_CHMAP2(SDL, SDR)}, -}; - -// Modify out_layout and return the new value. The intention is reducing the -// loss libswresample's rematrixing will cause by exchanging similar, but -// strictly speaking incompatible channel pairs. For example, 7.1 should be -// changed to 7.1(wide) without dropping the SL/SR channels. (We still leave -// it to libswresample to create the remix matrix.) -static uint64_t fudge_layout_conversion(struct mp_aconverter *p, - uint64_t in, uint64_t out) -{ - for (int n = 0; n < MP_ARRAY_SIZE(fudge_pairs); n++) { - uint64_t a = mp_chmap_to_lavc(&fudge_pairs[n][0]); - uint64_t b = mp_chmap_to_lavc(&fudge_pairs[n][1]); - if ((in & a) == a && (in & b) == 0 && - (out & a) == 0 && (out & b) == b) - { - out = (out & ~b) | a; - - MP_VERBOSE(p, "Fudge: %s -> %s\n", - mp_chmap_to_str(&fudge_pairs[n][0]), - mp_chmap_to_str(&fudge_pairs[n][1])); - } - } - return out; -} - -// mp_chmap_get_reorder() performs: -// to->speaker[n] = from->speaker[src[n]] -// but libavresample does: -// to->speaker[dst[n]] = from->speaker[n] -static void transpose_order(int *map, int num) -{ - int nmap[MP_NUM_CHANNELS] = {0}; - for (int n = 0; n < num; n++) { - for (int i = 0; i < num; i++) { - if (map[n] == i) - nmap[i] = n; - } - } - memcpy(map, nmap, sizeof(nmap)); -} - -static bool configure_lavrr(struct mp_aconverter *p, bool verbose) -{ - close_lavrr(p); - - p->in_rate = rate_from_speed(p->in_rate_user, p->playback_speed); - - p->passthrough_mode = p->opts->allow_passthrough && - p->in_rate == p->out_rate && - p->in_format == p->out_format && - mp_chmap_equals(&p->in_channels, &p->out_channels); - - if (p->passthrough_mode) - return true; - - p->avrctx = avresample_alloc_context(); - p->avrctx_out = avresample_alloc_context(); - if (!p->avrctx || !p->avrctx_out) - goto error; - - enum AVSampleFormat in_samplefmt = af_to_avformat(p->in_format); - enum AVSampleFormat out_samplefmt = af_to_avformat(p->out_format); - enum AVSampleFormat out_samplefmtp = av_get_planar_sample_fmt(out_samplefmt); - - if (in_samplefmt == AV_SAMPLE_FMT_NONE || - out_samplefmt == AV_SAMPLE_FMT_NONE || - out_samplefmtp == AV_SAMPLE_FMT_NONE) - goto error; - - av_opt_set_int(p->avrctx, "filter_size", p->opts->filter_size, 0); - av_opt_set_int(p->avrctx, "phase_shift", p->opts->phase_shift, 0); - av_opt_set_int(p->avrctx, "linear_interp", p->opts->linear, 0); - - double cutoff = p->opts->cutoff; - if (cutoff <= 0.0) - cutoff = MPMAX(1.0 - 6.5 / (p->opts->filter_size + 8), 0.80); - av_opt_set_double(p->avrctx, "cutoff", cutoff, 0); - - int normalize = p->opts->normalize; -#if HAVE_LIBSWRESAMPLE - av_opt_set_double(p->avrctx, "rematrix_maxval", normalize ? 1 : 1000, 0); -#else - av_opt_set_int(p->avrctx, "normalize_mix_level", !!normalize, 0); -#endif - - if (mp_set_avopts(p->log, p->avrctx, p->opts->avopts) < 0) - goto error; - - struct mp_chmap map_in = p->in_channels; - struct mp_chmap map_out = p->out_channels; - - // Try not to do any remixing if at least one is "unknown". Some corner - // cases also benefit from disabling all channel handling logic if the - // src/dst layouts are the same (like fl-fr-na -> fl-fr-na). - if (mp_chmap_is_unknown(&map_in) || mp_chmap_is_unknown(&map_out) || - mp_chmap_equals(&map_in, &map_out)) - { - mp_chmap_set_unknown(&map_in, map_in.num); - mp_chmap_set_unknown(&map_out, map_out.num); - } - - // unchecked: don't take any channel reordering into account - uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in); - uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out); - - struct mp_chmap in_lavc, out_lavc; - mp_chmap_from_lavc(&in_lavc, in_ch_layout); - mp_chmap_from_lavc(&out_lavc, out_ch_layout); - - if (verbose && !mp_chmap_equals(&in_lavc, &out_lavc)) { - MP_VERBOSE(p, "Remix: %s -> %s\n", mp_chmap_to_str(&in_lavc), - mp_chmap_to_str(&out_lavc)); - } - - if (in_lavc.num != map_in.num) { - // For handling NA channels, we would have to add a planarization step. - MP_FATAL(p, "Unsupported input channel layout %s.\n", - mp_chmap_to_str(&map_in)); - goto error; - } - - mp_chmap_get_reorder(p->reorder_in, &map_in, &in_lavc); - transpose_order(p->reorder_in, map_in.num); - - if (mp_chmap_equals(&out_lavc, &map_out)) { - // No intermediate step required - output new format directly. - out_samplefmtp = out_samplefmt; - } else { - // Verify that we really just reorder and/or insert NA channels. - struct mp_chmap withna = out_lavc; - mp_chmap_fill_na(&withna, map_out.num); - if (withna.num != map_out.num) - goto error; - } - mp_chmap_get_reorder(p->reorder_out, &out_lavc, &map_out); - - p->pre_out_fmt = mp_aframe_create(); - mp_aframe_set_rate(p->pre_out_fmt, p->out_rate); - mp_aframe_set_chmap(p->pre_out_fmt, &p->out_channels); - mp_aframe_set_format(p->pre_out_fmt, p->out_format); - - p->avrctx_fmt = mp_aframe_create(); - mp_aframe_config_copy(p->avrctx_fmt, p->pre_out_fmt); - mp_aframe_set_chmap(p->avrctx_fmt, &out_lavc); - mp_aframe_set_format(p->avrctx_fmt, af_from_avformat(out_samplefmtp)); - - // If there are NA channels, the final output will have more channels than - // the avrctx output. Also, avrctx will output planar (out_samplefmtp was - // not overwritten). Allocate the output frame with more channels, so the - // NA channels can be trivially added. - p->pool_fmt = mp_aframe_create(); - mp_aframe_config_copy(p->pool_fmt, p->avrctx_fmt); - if (map_out.num > out_lavc.num) - mp_aframe_set_chmap(p->pool_fmt, &map_out); - - out_ch_layout = fudge_layout_conversion(p, in_ch_layout, out_ch_layout); - - // Real conversion; output is input to avrctx_out. - av_opt_set_int(p->avrctx, "in_channel_layout", in_ch_layout, 0); - av_opt_set_int(p->avrctx, "out_channel_layout", out_ch_layout, 0); - av_opt_set_int(p->avrctx, "in_sample_rate", p->in_rate, 0); - av_opt_set_int(p->avrctx, "out_sample_rate", p->out_rate, 0); - av_opt_set_int(p->avrctx, "in_sample_fmt", in_samplefmt, 0); - av_opt_set_int(p->avrctx, "out_sample_fmt", out_samplefmtp, 0); - - // Just needs the correct number of channels for deplanarization. - struct mp_chmap fake_chmap; - mp_chmap_set_unknown(&fake_chmap, map_out.num); - uint64_t fake_out_ch_layout = mp_chmap_to_lavc_unchecked(&fake_chmap); - if (!fake_out_ch_layout) - goto error; - av_opt_set_int(p->avrctx_out, "in_channel_layout", fake_out_ch_layout, 0); - av_opt_set_int(p->avrctx_out, "out_channel_layout", fake_out_ch_layout, 0); - - av_opt_set_int(p->avrctx_out, "in_sample_fmt", out_samplefmtp, 0); - av_opt_set_int(p->avrctx_out, "out_sample_fmt", out_samplefmt, 0); - av_opt_set_int(p->avrctx_out, "in_sample_rate", p->out_rate, 0); - av_opt_set_int(p->avrctx_out, "out_sample_rate", p->out_rate, 0); - - // API has weird requirements, quoting avresample.h: - // * This function can only be called when the allocated context is not open. - // * Also, the input channel layout must have already been set. - avresample_set_channel_mapping(p->avrctx, p->reorder_in); - - p->is_resampling = false; - - if (avresample_open(p->avrctx) < 0 || avresample_open(p->avrctx_out) < 0) { - MP_ERR(p, "Cannot open Libavresample context.\n"); - goto error; - } - return true; - -error: - close_lavrr(p); - return false; -} - -bool mp_aconverter_reconfig(struct mp_aconverter *p, - int in_rate, int in_format, struct mp_chmap in_channels, - int out_rate, int out_format, struct mp_chmap out_channels) -{ - close_lavrr(p); - - TA_FREEP(&p->input); - TA_FREEP(&p->output); - p->input_eof = p->output_eof = false; - - p->playback_speed = 1.0; - - p->in_rate_user = in_rate; - p->in_format = in_format; - p->in_channels = in_channels; - p->out_rate = out_rate; - p->out_format = out_format; - p->out_channels = out_channels; - - return configure_lavrr(p, true); -} - -void mp_aconverter_flush(struct mp_aconverter *p) -{ - if (!p->avrctx) - return; -#if HAVE_LIBSWRESAMPLE - swr_close(p->avrctx); - if (swr_init(p->avrctx) < 0) - close_lavrr(p); -#else - while (avresample_read(p->avrctx, NULL, 1000) > 0) {} -#endif -} - -void mp_aconverter_set_speed(struct mp_aconverter *p, double speed) -{ - p->playback_speed = speed; -} - -static void extra_output_conversion(struct mp_aframe *mpa) -{ - int format = af_fmt_from_planar(mp_aframe_get_format(mpa)); - int num_planes = mp_aframe_get_planes(mpa); - uint8_t **planes = mp_aframe_get_data_rw(mpa); - if (!planes) - return; - for (int p = 0; p < num_planes; p++) { - void *ptr = planes[p]; - int total = mp_aframe_get_total_plane_samples(mpa); - if (format == AF_FORMAT_FLOAT) { - for (int s = 0; s < total; s++) - ((float *)ptr)[s] = av_clipf(((float *)ptr)[s], -1.0f, 1.0f); - } else if (format == AF_FORMAT_DOUBLE) { - for (int s = 0; s < total; s++) - ((double *)ptr)[s] = MPCLAMP(((double *)ptr)[s], -1.0, 1.0); - } - } -} - -// This relies on the tricky way mpa was allocated. -static bool reorder_planes(struct mp_aframe *mpa, int *reorder, - struct mp_chmap *newmap) -{ - if (!mp_aframe_set_chmap(mpa, newmap)) - return false; - - int num_planes = newmap->num; - uint8_t **planes = mp_aframe_get_data_rw(mpa); - uint8_t *old_planes[MP_NUM_CHANNELS]; - assert(num_planes <= MP_NUM_CHANNELS); - for (int n = 0; n < num_planes; n++) - old_planes[n] = planes[n]; - - int next_na = 0; - for (int n = 0; n < num_planes; n++) - next_na += newmap->speaker[n] != MP_SPEAKER_ID_NA; - - for (int n = 0; n < num_planes; n++) { - int src = reorder[n]; - assert(src >= -1 && src < num_planes); - if (src >= 0) { - planes[n] = old_planes[src]; - } else { - assert(next_na < num_planes); - planes[n] = old_planes[next_na++]; - // The NA planes were never written by avrctx, so clear them. - af_fill_silence(planes[n], - mp_aframe_get_sstride(mpa) * mp_aframe_get_size(mpa), - mp_aframe_get_format(mpa)); - } - } - - return true; -} - -static int resample_frame(struct AVAudioResampleContext *r, - struct mp_aframe *out, struct mp_aframe *in) -{ - // Be aware that the channel layout and count can be different for in and - // out frames. In some situations the caller will fix up the frames before - // or after conversion. The sample rates can also be different. - AVFrame *av_i = in ? mp_aframe_get_raw_avframe(in) : NULL; - AVFrame *av_o = out ? mp_aframe_get_raw_avframe(out) : NULL; - return avresample_convert(r, - av_o ? av_o->extended_data : NULL, - av_o ? av_o->linesize[0] : 0, - av_o ? av_o->nb_samples : 0, - av_i ? av_i->extended_data : NULL, - av_i ? av_i->linesize[0] : 0, - av_i ? av_i->nb_samples : 0); -} - -static void filter_resample(struct mp_aconverter *p, struct mp_aframe *in) -{ - struct mp_aframe *out = NULL; - - if (!p->avrctx) - goto error; - - int samples = get_out_samples(p, in ? mp_aframe_get_size(in) : 0); - out = mp_aframe_create(); - mp_aframe_config_copy(out, p->pool_fmt); - if (mp_aframe_pool_allocate(p->out_pool, out, samples) < 0) - goto error; - - int out_samples = 0; - if (samples) { - out_samples = resample_frame(p->avrctx, out, in); - if (out_samples < 0 || out_samples > samples) - goto error; - mp_aframe_set_size(out, out_samples); - } - - struct mp_chmap out_chmap; - if (!mp_aframe_get_chmap(p->pool_fmt, &out_chmap)) - goto error; - if (!reorder_planes(out, p->reorder_out, &out_chmap)) - goto error; - - if (!mp_aframe_config_equals(out, p->pre_out_fmt)) { - struct mp_aframe *new = mp_aframe_create(); - mp_aframe_config_copy(new, p->pre_out_fmt); - if (mp_aframe_pool_allocate(p->reorder_buffer, new, out_samples) < 0) { - talloc_free(new); - goto error; - } - int got = 0; - if (out_samples) - got = resample_frame(p->avrctx_out, new, out); - talloc_free(out); - out = new; - if (got != out_samples) - goto error; - } - - extra_output_conversion(out); - - if (in) - mp_aframe_copy_attributes(out, in); - - if (out_samples) { - p->output = out; - } else { - talloc_free(out); - } - p->output_eof = !in; // we've read everything - - return; -error: - talloc_free(out); - MP_ERR(p, "Error on resampling.\n"); -} - -static void filter(struct mp_aconverter *p) -{ - if (p->output || p->output_eof || !(p->input || p->input_eof)) - return; - - int new_rate = rate_from_speed(p->in_rate_user, p->playback_speed); - - if (p->passthrough_mode && new_rate != p->in_rate) - configure_lavrr(p, false); - - if (p->passthrough_mode) { - p->output = p->input; - p->input = NULL; - p->output_eof = p->input_eof; - p->input_eof = false; - return; - } - - if (p->avrctx && !(!p->is_resampling && new_rate == p->in_rate)) { - AVRational r = av_d2q(p->playback_speed * p->in_rate_user / p->in_rate, - INT_MAX / 2); - // Essentially, swr/avresample_set_compensation() does 2 things: - // - adjust output sample rate by sample_delta/compensation_distance - // - reset the adjustment after compensation_distance output samples - // Increase the compensation_distance to avoid undesired reset - // semantics - we want to keep the ratio for the whole frame we're - // feeding it, until the next filter() call. - int mult = INT_MAX / 2 / MPMAX(MPMAX(abs(r.num), abs(r.den)), 1); - r = (AVRational){ r.num * mult, r.den * mult }; - if (avresample_set_compensation(p->avrctx, r.den - r.num, r.den) >= 0) { - new_rate = p->in_rate; - p->is_resampling = true; - } - } - - bool need_reinit = fabs(new_rate / (double)p->in_rate - 1) > 0.01; - if (need_reinit && new_rate != p->in_rate) { - // Before reconfiguring, drain the audio that is still buffered - // in the resampler. - filter_resample(p, NULL); - // Reinitialize resampler. - configure_lavrr(p, false); - p->output_eof = false; - if (p->output) - return; // need to read output before continuing filtering - } - - filter_resample(p, p->input); - TA_FREEP(&p->input); - p->input_eof = false; -} - -// Queue input. If true, ownership of in passes to mp_aconverted and the input -// was accepted. Otherwise, return false and reject in. -// in==NULL means trigger EOF. -bool mp_aconverter_write_input(struct mp_aconverter *p, struct mp_aframe *in) -{ - if (p->input || p->input_eof) - return false; - - p->input = in; - p->input_eof = !in; - return true; -} - -// Return output frame, or NULL if nothing available. -// *eof is set to true if NULL is returned, and it was due to EOF. -struct mp_aframe *mp_aconverter_read_output(struct mp_aconverter *p, bool *eof) -{ - *eof = false; - - filter(p); - - if (p->output) { - struct mp_aframe *out = p->output; - p->output = NULL; - return out; - } - - *eof = p->output_eof; - p->output_eof = false; - return NULL; -} - -double mp_aconverter_get_latency(struct mp_aconverter *p) -{ - double delay = get_delay(p); - - if (p->input) - delay += mp_aframe_duration(p->input); - - // In theory this is influenced by playback speed, but other parts of the - // player get it wrong anyway. - if (p->output) - delay += mp_aframe_duration(p->output); - - return delay; -} - -static void destroy_aconverter(void *ptr) -{ - struct mp_aconverter *p = ptr; - - close_lavrr(p); - - talloc_free(p->input); - talloc_free(p->output); -} - -// If opts is not NULL, the pointer must be valid for the lifetime of the -// mp_aconverter. -struct mp_aconverter *mp_aconverter_create(struct mpv_global *global, - struct mp_log *log, - const struct mp_resample_opts *opts) -{ - struct mp_aconverter *p = talloc_zero(NULL, struct mp_aconverter); - p->log = log; - p->global = global; - - p->opts = opts; - if (!p->opts) - p->opts = mp_get_config_group(p, global, &resample_config); - - p->reorder_buffer = mp_aframe_pool_create(p); - p->out_pool = mp_aframe_pool_create(p); - - talloc_set_destructor(p, destroy_aconverter); - - return p; -} diff --git a/audio/aconverter.h b/audio/aconverter.h deleted file mode 100644 index 22ca93e4c1..0000000000 --- a/audio/aconverter.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include <stdbool.h> - -#include "chmap.h" - -struct mp_aconverter; -struct mp_aframe; -struct mpv_global; -struct mp_log; - -struct mp_resample_opts { - int filter_size; - int phase_shift; - int linear; - double cutoff; - int normalize; - int allow_passthrough; - char **avopts; -}; - -#define MP_RESAMPLE_OPTS_DEF { \ - .filter_size = 16, \ - .cutoff = 0.0, \ - .phase_shift = 10, \ - .normalize = 0, \ - } - -extern const struct m_sub_options resample_config; - -struct mp_aconverter *mp_aconverter_create(struct mpv_global *global, - struct mp_log *log, - const struct mp_resample_opts *opts); -bool mp_aconverter_reconfig(struct mp_aconverter *p, - int in_rate, int in_format, struct mp_chmap in_channels, - int out_rate, int out_format, struct mp_chmap out_channels); -void mp_aconverter_flush(struct mp_aconverter *p); -void mp_aconverter_set_speed(struct mp_aconverter *p, double speed); -bool mp_aconverter_write_input(struct mp_aconverter *p, struct mp_aframe *in); -struct mp_aframe *mp_aconverter_read_output(struct mp_aconverter *p, bool *eof); -double mp_aconverter_get_latency(struct mp_aconverter *p); diff --git a/audio/aframe.c b/audio/aframe.c index 1f053a6715..9115cf67fd 100644 --- a/audio/aframe.c +++ b/audio/aframe.c @@ -32,6 +32,11 @@ struct mp_aframe { // We support spdif formats, which are allocated as AV_SAMPLE_FMT_S16. int format; double pts; + double speed; +}; + +struct avframe_opaque { + double speed; }; static void free_frame(void *ptr) @@ -43,11 +48,11 @@ static void free_frame(void *ptr) struct mp_aframe *mp_aframe_create(void) { struct mp_aframe *frame = talloc_zero(NULL, struct mp_aframe); - frame->pts = MP_NOPTS_VALUE; frame->av_frame = av_frame_alloc(); if (!frame->av_frame) abort(); talloc_set_destructor(frame, free_frame); + mp_aframe_reset(frame); return frame; } @@ -61,6 +66,7 @@ struct mp_aframe *mp_aframe_new_ref(struct mp_aframe *frame) dst->chmap = frame->chmap; dst->format = frame->format; dst->pts = frame->pts; + dst->speed = frame->speed; if (mp_aframe_is_allocated(frame)) { if (av_frame_ref(dst->av_frame, frame->av_frame) < 0) @@ -80,6 +86,7 @@ void mp_aframe_reset(struct mp_aframe *frame) frame->chmap.num = 0; frame->format = 0; frame->pts = MP_NOPTS_VALUE; + frame->speed = 1.0; } // Remove all actual audio data and leave only the metadata. @@ -120,6 +127,11 @@ struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame) mp_chmap_from_channels(&frame->chmap, av_frame->channels); #endif + if (av_frame->opaque_ref) { + struct avframe_opaque *op = (void *)av_frame->opaque_ref->data; + frame->speed = op->speed; + } + return frame; } @@ -137,6 +149,16 @@ struct AVFrame *mp_aframe_to_avframe(struct mp_aframe *frame) if (!mp_chmap_is_lavc(&frame->chmap)) return NULL; + if (!frame->av_frame->opaque_ref && frame->speed != 1.0) { + frame->av_frame->opaque_ref = + av_buffer_alloc(sizeof(struct avframe_opaque)); + if (!frame->av_frame->opaque_ref) + return NULL; + + struct avframe_opaque *op = (void *)frame->av_frame->opaque_ref->data; + op->speed = frame->speed; + } + return av_frame_clone(frame->av_frame); } @@ -183,6 +205,7 @@ void mp_aframe_config_copy(struct mp_aframe *dst, struct mp_aframe *src) void mp_aframe_copy_attributes(struct mp_aframe *dst, struct mp_aframe *src) { dst->pts = src->pts; + dst->speed = src->speed; int rate = dst->av_frame->sample_rate; @@ -316,6 +339,37 @@ void mp_aframe_set_pts(struct mp_aframe *frame, double pts) frame->pts = pts; } +// Set a speed factor. This is multiplied with the sample rate to get the +// "effective" samplerate (mp_aframe_get_effective_rate()), which will be used +// to do PTS calculations. If speed!=1.0, the PTS values always refer to the +// original PTS (before changing speed), and if you want reasonably continuous +// PTS between frames, you need to use the effective samplerate. +void mp_aframe_set_speed(struct mp_aframe *frame, double factor) +{ + frame->speed = factor; +} + +// Adjust current speed factor. +void mp_aframe_mul_speed(struct mp_aframe *frame, double factor) +{ + frame->speed *= factor; +} + +double mp_aframe_get_speed(struct mp_aframe *frame) +{ + return frame->speed; +} + +// Matters for speed changed frames (such as a frame which has been resampled +// to play at a different speed). +// Return the sample rate at which the frame would have to be played to result +// in the same duration as the original frame before the speed change. +// This is used for A/V sync. +double mp_aframe_get_effective_rate(struct mp_aframe *frame) +{ + return mp_aframe_get_rate(frame) / frame->speed; +} + // Return number of data pointers. int mp_aframe_get_planes(struct mp_aframe *frame) { @@ -339,6 +393,18 @@ int mp_aframe_get_total_plane_samples(struct mp_aframe *frame) ? 1 : mp_aframe_get_channels(frame)); } +char *mp_aframe_format_str_buf(char *buf, size_t buf_size, struct mp_aframe *fmt) +{ + char ch[128]; + mp_chmap_to_str_buf(ch, sizeof(ch), &fmt->chmap); + char *hr_ch = mp_chmap_to_str_hr(&fmt->chmap); + if (strcmp(hr_ch, ch) != 0) + mp_snprintf_cat(ch, sizeof(ch), " (%s)", hr_ch); + snprintf(buf, buf_size, "%dHz %s %dch %s", fmt->av_frame->sample_rate, + ch, fmt->chmap.num, af_fmt_to_str(fmt->format)); + return buf; +} + // Set data to the audio after the given number of samples (i.e. slice it). void mp_aframe_skip_samples(struct mp_aframe *f, int samples) { @@ -352,25 +418,25 @@ void mp_aframe_skip_samples(struct mp_aframe *f, int samples) f->av_frame->nb_samples -= samples; if (f->pts != MP_NOPTS_VALUE) - f->pts += samples / (double)mp_aframe_get_rate(f); + f->pts += samples / mp_aframe_get_effective_rate(f); } // Return the timestamp of the sample just after the end of this frame. double mp_aframe_end_pts(struct mp_aframe *f) { - int rate = mp_aframe_get_rate(f); - if (f->pts == MP_NOPTS_VALUE || rate < 1) + double rate = mp_aframe_get_effective_rate(f); + if (f->pts == MP_NOPTS_VALUE || rate <= 0) return MP_NOPTS_VALUE; - return f->pts + f->av_frame->nb_samples / (double)rate; + return f->pts + f->av_frame->nb_samples / rate; } // Return the duration in seconds of the frame (0 if invalid). double mp_aframe_duration(struct mp_aframe *f) { - int rate = mp_aframe_get_rate(f); - if (rate < 1) + double rate = mp_aframe_get_effective_rate(f); + if (rate <= 0) return 0; - return f->av_frame->nb_samples / (double)rate; + return f->av_frame->nb_samples / rate; } // Clip the given frame to the given timestamp range. Adjusts the frame size @@ -378,7 +444,7 @@ double mp_aframe_duration(struct mp_aframe *f) void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end) { double f_end = mp_aframe_end_pts(f); - int rate = mp_aframe_get_rate(f); + double rate = mp_aframe_get_effective_rate(f); if (f_end == MP_NOPTS_VALUE) return; if (end != MP_NOPTS_VALUE) { @@ -405,6 +471,52 @@ void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end) } } +bool mp_aframe_copy_samples(struct mp_aframe *dst, int dst_offset, + struct mp_aframe *src, int src_offset, + int samples) +{ + if (!mp_aframe_config_equals(dst, src)) + return false; + + if (mp_aframe_get_size(dst) < dst_offset + samples || + mp_aframe_get_size(src) < src_offset + samples) + return false; + + uint8_t **s = mp_aframe_get_data_ro(src); + uint8_t **d = mp_aframe_get_data_rw(dst); + if (!s || !d) + return false; + + int planes = mp_aframe_get_planes(dst); + size_t sstride = mp_aframe_get_sstride(dst); + + for (int n = 0; n < planes; n++) { + memcpy(d[n] + dst_offset * sstride, s[n] + src_offset * sstride, + samples * sstride); + } + + return true; +} + +bool mp_aframe_set_silence(struct mp_aframe *f, int offset, int samples) +{ + if (mp_aframe_get_size(f) < offset + samples) + return false; + + int format = mp_aframe_get_format(f); + uint8_t **d = mp_aframe_get_data_rw(f); + if (!d) + return false; + + int planes = mp_aframe_get_planes(f); + size_t sstride = mp_aframe_get_sstride(f); + + for (int n = 0; n < planes; n++) + af_fill_silence(d[n] + offset * sstride, samples * sstride, format); + + return true; +} + struct mp_aframe_pool { AVBufferPool *avpool; int element_size; diff --git a/audio/aframe.h b/audio/aframe.h index 8ea676c198..ed92c223f6 100644 --- a/audio/aframe.h +++ b/audio/aframe.h @@ -36,21 +36,32 @@ int mp_aframe_get_channels(struct mp_aframe *frame); int mp_aframe_get_rate(struct mp_aframe *frame); int mp_aframe_get_size(struct mp_aframe *frame); double mp_aframe_get_pts(struct mp_aframe *frame); +double mp_aframe_get_speed(struct mp_aframe *frame); +double mp_aframe_get_effective_rate(struct mp_aframe *frame); bool mp_aframe_set_format(struct mp_aframe *frame, int format); bool mp_aframe_set_chmap(struct mp_aframe *frame, struct mp_chmap *in); bool mp_aframe_set_rate(struct mp_aframe *frame, int rate); bool mp_aframe_set_size(struct mp_aframe *frame, int samples); void mp_aframe_set_pts(struct mp_aframe *frame, double pts); +void mp_aframe_set_speed(struct mp_aframe *frame, double factor); +void mp_aframe_mul_speed(struct mp_aframe *frame, double factor); int mp_aframe_get_planes(struct mp_aframe *frame); int mp_aframe_get_total_plane_samples(struct mp_aframe *frame); size_t mp_aframe_get_sstride(struct mp_aframe *frame); +char *mp_aframe_format_str_buf(char *buf, size_t buf_size, struct mp_aframe *fmt); +#define mp_aframe_format_str(fmt) mp_aframe_format_str_buf((char[32]){0}, 32, (fmt)) + void mp_aframe_skip_samples(struct mp_aframe *f, int samples); double mp_aframe_end_pts(struct mp_aframe *f); double mp_aframe_duration(struct mp_aframe *f); void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end); +bool mp_aframe_copy_samples(struct mp_aframe *dst, int dst_offset, + struct mp_aframe *src, int src_offset, + int samples); +bool mp_aframe_set_silence(struct mp_aframe *f, int offset, int samples); struct mp_aframe_pool; struct mp_aframe_pool *mp_aframe_pool_create(void *ta_parent); diff --git a/audio/audio.c b/audio/audio.c deleted file mode 100644 index 55e4266f45..0000000000 --- a/audio/audio.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdint.h> -#include <limits.h> -#include <stdlib.h> -#include <assert.h> - -#include <libavutil/buffer.h> -#include <libavutil/frame.h> -#include <libavutil/mem.h> -#include <libavutil/version.h> - -#include "mpv_talloc.h" -#include "common/common.h" -#include "fmt-conversion.h" -#include "audio.h" -#include "aframe.h" - -static void update_redundant_info(struct mp_audio *mpa) -{ - assert(mp_chmap_is_empty(&mpa->channels) || - mp_chmap_is_valid(&mpa->channels)); - mpa->nch = mpa->channels.num; - mpa->bps = af_fmt_to_bytes(mpa->format); - if (af_fmt_is_planar(mpa->format)) { - mpa->spf = 1; - mpa->num_planes = mpa->nch; - mpa->sstride = mpa->bps; - } else { - mpa->spf = mpa->nch; - mpa->num_planes = 1; - mpa->sstride = mpa->bps * mpa->nch; - } -} - -void mp_audio_set_format(struct mp_audio *mpa, int format) -{ - mpa->format = format; - update_redundant_info(mpa); -} - -void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels) -{ - mp_chmap_from_channels(&mpa->channels, num_channels); - update_redundant_info(mpa); -} - -void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap) -{ - mpa->channels = *chmap; - update_redundant_info(mpa); -} - -void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src) -{ - dst->format = src->format; - dst->channels = src->channels; - dst->rate = src->rate; - update_redundant_info(dst); -} - -bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b) -{ - return a->format == b->format && a->rate == b->rate && - mp_chmap_equals(&a->channels, &b->channels); -} - -bool mp_audio_config_valid(const struct mp_audio *mpa) -{ - return mp_chmap_is_valid(&mpa->channels) && af_fmt_is_valid(mpa->format) - && mpa->rate >= 1 && mpa->rate < 10000000; -} - -char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa) -{ - char ch[128]; - mp_chmap_to_str_buf(ch, sizeof(ch), &mpa->channels); - char *hr_ch = mp_chmap_to_str_hr(&mpa->channels); - if (strcmp(hr_ch, ch) != 0) - mp_snprintf_cat(ch, sizeof(ch), " (%s)", hr_ch); - snprintf(buf, buf_sz, "%dHz %s %dch %s", mpa->rate, - ch, mpa->channels.num, af_fmt_to_str(mpa->format)); - return buf; -} - -void mp_audio_force_interleaved_format(struct mp_audio *mpa) -{ - if (af_fmt_is_planar(mpa->format)) - mp_audio_set_format(mpa, af_fmt_from_planar(mpa->format)); -} - -// Return used size of a plane. (The size is the same for all planes.) -int mp_audio_psize(struct mp_audio *mpa) -{ - return mpa->samples * mpa->sstride; -} - -void mp_audio_set_null_data(struct mp_audio *mpa) -{ - for (int n = 0; n < MP_NUM_CHANNELS; n++) { - mpa->planes[n] = NULL; - mpa->allocated[n] = NULL; - } - mpa->samples = 0; -} - -static int get_plane_size(const struct mp_audio *mpa, int samples) -{ - if (samples < 0 || !mp_audio_config_valid(mpa)) - return -1; - if (samples >= INT_MAX / mpa->sstride) - return -1; - return MPMAX(samples * mpa->sstride, 1); -} - -static void mp_audio_destructor(void *ptr) -{ - struct mp_audio *mpa = ptr; - for (int n = 0; n < MP_NUM_CHANNELS; n++) - av_buffer_unref(&mpa->allocated[n]); -} - -/* Reallocate the data stored in mpa->planes[n] so that enough samples are - * available on every plane. The previous data is kept (for the smallest - * common number of samples before/after resize). - * - * This also makes sure the resulting buffer is writable (even in the case - * the buffer has the correct size). - * - * mpa->samples is not set or used. - * - * This function is flexible enough to handle format and channel layout - * changes. In these cases, all planes are reallocated as needed. Unused - * planes are freed. - * - * mp_audio_realloc(mpa, 0) will still yield non-NULL for mpa->data[n]. - * - * Allocated data is implicitly freed on talloc_free(mpa). - */ -void mp_audio_realloc(struct mp_audio *mpa, int samples) -{ - int size = get_plane_size(mpa, samples); - if (size < 0) - abort(); // oom or invalid parameters - if (!mp_audio_is_writeable(mpa)) { - for (int n = 0; n < MP_NUM_CHANNELS; n++) { - av_buffer_unref(&mpa->allocated[n]); - mpa->planes[n] = NULL; - } - } - for (int n = 0; n < mpa->num_planes; n++) { - if (!mpa->allocated[n] || size != mpa->allocated[n]->size) { - if (av_buffer_realloc(&mpa->allocated[n], size) < 0) - abort(); // OOM - } - mpa->planes[n] = mpa->allocated[n]->data; - } - for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) { - av_buffer_unref(&mpa->allocated[n]); - mpa->planes[n] = NULL; - } - talloc_set_destructor(mpa, mp_audio_destructor); -} - -// Like mp_audio_realloc(), but only reallocate if the audio grows in size. -// If the buffer is reallocated, also preallocate. -void mp_audio_realloc_min(struct mp_audio *mpa, int samples) -{ - if (samples > mp_audio_get_allocated_size(mpa) || !mp_audio_is_writeable(mpa)) { - size_t alloc = ta_calc_prealloc_elems(samples); - if (alloc > INT_MAX) - abort(); // oom - mp_audio_realloc(mpa, alloc); - } -} - -/* Get the size allocated for the data, in number of samples. If the allocated - * size isn't on sample boundaries (e.g. after format changes), the returned - * sample number is a rounded down value. - * - * Note that this only works in situations where mp_audio_realloc() also works! - */ -int mp_audio_get_allocated_size(struct mp_audio *mpa) -{ - int size = 0; - for (int n = 0; n < mpa->num_planes; n++) { - for (int i = 0; i < MP_NUM_CHANNELS && mpa->allocated[i]; i++) { - uint8_t *start = mpa->allocated[i]->data; - uint8_t *end = start + mpa->allocated[i]->size; - uint8_t *plane = mpa->planes[n]; - if (plane >= start && plane < end) { - int s = MPMIN((end - plane) / mpa->sstride, INT_MAX); - size = n == 0 ? s : MPMIN(size, s); - goto next; - } - } - return 0; // plane is not covered by any buffer - next: ; - } - return size; -} - -// Clear the samples [start, start + length) with silence. -void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length) -{ - assert(start >= 0 && length >= 0 && start + length <= mpa->samples); - int offset = start * mpa->sstride; - int size = length * mpa->sstride; - for (int n = 0; n < mpa->num_planes; n++) { - if (n > 0 && mpa->planes[n] == mpa->planes[0]) - continue; // silly optimization for special cases - af_fill_silence((char *)mpa->planes[n] + offset, size, mpa->format); - } -} - -// All integer parameters are in samples. -// dst and src can overlap. -void mp_audio_copy(struct mp_audio *dst, int dst_offset, - struct mp_audio *src, int src_offset, int length) -{ - assert(mp_audio_config_equals(dst, src)); - assert(length >= 0); - assert(dst_offset >= 0 && dst_offset + length <= dst->samples); - assert(src_offset >= 0 && src_offset + length <= src->samples); - - for (int n = 0; n < dst->num_planes; n++) { - memmove((char *)dst->planes[n] + dst_offset * dst->sstride, - (char *)src->planes[n] + src_offset * src->sstride, - length * dst->sstride); - } -} - -// Copy fields that describe characteristics of the audio frame, but which are -// not part of the core format (format/channels/rate), and not part of the -// data (samples). -void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src) -{ - // nothing yet -} - -// Set data to the audio after the given number of samples (i.e. slice it). -void mp_audio_skip_samples(struct mp_audio *data, int samples) -{ - assert(samples >= 0 && samples <= data->samples); - - for (int n = 0; n < data->num_planes; n++) - data->planes[n] = (uint8_t *)data->planes[n] + samples * data->sstride; - - data->samples -= samples; - - if (data->pts != MP_NOPTS_VALUE) - data->pts += samples / (double)data->rate; -} - -// Return the timestamp of the sample just after the end of this frame. -double mp_audio_end_pts(struct mp_audio *f) -{ - if (f->pts == MP_NOPTS_VALUE || f->rate < 1) - return MP_NOPTS_VALUE; - return f->pts + f->samples / (double)f->rate; -} - -// Clip the given frame to the given timestamp range. Adjusts the frame size -// and timestamp. -void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end) -{ - double f_end = mp_audio_end_pts(f); - if (f_end == MP_NOPTS_VALUE) - return; - if (end != MP_NOPTS_VALUE) { - if (f_end >= end) { - if (f->pts >= end) { - f->samples = 0; - } else { - int new = (end - f->pts) * f->rate; - f->samples = MPCLAMP(new, 0, f->samples); - } - } - } - if (start != MP_NOPTS_VALUE) { - if (f->pts < start) { - if (f_end <= start) { - f->samples = 0; - f->pts = f_end; - } else { - int skip = (start - f->pts) * f->rate; - skip = MPCLAMP(skip, 0, f->samples); - mp_audio_skip_samples(f, skip); - } - } - } -} - - -// Return false if the frame data is shared, true otherwise. -// Will return true for non-refcounted frames. -bool mp_audio_is_writeable(struct mp_audio *data) -{ - bool ok = true; - for (int n = 0; n < MP_NUM_CHANNELS; n++) { - if (data->allocated[n]) - ok &= av_buffer_is_writable(data->allocated[n]); - } - return ok; -} - -static void mp_audio_steal_data(struct mp_audio *dst, struct mp_audio *src) -{ - talloc_set_destructor(dst, mp_audio_destructor); - mp_audio_destructor(dst); - *dst = *src; - talloc_set_destructor(src, NULL); - talloc_free(src); -} - -// Make sure the frame owns the audio data, and if not, copy the data. -// Return negative value on failure (which means it can't be made writeable). -// Non-refcounted frames are always considered writeable. -int mp_audio_make_writeable(struct mp_audio *data) -{ - if (!mp_audio_is_writeable(data)) { - struct mp_audio *new = talloc(NULL, struct mp_audio); - *new = *data; - mp_audio_set_null_data(new); // use format only - mp_audio_realloc(new, data->samples); - new->samples = data->samples; - mp_audio_copy(new, 0, data, 0, data->samples); - mp_audio_steal_data(data, new); - } - return 0; -} - -struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe) -{ - AVFrame *tmp = NULL; - struct mp_audio *new = talloc_zero(NULL, struct mp_audio); - talloc_set_destructor(new, mp_audio_destructor); - - mp_audio_set_format(new, af_from_avformat(avframe->format)); - - struct mp_chmap lavc_chmap; - mp_chmap_from_lavc(&lavc_chmap, avframe->channel_layout); - -#if LIBAVUTIL_VERSION_MICRO >= 100 - // FFmpeg being stupid POS again - if (lavc_chmap.num != avframe->channels) - mp_chmap_from_channels(&lavc_chmap, avframe->channels); -#endif - - new->rate = avframe->sample_rate; - - mp_audio_set_channels(new, &lavc_chmap); - - // Force refcounted frame. - if (!avframe->buf[0]) { - tmp = av_frame_alloc(); - if (!tmp) - goto fail; - if (av_frame_ref(tmp, avframe) < 0) - goto fail; - avframe = tmp; - } - - // If we can't handle the format (e.g. too many channels), bail out. - if (!mp_audio_config_valid(new)) - goto fail; - - for (int n = 0; n < AV_NUM_DATA_POINTERS + avframe->nb_extended_buf; n++) { - AVBufferRef *buf = n < AV_NUM_DATA_POINTERS ? avframe->buf[n] - : avframe->extended_buf[n - AV_NUM_DATA_POINTERS]; - if (!buf) - break; - if (n >= MP_NUM_CHANNELS) - goto fail; - new->allocated[n] = av_buffer_ref(buf); - if (!new->allocated[n]) - goto fail; - } - - for (int n = 0; n < new->num_planes; n++) - new->planes[n] = avframe->extended_data[n]; - new->samples = avframe->nb_samples; - - return new; - -fail: - talloc_free(new); - av_frame_free(&tmp); - return NULL; -} - -struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe) -{ - if (!aframe) - return NULL; - - struct AVFrame *av = mp_aframe_get_raw_avframe(aframe); - struct mp_audio *res = mp_audio_from_avframe(av); - if (!res) - return NULL; - struct mp_chmap chmap = {0}; - mp_aframe_get_chmap(aframe, &chmap); - mp_audio_set_channels(res, &chmap); - mp_audio_set_format(res, mp_aframe_get_format(aframe)); - res->pts = mp_aframe_get_pts(aframe); - return res; -} - -void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src) -{ - *dst = (struct mp_audio){0}; - struct mp_chmap chmap = {0}; - mp_aframe_get_chmap(src, &chmap); - mp_audio_set_channels(dst, &chmap); - mp_audio_set_format(dst, mp_aframe_get_format(src)); - dst->rate = mp_aframe_get_rate(src); -} - -struct mp_aframe *mp_audio_to_aframe(struct mp_audio *mpa) -{ - if (!mpa) - return NULL; - - struct mp_aframe *aframe = mp_aframe_create(); - struct AVFrame *av = mp_aframe_get_raw_avframe(aframe); - mp_aframe_set_format(aframe, mpa->format); - mp_aframe_set_chmap(aframe, &mpa->channels); - mp_aframe_set_rate(aframe, mpa->rate); - - // bullshit it into ffmpeg-compatible parameters - struct mp_audio mpb = *mpa; - struct mp_chmap chmap; - mp_chmap_set_unknown(&chmap, mpb.channels.num); - mp_audio_set_channels(&mpb, &chmap); - if (af_fmt_is_spdif(mpb.format)) - mp_audio_set_format(&mpb, AF_FORMAT_S16); - - // put the reference into av, which magically puts it into aframe - // aframe keeps its parameters, so the bullshit doesn't matter - if (mp_audio_to_avframe(&mpb, av) < 0) { - talloc_free(aframe); - return NULL; - } - return aframe; -} - -int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe) -{ - av_frame_unref(avframe); - - avframe->nb_samples = frame->samples; - avframe->format = af_to_avformat(frame->format); - if (avframe->format == AV_SAMPLE_FMT_NONE) - goto fail; - - avframe->channel_layout = mp_chmap_to_lavc(&frame->channels); - if (!avframe->channel_layout) - goto fail; -#if LIBAVUTIL_VERSION_MICRO >= 100 - // FFmpeg being a stupid POS again - avframe->channels = frame->channels.num; -#endif - avframe->sample_rate = frame->rate; - - if (frame->num_planes > AV_NUM_DATA_POINTERS) { - avframe->extended_data = - av_mallocz_array(frame->num_planes, sizeof(avframe->extended_data[0])); - int extbufs = frame->num_planes - AV_NUM_DATA_POINTERS; - avframe->extended_buf = - av_mallocz_array(extbufs, sizeof(avframe->extended_buf[0])); - if (!avframe->extended_data || !avframe->extended_buf) - goto fail; - avframe->nb_extended_buf = extbufs; - } - - for (int p = 0; p < frame->num_planes; p++) - avframe->extended_data[p] = frame->planes[p]; - avframe->linesize[0] = frame->samples * frame->sstride; - - for (int p = 0; p < AV_NUM_DATA_POINTERS; p++) - avframe->data[p] = avframe->extended_data[p]; - - for (int p = 0; p < frame->num_planes; p++) { - if (!frame->allocated[p]) - break; - AVBufferRef *nref = av_buffer_ref(frame->allocated[p]); - if (!nref) - goto fail; - if (p < AV_NUM_DATA_POINTERS) { - avframe->buf[p] = nref; - } else { - avframe->extended_buf[p - AV_NUM_DATA_POINTERS] = nref; - } - } - - // Force refcounted frame. - if (!avframe->buf[0]) { - AVFrame *tmp = av_frame_alloc(); - if (!tmp) - goto fail; - if (av_frame_ref(tmp, avframe) < 0) - goto fail; - av_frame_free(&avframe); - avframe = tmp; - } - - return 0; - -fail: - av_frame_unref(avframe); - return -1; -} - -// Returns NULL on failure. The input is always unreffed. -struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame) -{ - struct AVFrame *avframe = av_frame_alloc(); - if (!avframe) - goto fail; - - if (mp_audio_to_avframe(frame, avframe) < 0) - goto fail; - - talloc_free(frame); - return avframe; - -fail: - av_frame_free(&avframe); - talloc_free(frame); - return NULL; -} - -struct mp_audio_pool { - AVBufferPool *avpool; - int element_size; -}; - -struct mp_audio_pool *mp_audio_pool_create(void *ta_parent) -{ - return talloc_zero(ta_parent, struct mp_audio_pool); -} - -static void mp_audio_pool_destructor(void *p) -{ - struct mp_audio_pool *pool = p; - av_buffer_pool_uninit(&pool->avpool); -} - -// Allocate data using the given format and number of samples. -// Returns NULL on error. -struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool, - const struct mp_audio *fmt, int samples) -{ - int size = get_plane_size(fmt, samples); - if (size < 0) - return NULL; - if (!pool->avpool || size > pool->element_size) { - size_t alloc = ta_calc_prealloc_elems(size); - if (alloc >= INT_MAX) - return NULL; - av_buffer_pool_uninit(&pool->avpool); - pool->element_size = alloc; - pool->avpool = av_buffer_pool_init(pool->element_size, NULL); - if (!pool->avpool) - return NULL; - talloc_set_destructor(pool, mp_audio_pool_destructor); - } - struct mp_audio *new = talloc_ptrtype(NULL, new); - talloc_set_destructor(new, mp_audio_destructor); - *new = *fmt; - mp_audio_set_null_data(new); - new->samples = samples; - for (int n = 0; n < new->num_planes; n++) { - new->allocated[n] = av_buffer_pool_get(pool->avpool); - if (!new->allocated[n]) { - talloc_free(new); - return NULL; - } - new->planes[n] = new->allocated[n]->data; - } - return new; -} - -// Return a copy of the given frame. -// Returns NULL on error. -struct mp_audio *mp_audio_pool_new_copy(struct mp_audio_pool *pool, - struct mp_audio *frame) -{ - struct mp_audio *new = mp_audio_pool_get(pool, frame, frame->samples); - if (new) { - mp_audio_copy(new, 0, frame, 0, new->samples); - mp_audio_copy_attributes(new, frame); - } - return new; -} - -// Exactly like mp_audio_make_writeable(), but get the data from the pool. -int mp_audio_pool_make_writeable(struct mp_audio_pool *pool, - struct mp_audio *data) -{ - if (mp_audio_is_writeable(data)) - return 0; - struct mp_audio *new = mp_audio_pool_get(pool, data, data->samples); - if (!new) - return -1; - mp_audio_copy(new, 0, data, 0, data->samples); - mp_audio_copy_attributes(new, data); - mp_audio_steal_data(data, new); - return 0; -} diff --git a/audio/audio.h b/audio/audio.h deleted file mode 100644 index f370067b78..0000000000 --- a/audio/audio.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MP_AUDIO_H -#define MP_AUDIO_H - -#include "format.h" -#include "chmap.h" - -// Audio data chunk -struct mp_audio { - int samples; // number of samples in data (per channel) - void *planes[MP_NUM_CHANNELS]; // data buffer (one per plane) - int rate; // sample rate - struct mp_chmap channels; // channel layout, use mp_audio_set_*() to set - int format; // format (AF_FORMAT_...), use mp_audio_set_format() to set - // Redundant fields, for convenience - int sstride; // distance between 2 samples in bytes on a plane - // interleaved: bps * nch - // planar: bps - int nch; // number of channels (redundant with chmap) - int spf; // sub-samples per sample on each plane - int num_planes; // number of planes - int bps; // size of sub-samples (af_fmt_to_bytes(format)) - - double pts; // currently invalid within the filter chain - - // --- private - // These do not necessarily map directly to planes[]. They can have - // different order or count. There shouldn't be more buffers than planes. - // If allocated[n] is NULL, allocated[n+1] must also be NULL. - struct AVBufferRef *allocated[MP_NUM_CHANNELS]; -}; - -void mp_audio_set_format(struct mp_audio *mpa, int format); -void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels); -void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap); -void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src); -bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b); -bool mp_audio_config_valid(const struct mp_audio *mpa); - -char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa); -#define mp_audio_config_to_str(m) mp_audio_config_to_str_buf((char[64]){0}, 64, (m)) - -void mp_audio_force_interleaved_format(struct mp_audio *mpa); - -int mp_audio_psize(struct mp_audio *mpa); - -void mp_audio_set_null_data(struct mp_audio *mpa); - -void mp_audio_realloc(struct mp_audio *mpa, int samples); -void mp_audio_realloc_min(struct mp_audio *mpa, int samples); -int mp_audio_get_allocated_size(struct mp_audio *mpa); - -void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length); - -void mp_audio_copy(struct mp_audio *dst, int dst_offset, - struct mp_audio *src, int src_offset, int length); -void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src); -void mp_audio_skip_samples(struct mp_audio *data, int samples); -void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end); -double mp_audio_end_pts(struct mp_audio *data); - -bool mp_audio_is_writeable(struct mp_audio *data); -int mp_audio_make_writeable(struct mp_audio *data); - -struct AVFrame; -struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe); -struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame); -int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe); - -struct mp_aframe; -struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe); -void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src); -struct mp_aframe *mp_audio_to_aframe(struct mp_audio *mpa); - -struct mp_audio_pool; -struct mp_audio_pool *mp_audio_pool_create(void *ta_parent); -struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool, - const struct mp_audio *fmt, int samples); -struct mp_audio *mp_audio_pool_new_copy(struct mp_audio_pool *pool, - struct mp_audio *frame); -int mp_audio_pool_make_writeable(struct mp_audio_pool *pool, - struct mp_audio *frame); - -#include "filter/af.h" - -#endif diff --git a/audio/filter/af.c b/audio/filter/af.c deleted file mode 100644 index cf200bbb84..0000000000 --- a/audio/filter/af.c +++ /dev/null @@ -1,824 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include "common/common.h" -#include "common/global.h" - -#include "options/m_option.h" -#include "options/m_config.h" - -#include "audio/audio_buffer.h" -#include "af.h" - -// Static list of filters -extern const struct af_info af_info_format; -extern const struct af_info af_info_lavcac3enc; -extern const struct af_info af_info_lavrresample; -extern const struct af_info af_info_scaletempo; -extern const struct af_info af_info_lavfi; -extern const struct af_info af_info_lavfi_bridge; -extern const struct af_info af_info_rubberband; - -static const struct af_info *const filter_list[] = { - &af_info_format, - &af_info_lavcac3enc, - &af_info_lavrresample, -#if HAVE_RUBBERBAND - &af_info_rubberband, -#endif - &af_info_scaletempo, - &af_info_lavfi, - &af_info_lavfi_bridge, - NULL -}; - -static bool get_desc(struct m_obj_desc *dst, int index) -{ - if (index >= MP_ARRAY_SIZE(filter_list) - 1) - return false; - const struct af_info *af = filter_list[index]; - *dst = (struct m_obj_desc) { - .name = af->name, - .description = af->info, - .priv_size = af->priv_size, - .priv_defaults = af->priv_defaults, - .options = af->options, - .set_defaults = af->set_defaults, - .p = af, - }; - return true; -} - -const struct m_obj_list af_obj_list = { - .get_desc = get_desc, - .description = "audio filters", - .allow_disable_entries = true, - .allow_unknown_entries = true, - .aliases = { - {"force", "format"}, - {0} - }, -}; - -static void af_forget_frames(struct af_instance *af) -{ - for (int n = 0; n < af->num_out_queued; n++) - talloc_free(af->out_queued[n]); - af->num_out_queued = 0; -} - -static void af_chain_forget_frames(struct af_stream *s) -{ - for (struct af_instance *cur = s->first; cur; cur = cur->next) - af_forget_frames(cur); -} - -static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src) -{ - if (dst->format == AF_FORMAT_UNKNOWN) - mp_audio_set_format(dst, src->format); - if (dst->nch == 0) - mp_audio_set_channels(dst, &src->channels); - if (dst->rate == 0) - dst->rate = src->rate; -} - -static int input_control(struct af_instance* af, int cmd, void* arg) -{ - switch (cmd) { - case AF_CONTROL_REINIT: - assert(arg == &((struct af_stream *)af->priv)->input); - return AF_OK; - } - return AF_UNKNOWN; -} - -static int output_control(struct af_instance* af, int cmd, void* arg) -{ - struct af_stream *s = af->priv; - struct mp_audio *output = &s->output; - struct mp_audio *filter_output = &s->filter_output; - - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - - *filter_output = *output; - af_copy_unset_fields(filter_output, in); - *in = *filter_output; - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - } - return AF_UNKNOWN; -} - -static int dummy_filter(struct af_instance *af, struct mp_audio *frame) -{ - af_add_output_frame(af, frame); - return 0; -} - -/* Function for creating a new filter of type name.The name may -contain the commandline parameters for the filter */ -static struct af_instance *af_create(struct af_stream *s, char *name, - char **args) -{ - const char *lavfi_name = NULL; - char **lavfi_args = NULL; - struct m_obj_desc desc; - if (!m_obj_list_find(&desc, &af_obj_list, bstr0(name))) { - if (!m_obj_list_find(&desc, &af_obj_list, bstr0("lavfi-bridge"))) { - MP_ERR(s, "Couldn't find audio filter '%s'.\n", name); - return NULL; - } - lavfi_name = name; - lavfi_args = args; - args = NULL; - if (strncmp(lavfi_name, "lavfi-", 6) == 0) - lavfi_name += 6; - } - MP_VERBOSE(s, "Adding filter %s \n", name); - - struct af_instance *af = talloc_zero(NULL, struct af_instance); - *af = (struct af_instance) { - .full_name = talloc_strdup(af, name), - .info = desc.p, - .data = talloc_zero(af, struct mp_audio), - .log = mp_log_new(af, s->log, name), - .opts = s->opts, - .global = s->global, - .out_pool = mp_audio_pool_create(af), - }; - struct m_config *config = - m_config_from_obj_desc_and_args(af, s->log, s->global, &desc, - name, s->opts->af_defs, args); - if (!config) - goto error; - if (lavfi_name) { - // Pass the filter arguments as proper sub-options to the bridge filter. - struct m_config_option *name_opt = m_config_get_co(config, bstr0("name")); - assert(name_opt); - assert(name_opt->opt->type == &m_option_type_string); - if (m_config_set_option_raw(config, name_opt, &lavfi_name, 0) < 0) - goto error; - struct m_config_option *opts = m_config_get_co(config, bstr0("opts")); - assert(opts); - assert(opts->opt->type == &m_option_type_keyvalue_list); - if (m_config_set_option_raw(config, opts, &lavfi_args, 0) < 0) - goto error; - af->full_name = talloc_asprintf(af, "%s (lavfi)", af->full_name); - } - af->priv = config->optstruct; - - // Initialize the new filter - if (af->info->open(af) != AF_OK) - goto error; - - return af; - -error: - MP_ERR(s, "Couldn't create or open audio filter '%s'\n", name); - talloc_free(af); - return NULL; -} - -/* Create and insert a new filter of type name before the filter in the - argument. This function can be called during runtime, the return - value is the new filter */ -static struct af_instance *af_prepend(struct af_stream *s, - struct af_instance *af, - char *name, char **args) -{ - if (!af) - af = s->last; - if (af == s->first) - af = s->first->next; - // Create the new filter and make sure it is OK - struct af_instance *new = af_create(s, name, args); - if (!new) - return NULL; - // Update pointers - new->next = af; - new->prev = af->prev; - af->prev = new; - new->prev->next = new; - return new; -} - -// Uninit and remove the filter "af" -static void af_remove(struct af_stream *s, struct af_instance *af) -{ - if (!af) - return; - - if (af == s->first || af == s->last) - return; - - // Print friendly message - MP_VERBOSE(s, "Removing filter %s \n", af->info->name); - - // Detach pointers - af->prev->next = af->next; - af->next->prev = af->prev; - - if (af->uninit) - af->uninit(af); - af_forget_frames(af); - talloc_free(af); -} - -static void remove_auto_inserted_filters(struct af_stream *s) -{ -repeat: - for (struct af_instance *af = s->first; af; af = af->next) { - if (af->auto_inserted) { - af_remove(s, af); - goto repeat; - } - } -} - -static void af_print_filter_chain(struct af_stream *s, struct af_instance *at, - int msg_level) -{ - MP_MSG(s, msg_level, "Audio filter chain:\n"); - - struct af_instance *af = s->first; - while (af) { - char b[128] = {0}; - mp_snprintf_cat(b, sizeof(b), " [%s] ", af->full_name); - if (af->label) - mp_snprintf_cat(b, sizeof(b), "\"%s\" ", af->label); - if (af->data) - mp_snprintf_cat(b, sizeof(b), "%s", mp_audio_config_to_str(af->data)); - if (af->auto_inserted) - mp_snprintf_cat(b, sizeof(b), " [a]"); - if (af == at) - mp_snprintf_cat(b, sizeof(b), " <-"); - MP_MSG(s, msg_level, "%s\n", b); - - af = af->next; - } - - MP_MSG(s, msg_level, " [ao] %s\n", mp_audio_config_to_str(&s->output)); -} - -static void reset_formats(struct af_stream *s) -{ - struct mp_audio none = {0}; - for (struct af_instance *af = s->first; af; af = af->next) { - if (af != s->first && af != s->last) - mp_audio_copy_config(af->data, &none); - } -} - -static int filter_reinit(struct af_instance *af) -{ - struct af_instance *prev = af->prev; - assert(prev); - - // Check if this is the first filter - struct mp_audio in = *prev->data; - // Reset just in case... - mp_audio_set_null_data(&in); - - if (!mp_audio_config_valid(&in)) - return AF_ERROR; - - af->fmt_in = in; - int rv = af->control(af, AF_CONTROL_REINIT, &in); - if (rv == AF_OK && !mp_audio_config_equals(&in, prev->data)) - rv = AF_FALSE; // conversion filter needed - if (rv == AF_FALSE) - af->fmt_in = in; - - if (rv == AF_OK) { - if (!mp_audio_config_valid(af->data)) - return AF_ERROR; - af->fmt_out = *af->data; - } - - return rv; -} - -static int filter_reinit_with_conversion(struct af_stream *s, struct af_instance *af) -{ - int rv = filter_reinit(af); - - // Conversion filter is needed - if (rv == AF_FALSE) { - // First try if we can change the output format of the previous - // filter to the input format the current filter is expecting. - struct mp_audio in = af->fmt_in; - if (af->prev != s->first && !mp_audio_config_equals(af->prev->data, &in)) { - // This should have been successful (because it succeeded - // before), even if just reverting to the old output format. - mp_audio_copy_config(af->prev->data, &in); - rv = filter_reinit(af->prev); - if (rv != AF_OK) - return rv; - } - if (!mp_audio_config_equals(af->prev->data, &in)) { - // Retry with conversion filter added. - char *opts[] = {"deprecation-warning", "no", NULL}; - struct af_instance *new = - af_prepend(s, af, "lavrresample", opts); - if (!new) - return AF_ERROR; - new->auto_inserted = true; - mp_audio_copy_config(new->data, &in); - rv = filter_reinit(new); - if (rv != AF_OK) - af_remove(s, new); - } - if (rv == AF_OK) - rv = filter_reinit(af); - } - - return rv; -} - -static int af_find_output_conversion(struct af_stream *s, struct mp_audio *cfg) -{ - assert(mp_audio_config_valid(&s->output)); - assert(s->initialized > 0); - - if (mp_chmap_equals_reordered(&s->input.channels, &s->output.channels)) - return AF_ERROR; - - // Heuristic to detect point of conversion. If it looks like something - // more complicated is going on, better bail out. - // We expect that the last filter converts channels. - struct af_instance *conv = s->last->prev; - if (!conv->auto_inserted) - return AF_ERROR; - if (!(mp_chmap_equals_reordered(&conv->fmt_in.channels, &s->input.channels) && - mp_chmap_equals_reordered(&conv->fmt_out.channels, &s->output.channels))) - return AF_ERROR; - // Also, should be the only one which does auto conversion. - for (struct af_instance *af = s->first->next; af != s->last; af = af->next) - { - if (af != conv && af->auto_inserted && - !mp_chmap_equals_reordered(&af->fmt_in.channels, &af->fmt_out.channels)) - return AF_ERROR; - } - // And not if it's the only filter. - if (conv->prev == s->first && conv->next == s->last) - return AF_ERROR; - - *cfg = s->output; - return AF_OK; -} - -// Return AF_OK on success or AF_ERROR on failure. -static int af_do_reinit(struct af_stream *s, bool second_pass) -{ - struct mp_audio convert_early = {0}; - if (second_pass) { - // If a channel conversion happens, and it is done by an auto-inserted - // filter, then insert a filter to convert it early. Otherwise, do - // nothing and return immediately. - if (af_find_output_conversion(s, &convert_early) != AF_OK) - return AF_OK; - } - - remove_auto_inserted_filters(s); - af_chain_forget_frames(s); - reset_formats(s); - s->first->fmt_in = s->first->fmt_out = s->input; - - if (mp_audio_config_valid(&convert_early)) { - char *opts[] = {"deprecation-warning", "no", NULL}; - struct af_instance *new = af_prepend(s, s->first, "lavrresample", opts); - if (!new) - return AF_ERROR; - new->auto_inserted = true; - mp_audio_copy_config(new->data, &convert_early); - int rv = filter_reinit(new); - if (rv != AF_DETACH && rv != AF_OK) - return AF_ERROR; - MP_VERBOSE(s, "Moving up output conversion.\n"); - } - - // Start with the second filter, as the first filter is the special input - // filter which needs no initialization. - struct af_instance *af = s->first->next; - while (af) { - int rv = filter_reinit_with_conversion(s, af); - - switch (rv) { - case AF_OK: - af = af->next; - break; - case AF_FALSE: { - // If the format conversion is (probably) caused by spdif, then - // (as a feature) drop the filter, instead of failing hard. - int fmt_in1 = af->prev->data->format; - int fmt_in2 = af->fmt_in.format; - if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) { - bool spd1 = af_fmt_is_spdif(fmt_in1); - bool spd2 = af_fmt_is_spdif(fmt_in2); - if (spd1 != spd2 && af->next) { - MP_WARN(af, "Filter %s apparently cannot be used due to " - "spdif passthrough - removing it.\n", - af->info->name); - struct af_instance *aft = af->prev; - af_remove(s, af); - af = aft->next; - continue; - } - } - goto negotiate_error; - } - case AF_DETACH: { // Filter is redundant and wants to be unloaded - struct af_instance *aft = af->prev; // never NULL - af_remove(s, af); - af = aft->next; - break; - } - default: - MP_ERR(s, "Reinitialization did not work, " - "audio filter '%s' returned error code %i\n", - af->info->name, rv); - goto error; - } - } - - /* Set previously unset fields in s->output to those of the filter chain - * output. This is used to make the output format fixed, and even if you - * insert new filters or change the input format, the output format won't - * change. (Audio outputs generally can't change format at runtime.) */ - af_copy_unset_fields(&s->output, &s->filter_output); - if (mp_audio_config_equals(&s->output, &s->filter_output)) { - s->initialized = 1; - af_print_filter_chain(s, NULL, MSGL_V); - return AF_OK; - } - - goto error; - -negotiate_error: - MP_ERR(s, "Unable to convert audio input format to output format.\n"); -error: - s->initialized = -1; - af_print_filter_chain(s, af, MSGL_ERR); - return AF_ERROR; -} - -static int af_reinit(struct af_stream *s) -{ - int r = af_do_reinit(s, false); - if (r == AF_OK && mp_audio_config_valid(&s->output)) { - r = af_do_reinit(s, true); - if (r != AF_OK) { - MP_ERR(s, "Failed second pass filter negotiation.\n"); - r = af_do_reinit(s, false); - } - } - return r; -} - -// Uninit and remove all filters -void af_uninit(struct af_stream *s) -{ - while (s->first->next && s->first->next != s->last) - af_remove(s, s->first->next); - af_chain_forget_frames(s); - s->initialized = 0; -} - -struct af_stream *af_new(struct mpv_global *global) -{ - struct af_stream *s = talloc_zero(NULL, struct af_stream); - s->log = mp_log_new(s, global->log, "!af"); - - static const struct af_info in = { .name = "in" }; - s->first = talloc(s, struct af_instance); - *s->first = (struct af_instance) { - .full_name = "in", - .info = &in, - .log = s->log, - .control = input_control, - .filter_frame = dummy_filter, - .priv = s, - .data = &s->input, - }; - - static const struct af_info out = { .name = "out" }; - s->last = talloc(s, struct af_instance); - *s->last = (struct af_instance) { - .full_name = "out", - .info = &out, - .log = s->log, - .control = output_control, - .filter_frame = dummy_filter, - .priv = s, - .data = &s->filter_output, - }; - - s->first->next = s->last; - s->last->prev = s->first; - s->opts = global->opts; - s->global = global; - return s; -} - -void af_destroy(struct af_stream *s) -{ - af_uninit(s); - talloc_free(s); -} - -/* Initialize the stream "s". This function creates a new filter list - if necessary according to the values set in input and output. Input - and output should contain the format of the current movie and the - format of the preferred output respectively. The function is - reentrant i.e. if called with an already initialized stream the - stream will be reinitialized. - If one of the preferred output parameters is 0 the one that needs - no conversion is used (i.e. the output format in the last filter). - The return value is 0 if success and -1 if failure */ -int af_init(struct af_stream *s) -{ - // Precaution in case caller is misbehaving - mp_audio_set_null_data(&s->input); - mp_audio_set_null_data(&s->output); - - // Check if this is the first call - if (s->first->next == s->last) { - // Add all filters in the list (if there are any) - struct m_obj_settings *list = s->opts->af_settings; - for (int i = 0; list && list[i].name; i++) { - if (!list[i].enabled) - continue; - struct af_instance *af = - af_prepend(s, s->last, list[i].name, list[i].attribs); - if (!af) { - af_uninit(s); - s->initialized = -1; - return -1; - } - af->label = talloc_strdup(af, list[i].label); - } - } - - if (af_reinit(s) != AF_OK) { - // Something is stuffed audio out will not work - MP_ERR(s, "Could not create audio filter chain.\n"); - return -1; - } - return 0; -} - -/* Add filter during execution. This function adds the filter "name" - to the stream s. The filter will be inserted somewhere nice in the - list of filters. The return value is a pointer to the new filter, - If the filter couldn't be added the return value is NULL. */ -struct af_instance *af_add(struct af_stream *s, char *name, char *label, - char **args) -{ - assert(label); - - if (af_find_by_label(s, label)) - return NULL; - - struct af_instance *new = af_prepend(s, s->last, name, args); - if (!new) - return NULL; - new->label = talloc_strdup(new, label); - - // Reinitialize the filter list - if (af_reinit(s) != AF_OK) { - af_remove_by_label(s, label); - return NULL; - } - return af_find_by_label(s, label); -} - -struct af_instance *af_find_by_label(struct af_stream *s, char *label) -{ - for (struct af_instance *af = s->first; af; af = af->next) { - if (af->label && strcmp(af->label, label) == 0) - return af; - } - return NULL; -} - -/* Remove the first filter that matches this name. Return number of filters - * removed (0, 1), or a negative error code if reinit after removing failed. - */ -int af_remove_by_label(struct af_stream *s, char *label) -{ - struct af_instance *af = af_find_by_label(s, label); - if (!af) - return 0; - af_remove(s, af); - if (af_reinit(s) != AF_OK) { - af_uninit(s); - af_init(s); - return -1; - } - return 1; -} - -/* Calculate the total delay [seconds of output] caused by the filters */ -double af_calc_delay(struct af_stream *s) -{ - struct af_instance *af = s->first; - double delay = 0.0; - while (af) { - delay += af->delay; - for (int n = 0; n < af->num_out_queued; n++) - delay += af->out_queued[n]->samples / (double)af->data->rate; - af = af->next; - } - return delay; -} - -/* Send control to all filters, starting with the last until one accepts the - * command with AF_OK. Return the accepting filter. */ -struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg) -{ - int res = AF_UNKNOWN; - struct af_instance *filt = s->last; - while (filt) { - res = filt->control(filt, cmd, arg); - if (res == AF_OK) - return filt; - filt = filt->prev; - } - return NULL; -} - -/* Send control to all filters. Never stop, even if a filter returns AF_OK. */ -void af_control_all(struct af_stream *s, int cmd, void *arg) -{ - for (struct af_instance *af = s->first; af; af = af->next) - af->control(af, cmd, arg); -} - -int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label) -{ - char *label_str = bstrdup0(NULL, label); - struct af_instance *cur = af_find_by_label(s, label_str); - talloc_free(label_str); - if (cur) { - return cur->control ? cur->control(cur, cmd, arg) : CONTROL_NA; - } else { - return CONTROL_UNKNOWN; - } -} - -int af_send_command(struct af_stream *s, char *label, char *cmd, char *arg) -{ - char *args[2] = {cmd, arg}; - if (strcmp(label, "all") == 0) { - af_control_all(s, AF_CONTROL_COMMAND, args); - return 0; - } else { - return af_control_by_label(s, AF_CONTROL_COMMAND, args, bstr0(label)); - } -} - -// Used by filters to add a filtered frame to the output queue. -// Ownership of frame is transferred from caller to the filter chain. -void af_add_output_frame(struct af_instance *af, struct mp_audio *frame) -{ - if (frame) { - assert(mp_audio_config_equals(&af->fmt_out, frame)); - MP_TARRAY_APPEND(af, af->out_queued, af->num_out_queued, frame); - } -} - -static bool af_has_output_frame(struct af_instance *af) -{ - if (!af->num_out_queued && af->filter_out) { - if (af->filter_out(af) < 0) - MP_ERR(af, "Error filtering frame.\n"); - } - return af->num_out_queued > 0; -} - -static struct mp_audio *af_dequeue_output_frame(struct af_instance *af) -{ - struct mp_audio *res = NULL; - if (af_has_output_frame(af)) { - res = af->out_queued[0]; - MP_TARRAY_REMOVE_AT(af->out_queued, af->num_out_queued, 0); - } - return res; -} - -static void read_remaining(struct af_instance *af) -{ - int num_frames; - do { - num_frames = af->num_out_queued; - if (!af->filter_out || af->filter_out(af) < 0) - break; - } while (num_frames != af->num_out_queued); -} - -static int af_do_filter(struct af_instance *af, struct mp_audio *frame) -{ - if (frame) - assert(mp_audio_config_equals(&af->fmt_in, frame)); - int r = af->filter_frame(af, frame); - if (r < 0) - MP_ERR(af, "Error filtering frame.\n"); - return r; -} - -// Input a frame into the filter chain. Ownership of frame is transferred. -// Return >= 0 on success, < 0 on failure (even if output frames were produced) -int af_filter_frame(struct af_stream *s, struct mp_audio *frame) -{ - assert(frame); - if (s->initialized < 1) { - talloc_free(frame); - return -1; - } - return af_do_filter(s->first, frame); -} - -// Output the next queued frame (if any) from the full filter chain. -// The frame can be retrieved with af_read_output_frame(). -// eof: if set, assume there's no more input i.e. af_filter_frame() will -// not be called (until reset) - flush all internally delayed frames -// returns: -1: error, 0: no output, 1: output available -int af_output_frame(struct af_stream *s, bool eof) -{ - if (s->last->num_out_queued) - return 1; - if (s->initialized < 1) - return -1; - while (1) { - struct af_instance *last = NULL; - for (struct af_instance * cur = s->first; cur; cur = cur->next) { - // Flush remaining frames on EOF, but do that only if the previous - // filters have been flushed (i.e. they have no more output). - if (eof && !last) { - read_remaining(cur); - int r = af_do_filter(cur, NULL); - if (r < 0) - return r; - } - if (af_has_output_frame(cur)) - last = cur; - } - if (!last) - return 0; - if (!last->next) - return 1; - int r = af_do_filter(last->next, af_dequeue_output_frame(last)); - if (r < 0) - return r; - } -} - -struct mp_audio *af_read_output_frame(struct af_stream *s) -{ - if (!s->last->num_out_queued) - af_output_frame(s, false); - return af_dequeue_output_frame(s->last); -} - -void af_unread_output_frame(struct af_stream *s, struct mp_audio *frame) -{ - struct af_instance *af = s->last; - MP_TARRAY_INSERT_AT(af, af->out_queued, af->num_out_queued, 0, frame); -} - -// Make sure the caller can change data referenced by the frame. -// Return negative error code on failure (i.e. you can't write). -int af_make_writeable(struct af_instance *af, struct mp_audio *frame) -{ - return mp_audio_pool_make_writeable(af->out_pool, frame); -} - -void af_seek_reset(struct af_stream *s) -{ - af_control_all(s, AF_CONTROL_RESET, NULL); - af_chain_forget_frames(s); -} diff --git a/audio/filter/af.h b/audio/filter/af.h deleted file mode 100644 index 3a07a5465f..0000000000 --- a/audio/filter/af.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MPLAYER_AF_H -#define MPLAYER_AF_H - -#include <stdio.h> -#include <stdbool.h> -#include <sys/types.h> - -#include "config.h" -#if !(HAVE_LIBAF && HAVE_GPL) -#error "libaf/GPL disabled" -#endif - -#include "options/options.h" -#include "audio/format.h" -#include "audio/chmap.h" -#include "audio/audio.h" -#include "common/msg.h" -#include "common/common.h" - -struct af_instance; -struct mpv_global; - -// Number of channels -#define AF_NCH MP_NUM_CHANNELS - -// Flags for af->filter() -#define AF_FILTER_FLAG_EOF 1 - -/* Audio filter information not specific for current instance, but for - a specific filter */ -struct af_info { - const char *info; - const char *name; - int (*open)(struct af_instance *vf); - int priv_size; - const void *priv_defaults; - const struct m_option *options; - // For m_obj_desc.set_defaults - void (*set_defaults)(struct mpv_global *global, void *p); -}; - -// Linked list of audio filters -struct af_instance { - const struct af_info *info; - char *full_name; - struct mp_log *log; - struct MPOpts *opts; - struct mpv_global *global; - int (*control)(struct af_instance *af, int cmd, void *arg); - void (*uninit)(struct af_instance *af); - /* Feed a frame. The frame is NULL if EOF was reached, and the filter - * should drain all remaining buffered data. - * Use af_add_output_frame() to output data. The optional filter_out - * callback can be set to produce output frames gradually. - */ - int (*filter_frame)(struct af_instance *af, struct mp_audio *frame); - int (*filter_out)(struct af_instance *af); - void *priv; - struct mp_audio *data; // configuration and buffer for outgoing data stream - - struct af_instance *next; - struct af_instance *prev; - double delay; /* Delay caused by the filter, in seconds of audio consumed - * without corresponding output */ - bool auto_inserted; // inserted by af.c, such as conversion filters - char *label; - - struct mp_audio fmt_in, fmt_out; - - struct mp_audio **out_queued; - int num_out_queued; - - struct mp_audio_pool *out_pool; -}; - -// Current audio stream -struct af_stream { - int initialized; // 0: no, 1: yes, -1: attempted to, but failed - - // The first and last filter in the list - struct af_instance *first; - struct af_instance *last; - // The user sets the input format (what the decoder outputs), and sets some - // or all fields in output to the output format the AO accepts. - struct mp_audio input; - struct mp_audio output; - struct mp_audio filter_output; - - struct mp_log *log; - struct MPOpts *opts; - struct mpv_global *global; -}; - -// Return values -#define AF_DETACH (CONTROL_OK + 1) -#define AF_OK CONTROL_OK -#define AF_TRUE CONTROL_TRUE -#define AF_FALSE CONTROL_FALSE -#define AF_UNKNOWN CONTROL_UNKNOWN -#define AF_ERROR CONTROL_ERROR - -// Parameters for af_control_* -enum af_control { - AF_CONTROL_REINIT = 1, - AF_CONTROL_RESET, - AF_CONTROL_SET_PLAYBACK_SPEED, - AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE, - AF_CONTROL_GET_METADATA, - AF_CONTROL_COMMAND, -}; - -// Argument for AF_CONTROL_SET_PAN_LEVEL -typedef struct af_control_ext_s { - void* arg; // Argument - int ch; // Chanel number -} af_control_ext_t; - -struct af_stream *af_new(struct mpv_global *global); -void af_destroy(struct af_stream *s); -int af_init(struct af_stream *s); -void af_uninit(struct af_stream *s); -struct af_instance *af_add(struct af_stream *s, char *name, char *label, - char **args); -int af_remove_by_label(struct af_stream *s, char *label); -struct af_instance *af_find_by_label(struct af_stream *s, char *label); -struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg); -void af_control_all(struct af_stream *s, int cmd, void *arg); -int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label); -void af_seek_reset(struct af_stream *s); -int af_send_command(struct af_stream *s, char *label, char *cmd, char *arg); - -void af_add_output_frame(struct af_instance *af, struct mp_audio *frame); -int af_filter_frame(struct af_stream *s, struct mp_audio *frame); -int af_output_frame(struct af_stream *s, bool eof); -struct mp_audio *af_read_output_frame(struct af_stream *s); -void af_unread_output_frame(struct af_stream *s, struct mp_audio *frame); -int af_make_writeable(struct af_instance *af, struct mp_audio *frame); - -double af_calc_delay(struct af_stream *s); - -int af_test_output(struct af_instance *af, struct mp_audio *out); - -int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma); -float af_softclip(float a); - -#endif /* MPLAYER_AF_H */ diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c index c4af9b768b..3e1eef664c 100644 --- a/audio/filter/af_format.c +++ b/audio/filter/af_format.c @@ -15,18 +15,14 @@ * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ -#include <stdlib.h> - -#include <libavutil/common.h> - -#include "options/m_option.h" - +#include "audio/aframe.h" #include "audio/format.h" -#include "af.h" - -struct priv { - struct m_config *config; +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" +#include "options/m_option.h" +struct f_opts { int in_format; int in_srate; struct m_channels in_channels; @@ -37,98 +33,109 @@ struct priv { int fail; }; -static void force_in_params(struct af_instance *af, struct mp_audio *in) -{ - struct priv *priv = af->priv; - - if (priv->in_format != AF_FORMAT_UNKNOWN) - mp_audio_set_format(in, priv->in_format); - - if (priv->in_channels.num_chmaps > 0) - mp_audio_set_channels(in, &priv->in_channels.chmaps[0]); - - if (priv->in_srate) - in->rate = priv->in_srate; -} +struct priv { + struct f_opts *opts; + struct mp_pin *in_pin; +}; -static void force_out_params(struct af_instance *af, struct mp_audio *out) +static void process(struct mp_filter *f) { - struct priv *priv = af->priv; + struct priv *p = f->priv; - if (priv->out_format != AF_FORMAT_UNKNOWN) - mp_audio_set_format(out, priv->out_format); + if (!mp_pin_can_transfer_data(f->ppins[1], p->in_pin)) + return; - if (priv->out_channels.num_chmaps > 0) - mp_audio_set_channels(out, &priv->out_channels.chmaps[0]); + struct mp_frame frame = mp_pin_out_read(p->in_pin); - if (priv->out_srate) - out->rate = priv->out_srate; -} + if (p->opts->fail) { + MP_ERR(f, "Failing on purpose.\n"); + goto error; + } -static int control(struct af_instance *af, int cmd, void *arg) -{ - struct priv *priv = af->priv; + if (frame.type == MP_FRAME_EOF) { + mp_pin_in_write(f->ppins[1], frame); + return; + } - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; + if (frame.type != MP_FRAME_AUDIO) { + MP_ERR(f, "audio frame expected\n"); + goto error; + } - force_in_params(af, in); - mp_audio_copy_config(out, in); - force_out_params(af, out); + struct mp_aframe *in = frame.data; - if (in->nch != out->nch || in->bps != out->bps) { - MP_ERR(af, "Forced input/output formats are incompatible.\n"); - return AF_ERROR; + if (p->opts->out_channels.num_chmaps > 0) { + if (!mp_aframe_set_chmap(in, &p->opts->out_channels.chmaps[0])) { + MP_ERR(f, "could not force output channels\n"); + goto error; } + } - if (priv->fail) { - MP_ERR(af, "Failing on purpose.\n"); - return AF_ERROR; - } + if (p->opts->out_srate) + mp_aframe_set_rate(in, p->opts->out_srate); - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - } - return AF_UNKNOWN; -} + mp_pin_in_write(f->ppins[1], frame); + return; -static int filter(struct af_instance *af, struct mp_audio *data) -{ - if (data) - mp_audio_copy_config(data, af->data); - af_add_output_frame(af, data); - return 0; +error: + mp_frame_unref(&frame); + mp_filter_internal_mark_failed(f); } -static int af_open(struct af_instance *af) +static const struct mp_filter_info af_format_filter = { + .name = "format", + .priv_size = sizeof(struct priv), + .process = process, +}; + +static struct mp_filter *af_format_create(struct mp_filter *parent, + void *options) { - af->control = control; - af->filter_frame = filter; + struct mp_filter *f = mp_filter_create(parent, &af_format_filter); + if (!f) { + talloc_free(options); + return NULL; + } - force_in_params(af, af->data); - force_out_params(af, af->data); + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); - return AF_OK; -} + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); -#define OPT_BASE_STRUCT struct priv + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); -const struct af_info af_info_format = { - .info = "Force audio format", - .name = "format", - .open = af_open, - .priv_size = sizeof(struct priv), - .options = (const struct m_option[]) { - OPT_AUDIOFORMAT("format", in_format, 0), - OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000), - OPT_CHANNELS("channels", in_channels, 0, .min = 1), - OPT_AUDIOFORMAT("out-format", out_format, 0), - OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000), - OPT_CHANNELS("out-channels", out_channels, 0, .min = 1), - OPT_FLAG("fail", fail, 0), - {0} + if (p->opts->in_format) + mp_autoconvert_add_afmt(conv, p->opts->in_format); + if (p->opts->in_srate) + mp_autoconvert_add_srate(conv, p->opts->in_srate); + if (p->opts->in_channels.num_chmaps > 0) + mp_autoconvert_add_chmap(conv, &p->opts->in_channels.chmaps[0]); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + p->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_format = { + .desc = { + .name = "format", + .description = "Force audio format", + .priv_size = sizeof(struct f_opts), + .options = (const struct m_option[]) { + OPT_AUDIOFORMAT("format", in_format, 0), + OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000), + OPT_CHANNELS("channels", in_channels, 0, .min = 1), + OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000), + OPT_CHANNELS("out-channels", out_channels, 0, .min = 1), + OPT_FLAG("fail", fail, 0), + {0} + }, }, + .create = af_format_create, }; diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c index 14aa53b980..c7582cf52b 100644 --- a/audio/filter/af_lavcac3enc.c +++ b/audio/filter/af_lavcac3enc.c @@ -31,14 +31,17 @@ #include <libavutil/bswap.h> #include <libavutil/mem.h> -#include "config.h" - -#include "common/av_common.h" -#include "common/common.h" -#include "af.h" -#include "audio/audio_buffer.h" +#include "audio/aframe.h" #include "audio/chmap_sel.h" #include "audio/fmt-conversion.h" +#include "audio/format.h" +#include "common/av_common.h" +#include "common/common.h" +#include "filters/f_autoconvert.h" +#include "filters/f_utils.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" +#include "options/m_option.h" #define AC3_MAX_CHANNELS 6 @@ -49,173 +52,89 @@ const uint16_t ac3_bitrate_tab[19] = { 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 }; -// Data for specific instances of this filter -typedef struct af_ac3enc_s { +struct f_opts { + int add_iec61937_header; + int bit_rate; + int min_channel_num; + char *encoder; + char **avopts; +}; + +struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe *in_frame; + struct mp_aframe_pool *out_pool; + struct AVCodec *lavc_acodec; struct AVCodecContext *lavc_actx; int bit_rate; - struct mp_audio *input; // frame passed to libavcodec - struct mp_audio *pending; // unconsumed input data - int in_samples; // samples of input per AC3 frame int out_samples; // upper bound on encoded output per AC3 frame - int64_t encoder_buffered; - - int cfg_add_iec61937_header; - int cfg_bit_rate; - int cfg_min_channel_num; - char *cfg_encoder; - char **cfg_avopts; -} af_ac3enc_t; - -// fmt carries the input format. Change it to the best next-possible format -// the encoder likely accepts. -static void select_encode_format(AVCodecContext *c, struct mp_audio *fmt) -{ - int formats[AF_FORMAT_COUNT]; - af_get_best_sample_formats(fmt->format, formats); - - for (int n = 0; formats[n]; n++) { - const enum AVSampleFormat *lf = c->codec->sample_fmts; - for (int i = 0; lf && lf[i] != AV_SAMPLE_FMT_NONE; i++) { - int mpfmt = af_from_avformat(lf[i]); - if (mpfmt && mpfmt == formats[n]) { - mp_audio_set_format(fmt, mpfmt); - goto done_fmt; - } - } - } -done_fmt: ; +}; - int rate = - af_select_best_samplerate(fmt->rate, c->codec->supported_samplerates); - if (rate > 0) - fmt->rate = rate; +static bool reinit(struct mp_filter *f) +{ + struct priv *s = f->priv; - struct mp_chmap_sel sel = {0}; - const uint64_t *lch = c->codec->channel_layouts; - for (int n = 0; lch && lch[n]; n++) { - struct mp_chmap chmap = {0}; - mp_chmap_from_lavc(&chmap, lch[n]); - mp_chmap_sel_add_map(&sel, &chmap); - } - struct mp_chmap res = fmt->channels; - mp_chmap_sel_adjust(&sel, &res); - if (!mp_chmap_is_empty(&res)) - mp_audio_set_channels(fmt, &res); -} + mp_aframe_reset(s->cur_format); -// Initialization and runtime control -static int control(struct af_instance *af, int cmd, void *arg) -{ - af_ac3enc_t *s = af->priv; static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \ {0, 96000, 192000, 256000, 384000, 448000, 448000}; - switch (cmd){ - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - - if (!af_fmt_is_pcm(in->format) || in->nch < s->cfg_min_channel_num) - return AF_DETACH; - - // At least currently, the AC3 encoder doesn't export sample rates. - in->rate = 48000; - select_encode_format(s->lavc_actx, in); - - af->data->rate = in->rate; - mp_audio_set_format(af->data, AF_FORMAT_S_AC3); - mp_audio_set_num_channels(af->data, 2); - - if (!mp_audio_config_equals(in, &orig_in)) - return AF_FALSE; - - if (s->cfg_add_iec61937_header) { - s->out_samples = AC3_FRAME_SIZE; - } else { - s->out_samples = AC3_MAX_CODED_FRAME_SIZE / af->data->sstride; - } - - mp_audio_copy_config(s->input, in); - - talloc_free(s->pending); - s->pending = NULL; - - MP_DBG(af, "reinit: %d, %d, %d.\n", in->nch, in->rate, s->in_samples); + if (s->opts->add_iec61937_header) { + s->out_samples = AC3_FRAME_SIZE; + } else { + s->out_samples = AC3_MAX_CODED_FRAME_SIZE / + mp_aframe_get_sstride(s->in_frame); + } - int bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[in->nch]; + int format = mp_aframe_get_format(s->in_frame); + int rate = mp_aframe_get_rate(s->in_frame); + struct mp_chmap chmap = {0}; + mp_aframe_get_chmap(s->in_frame, &chmap); - if (s->lavc_actx->channels != in->nch || - s->lavc_actx->sample_rate != in->rate || - s->lavc_actx->bit_rate != bit_rate) - { - avcodec_close(s->lavc_actx); + int bit_rate = s->bit_rate; + if (!bit_rate && chmap.num < AC3_MAX_CHANNELS + 1) + bit_rate = default_bit_rate[chmap.num]; - // Put sample parameters - s->lavc_actx->sample_fmt = af_to_avformat(in->format); - s->lavc_actx->channels = in->nch; - s->lavc_actx->channel_layout = mp_chmap_to_lavc(&in->channels); - s->lavc_actx->sample_rate = in->rate; - s->lavc_actx->bit_rate = bit_rate; + avcodec_close(s->lavc_actx); - if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) { - MP_ERR(af, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate); - return AF_ERROR; - } + // Put sample parameters + s->lavc_actx->sample_fmt = af_to_avformat(format); + s->lavc_actx->channels = chmap.num; + s->lavc_actx->channel_layout = mp_chmap_to_lavc(&chmap); + s->lavc_actx->sample_rate = rate; + s->lavc_actx->bit_rate = bit_rate; - if (s->lavc_actx->frame_size < 1) { - MP_ERR(af, "encoder didn't specify input frame size\n"); - return AF_ERROR; - } - } - s->in_samples = s->lavc_actx->frame_size; - mp_audio_realloc(s->input, s->in_samples); - s->input->samples = 0; - s->encoder_buffered = 0; - return AF_OK; + if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) { + MP_ERR(f, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate); + return false; } - case AF_CONTROL_RESET: - if (avcodec_is_open(s->lavc_actx)) - avcodec_flush_buffers(s->lavc_actx); - talloc_free(s->pending); - s->pending = NULL; - s->input->samples = 0; - s->encoder_buffered = 0; - return AF_OK; - } - return AF_UNKNOWN; -} - -// Deallocate memory -static void uninit(struct af_instance* af) -{ - af_ac3enc_t *s = af->priv; - if (s) { - avcodec_free_context(&s->lavc_actx); - talloc_free(s->pending); + if (s->lavc_actx->frame_size < 1) { + MP_ERR(f, "encoder didn't specify input frame size\n"); + return false; } + + mp_aframe_config_copy(s->cur_format, s->in_frame); + return true; } -static void update_delay(struct af_instance *af) +static void reset(struct mp_filter *f) { - af_ac3enc_t *s = af->priv; - af->delay = ((s->pending ? s->pending->samples : 0) + s->input->samples + - s->encoder_buffered) / (double)s->input->rate; + struct priv *s = f->priv; + + TA_FREEP(&s->in_frame); } -static int filter_frame(struct af_instance *af, struct mp_audio *audio) +static void destroy(struct mp_filter *f) { - af_ac3enc_t *s = af->priv; + struct priv *s = f->priv; - // filter_output must have been called until no output was produced. - if (s->pending && s->pending->samples) - MP_ERR(af, "broken data flow\n"); - - talloc_free(s->pending); - s->pending = audio; - update_delay(af); - return 0; + reset(f); + avcodec_free_context(&s->lavc_actx); } static void swap_16(uint16_t *ptr, size_t size) @@ -224,105 +143,84 @@ static void swap_16(uint16_t *ptr, size_t size) ptr[n] = av_bswap16(ptr[n]); } -// Copy data from input frame to encode frame (because libavcodec wants a full -// AC3 frame for encoding, while filter input frames can be smaller or larger). -// Return true if the frame is complete. -static bool fill_buffer(struct af_instance *af) -{ - af_ac3enc_t *s = af->priv; - - af->delay = 0; - - if (s->pending) { - if (!mp_audio_is_writeable(s->input)) - assert(s->input->samples == 0); // we can't have sent a partial frame - mp_audio_realloc_min(s->input, s->in_samples); - int copy = MPMIN(s->in_samples - s->input->samples, s->pending->samples); - s->input->samples += copy; - mp_audio_copy(s->input, s->input->samples - copy, s->pending, 0, copy); - mp_audio_skip_samples(s->pending, copy); - } - update_delay(af); - return s->input->samples >= s->in_samples; -} - -// Return <0 on error, 0 on need more input, 1 on success (and *frame set). -// To actually advance the read pointer, set s->input->samples=0 afterwards. -static int read_input_frame(struct af_instance *af, AVFrame *frame) +static void process(struct mp_filter *f) { - af_ac3enc_t *s = af->priv; - if (!fill_buffer(af)) - return 0; // need more input + struct priv *s = f->priv; - if (mp_audio_to_avframe(s->input, frame) < 0) - return -1; - - return 1; -} - -static int filter_out(struct af_instance *af) -{ - af_ac3enc_t *s = af->priv; - - if (!s->pending) - return 0; - - AVFrame *frame = av_frame_alloc(); - if (!frame) { - MP_FATAL(af, "Could not allocate memory \n"); - return -1; - } - int err = -1; + if (!mp_pin_in_needs_data(f->ppins[1])) + return; + bool err = true; + struct mp_aframe *out = NULL; AVPacket pkt = {0}; av_init_packet(&pkt); // Send input as long as it wants. while (1) { - err = read_input_frame(af, frame); - if (err < 0) + if (avcodec_is_open(s->lavc_actx)) { + int lavc_ret = avcodec_receive_packet(s->lavc_actx, &pkt); + if (lavc_ret >= 0) + break; + if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) { + MP_FATAL(f, "Encode failed (receive).\n"); + goto done; + } + } + AVFrame *frame = NULL; + struct mp_frame input = mp_pin_out_read(s->in_pin); + // The following code assumes no sample data buffering in the encoder. + if (input.type == MP_FRAME_EOF) { + mp_pin_in_write(f->ppins[1], input); + return; + } else if (input.type == MP_FRAME_AUDIO) { + TA_FREEP(&s->in_frame); + s->in_frame = input.data; + frame = mp_frame_to_av(input, NULL); + if (!frame) + goto done; + if (mp_aframe_get_channels(s->in_frame) < s->opts->min_channel_num) { + // Just pass it through. + s->in_frame = NULL; + mp_pin_in_write(f->ppins[1], input); + return; + } + if (!mp_aframe_config_equals(s->in_frame, s->cur_format)) { + if (!reinit(f)) + goto done; + } + } else if (input.type) { goto done; - if (err == 0) - break; - err = -1; + } else { + return; // no data yet + } int lavc_ret = avcodec_send_frame(s->lavc_actx, frame); - // On EAGAIN, we're supposed to read remaining output. - if (lavc_ret == AVERROR(EAGAIN)) - break; - if (lavc_ret < 0) { - MP_FATAL(af, "Encode failed.\n"); + av_frame_free(&frame); + if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) { + MP_FATAL(f, "Encode failed (send).\n"); goto done; } - s->encoder_buffered += s->input->samples; - s->input->samples = 0; - } - int lavc_ret = avcodec_receive_packet(s->lavc_actx, &pkt); - if (lavc_ret == AVERROR(EAGAIN)) { - // Need to buffer more input. - err = 0; - goto done; - } - if (lavc_ret < 0) { - MP_FATAL(af, "Encode failed.\n"); - goto done; } - MP_DBG(af, "avcodec_encode_audio got %d, pending %d.\n", - pkt.size, s->pending->samples + s->input->samples); + if (!s->in_frame) + goto done; - s->encoder_buffered -= AC3_FRAME_SIZE; + out = mp_aframe_create(); + mp_aframe_set_format(out, AF_FORMAT_S_AC3); + mp_aframe_set_chmap(out, &(struct mp_chmap)MP_CHMAP_INIT_STEREO); + mp_aframe_set_rate(out, 48000); - struct mp_audio *out = - mp_audio_pool_get(af->out_pool, af->data, s->out_samples); - if (!out) + if (mp_aframe_pool_allocate(s->out_pool, out, s->out_samples) < 0) goto done; - mp_audio_copy_attributes(out, s->pending); + + int sstride = mp_aframe_get_sstride(out); + + mp_aframe_copy_attributes(out, s->in_frame); int frame_size = pkt.size; int header_len = 0; char hdr[8]; - if (s->cfg_add_iec61937_header && pkt.size > 5) { + if (s->opts->add_iec61937_header && pkt.size > 5) { int bsmod = pkt.data[5] & 0x7; int len = frame_size; @@ -336,48 +234,69 @@ static int filter_out(struct af_instance *af) AV_WL16(hdr + 6, len << 3); // number of bits in payload } - if (frame_size > out->samples * out->sstride) + if (frame_size > s->out_samples * sstride) abort(); - char *buf = (char *)out->planes[0]; + uint8_t **planes = mp_aframe_get_data_rw(out); + if (!planes) + goto done; + char *buf = planes[0]; memcpy(buf, hdr, header_len); memcpy(buf + header_len, pkt.data, pkt.size); memset(buf + header_len + pkt.size, 0, frame_size - (header_len + pkt.size)); swap_16((uint16_t *)(buf + header_len), pkt.size / 2); - out->samples = frame_size / out->sstride; - af_add_output_frame(af, out); + mp_aframe_set_size(out, frame_size / sstride); + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); + out = NULL; err = 0; done: av_packet_unref(&pkt); - av_frame_free(&frame); - update_delay(af); - return err; + talloc_free(out); + if (err) + mp_filter_internal_mark_failed(f); } -static int af_open(struct af_instance* af){ +static const struct mp_filter_info af_lavcac3enc_filter = { + .name = "lavcac3enc", + .priv_size = sizeof(struct priv), + .process = process, + .reset = reset, + .destroy = destroy, +}; - af_ac3enc_t *s = af->priv; - af->control=control; - af->uninit=uninit; - af->filter_frame = filter_frame; - af->filter_out = filter_out; +static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent, + void *options) +{ + struct mp_filter *f = mp_filter_create(parent, &af_lavcac3enc_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"); - s->lavc_acodec = avcodec_find_encoder_by_name(s->cfg_encoder); + struct priv *s = f->priv; + s->opts = talloc_steal(s, options); + s->cur_format = talloc_steal(s, mp_aframe_create()); + s->out_pool = mp_aframe_pool_create(s); + + s->lavc_acodec = avcodec_find_encoder_by_name(s->opts->encoder); if (!s->lavc_acodec) { - MP_ERR(af, "Couldn't find encoder %s.\n", s->cfg_encoder); - return AF_ERROR; + MP_ERR(f, "Couldn't find encoder %s.\n", s->opts->encoder); + goto error; } s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec); if (!s->lavc_actx) { - MP_ERR(af, "Audio LAVC, couldn't allocate context!\n"); - return AF_ERROR; + MP_ERR(f, "Audio LAVC, couldn't allocate context!\n"); + goto error; } - if (mp_set_avopts(af->log, s->lavc_actx, s->cfg_avopts) < 0) - return AF_ERROR; + if (mp_set_avopts(f->log, s->lavc_actx, s->opts->avopts) < 0) + goto error; // For this one, we require the decoder to expert lists of all supported // parameters. (Not all decoders do that, but the ones we're interested @@ -385,50 +304,85 @@ static int af_open(struct af_instance* af){ if (!s->lavc_acodec->sample_fmts || !s->lavc_acodec->channel_layouts) { - MP_ERR(af, "Audio encoder doesn't list supported parameters.\n"); - return AF_ERROR; + MP_ERR(f, "Audio encoder doesn't list supported parameters.\n"); + goto error; } - s->input = talloc_zero(s, struct mp_audio); - - if (s->cfg_bit_rate) { + if (s->opts->bit_rate) { int i; for (i = 0; i < 19; i++) { - if (ac3_bitrate_tab[i] == s->cfg_bit_rate) { + if (ac3_bitrate_tab[i] == s->opts->bit_rate) { s->bit_rate = ac3_bitrate_tab[i] * 1000; break; } } if (i >= 19) { - MP_WARN(af, "unable set unsupported bitrate %d, using default " + MP_WARN(f, "unable set unsupported bitrate %d, using default " "bitrate (check manpage to see supported bitrates).\n", - s->cfg_bit_rate); + s->opts->bit_rate); } } - return AF_OK; -} + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); -#define OPT_BASE_STRUCT struct af_ac3enc_s + const enum AVSampleFormat *lf = s->lavc_acodec->sample_fmts; + for (int i = 0; lf && lf[i] != AV_SAMPLE_FMT_NONE; i++) { + int mpfmt = af_from_avformat(lf[i]); + if (mpfmt) + mp_autoconvert_add_afmt(conv, mpfmt); + } -const struct af_info af_info_lavcac3enc = { - .info = "runtime encode to ac3 using libavcodec", - .name = "lavcac3enc", - .open = af_open, - .priv_size = sizeof(struct af_ac3enc_s), - .priv_defaults = &(const struct af_ac3enc_s){ - .cfg_add_iec61937_header = 1, - .cfg_bit_rate = 640, - .cfg_min_channel_num = 3, - .cfg_encoder = "ac3", - }, - .options = (const struct m_option[]) { - OPT_FLAG("tospdif", cfg_add_iec61937_header, 0), - OPT_CHOICE_OR_INT("bitrate", cfg_bit_rate, 0, 32, 640, - ({"auto", 0}, {"default", 0})), - OPT_INTRANGE("minch", cfg_min_channel_num, 0, 2, 6), - OPT_STRING("encoder", cfg_encoder, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} + const uint64_t *lch = s->lavc_acodec->channel_layouts; + for (int n = 0; lch && lch[n]; n++) { + struct mp_chmap chmap = {0}; + mp_chmap_from_lavc(&chmap, lch[n]); + if (mp_chmap_is_valid(&chmap)) + mp_autoconvert_add_chmap(conv, &chmap); + } + + // At least currently, the AC3 encoder doesn't export sample rates. + mp_autoconvert_add_srate(conv, 48000); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + + struct mp_filter *fs = mp_fixed_aframe_size_create(f, AC3_FRAME_SIZE, true); + if (!fs) + abort(); + + mp_pin_connect(fs->pins[0], conv->f->pins[1]); + s->in_pin = fs->pins[1]; + + return f; + +error: + talloc_free(f); + return NULL; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_lavcac3enc = { + .desc = { + .description = "runtime encode to ac3 using libavcodec", + .name = "lavcac3enc", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .add_iec61937_header = 1, + .bit_rate = 640, + .min_channel_num = 3, + .encoder = "ac3", + }, + .options = (const struct m_option[]) { + OPT_FLAG("tospdif", add_iec61937_header, 0), + OPT_CHOICE_OR_INT("bitrate", bit_rate, 0, 32, 640, + ({"auto", 0}, {"default", 0})), + OPT_INTRANGE("minch", min_channel_num, 0, 2, 6), + OPT_STRING("encoder", encoder, 0), + OPT_KEYVALUELIST("o", avopts, 0), + {0} + }, }, + .create = af_lavcac3enc_create, }; diff --git a/audio/filter/af_lavfi.c b/audio/filter/af_lavfi.c deleted file mode 100644 index ab8a026de7..0000000000 --- a/audio/filter/af_lavfi.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * This file is part of mpv. - * - * Filter graph creation code taken from FFmpeg ffplay.c (LGPL 2.1 or later) - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <stdint.h> -#include <stdio.h> -#include <assert.h> - -#include <libavutil/avstring.h> -#include <libavutil/mem.h> -#include <libavutil/mathematics.h> -#include <libavutil/rational.h> -#include <libavutil/samplefmt.h> -#include <libavutil/time.h> -#include <libavutil/opt.h> -#include <libavfilter/avfilter.h> -#include <libavfilter/buffersink.h> -#include <libavfilter/buffersrc.h> - -#include "config.h" - -#include "audio/format.h" -#include "audio/fmt-conversion.h" -#include "af.h" - -#include "common/av_common.h" -#include "common/tags.h" - -#include "options/m_option.h" - -// FFmpeg and Libav have slightly different APIs, just enough to cause us -// unnecessary pain. <Expletive deleted.> -#if LIBAVFILTER_VERSION_MICRO < 100 -#define graph_parse(graph, filters, inputs, outputs, log_ctx) \ - avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx) -#define avfilter_graph_send_command(a, b, c, d, e, f, g) -1 -#else -#define graph_parse(graph, filters, inputs, outputs, log_ctx) \ - avfilter_graph_parse_ptr(graph, filters, &(inputs), &(outputs), log_ctx) -#endif - -struct priv { - // Single filter bridge, instead of a graph. - bool is_bridge; - - AVFilterGraph *graph; - AVFilterContext *in; - AVFilterContext *out; - - int64_t samples_in; - - AVRational timebase_out; - - bool eof; - - struct mp_tags *metadata; - - // options - char *cfg_graph; - char **cfg_avopts; - char *cfg_filter_name; - char **cfg_filter_opts; -}; - -static void destroy_graph(struct af_instance *af) -{ - struct priv *p = af->priv; - avfilter_graph_free(&p->graph); - p->in = p->out = NULL; - p->samples_in = 0; - p->eof = false; -} - -static bool recreate_graph(struct af_instance *af, struct mp_audio *config) -{ - void *tmp = talloc_new(NULL); - struct priv *p = af->priv; - AVFilterContext *in = NULL, *out = NULL; - bool ok = false; - - if (!p->is_bridge && bstr0(p->cfg_graph).len == 0) { - MP_FATAL(af, "lavfi: no filter graph set\n"); - return false; - } - - destroy_graph(af); - - AVFilterGraph *graph = avfilter_graph_alloc(); - if (!graph) - goto error; - - if (mp_set_avopts(af->log, graph, p->cfg_avopts) < 0) - goto error; - - AVFilterInOut *outputs = avfilter_inout_alloc(); - AVFilterInOut *inputs = avfilter_inout_alloc(); - if (!outputs || !inputs) - goto error; - - char *src_args = talloc_asprintf(tmp, - "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:" - "channel_layout=0x%"PRIx64, config->rate, - av_get_sample_fmt_name(af_to_avformat(config->format)), - 1, config->rate, mp_chmap_to_lavc(&config->channels)); - - if (avfilter_graph_create_filter(&in, avfilter_get_by_name("abuffer"), - "src", src_args, NULL, graph) < 0) - goto error; - - if (avfilter_graph_create_filter(&out, avfilter_get_by_name("abuffersink"), - "out", NULL, NULL, graph) < 0) - goto error; - - if (p->is_bridge) { - AVFilterContext *filter = avfilter_graph_alloc_filter(graph, - avfilter_get_by_name(p->cfg_filter_name), "filter"); - if (!filter) - goto error; - - if (mp_set_avopts(af->log, filter->priv, p->cfg_filter_opts) < 0) - goto error; - - if (avfilter_init_str(filter, NULL) < 0) - goto error; - - // Yep, we have to manually link those filters. - if (filter->nb_inputs != 1 || - avfilter_pad_get_type(filter->input_pads, 0) != AVMEDIA_TYPE_AUDIO || - filter->nb_outputs != 1 || - avfilter_pad_get_type(filter->output_pads, 0) != AVMEDIA_TYPE_AUDIO) - { - MP_ERR(af, "The filter is required to have 1 audio input pad and " - "1 audio output pad.\n"); - goto error; - } - if (avfilter_link(in, 0, filter, 0) < 0 || - avfilter_link(filter, 0, out, 0) < 0) - { - MP_ERR(af, "Failed to link filter.\n"); - goto error; - } - } else { - MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = in; - - inputs->name = av_strdup("out"); - inputs->filter_ctx = out; - - if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) - goto error; - } - - if (avfilter_graph_config(graph, NULL) < 0) - goto error; - - p->in = in; - p->out = out; - p->graph = graph; - - assert(out->nb_inputs == 1); - assert(in->nb_outputs == 1); - - ok = true; -error: - - if (!ok) { - MP_FATAL(af, "Can't configure libavfilter graph.\n"); - avfilter_graph_free(&graph); - } - avfilter_inout_free(&inputs); - avfilter_inout_free(&outputs); - talloc_free(tmp); - return ok; -} - -static void reset(struct af_instance *af) -{ - if (!recreate_graph(af, &af->fmt_in)) - MP_FATAL(af, "Can't recreate libavfilter filter after a seek reset.\n"); -} - -static int control(struct af_instance *af, int cmd, void *arg) -{ - struct priv *p = af->priv; - - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; - - if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE) - mp_audio_set_format(in, AF_FORMAT_FLOAT); - - // Removing this requires fixing AVFrame.data vs. AVFrame.extended_data - if (in->channels.num > AV_NUM_DATA_POINTERS) - return AF_ERROR; - - if (!mp_chmap_is_lavc(&in->channels)) - mp_chmap_reorder_to_lavc(&in->channels); // will always work - - if (!recreate_graph(af, in)) - return AF_ERROR; - - AVFilterLink *l_out = p->out->inputs[0]; - - out->rate = l_out->sample_rate; - - mp_audio_set_format(out, af_from_avformat(l_out->format)); - - struct mp_chmap out_cm; - mp_chmap_from_lavc(&out_cm, l_out->channel_layout); - mp_audio_set_channels(out, &out_cm); - - if (!mp_audio_config_valid(out) || out->channels.num > AV_NUM_DATA_POINTERS) - return AF_ERROR; - - p->timebase_out = l_out->time_base; - - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - case AF_CONTROL_COMMAND: { - if (!p->graph) - break; - char **args = arg; - return avfilter_graph_send_command(p->graph, "all", - args[0], args[1], &(char){0}, 0, 0) - >= 0 ? CONTROL_OK : CONTROL_ERROR; - } - case AF_CONTROL_GET_METADATA: - if (p->metadata) { - *(struct mp_tags *)arg = *p->metadata; - return CONTROL_OK; - } - return CONTROL_NA; - case AF_CONTROL_RESET: - reset(af); - return AF_OK; - } - return AF_UNKNOWN; -} - -static void get_metadata_from_av_frame(struct af_instance *af, AVFrame *frame) -{ -#if LIBAVUTIL_VERSION_MICRO >= 100 - struct priv *p = af->priv; - if (!p->metadata) - p->metadata = talloc_zero(p, struct mp_tags); - - mp_tags_copy_from_av_dictionary(p->metadata, frame->metadata); -#endif -} - -static int filter_frame(struct af_instance *af, struct mp_audio *data) -{ - struct priv *p = af->priv; - AVFrame *frame = NULL; - - if (p->eof && data) - reset(af); - - if (!p->graph) - goto error; - - if (!data) { - if (p->eof) - return 0; - p->eof = true; - } - - if (data) { - frame = mp_audio_to_avframe_and_unref(data); - data = NULL; - if (!frame) - goto error; - - // Timebase is 1/sample_rate - frame->pts = p->samples_in; - p->samples_in += frame->nb_samples; - } - - if (av_buffersrc_add_frame(p->in, frame) < 0) - goto error; - - av_frame_free(&frame); - talloc_free(data); - return 0; -error: - av_frame_free(&frame); - talloc_free(data); - return -1; -} - -static int filter_out(struct af_instance *af) -{ - struct priv *p = af->priv; - - if (!p->graph) - goto error; - - AVFrame *frame = av_frame_alloc(); - if (!frame) - goto error; - - int err = av_buffersink_get_frame(p->out, frame); - if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) { - // Not an error situation - no more output buffers in queue. - // AVERROR_EOF means we shouldn't even give the filter more - // input, but we don't handle that completely correctly. - av_frame_free(&frame); - p->eof |= err == AVERROR_EOF; - return 0; - } - - struct mp_audio *out = mp_audio_from_avframe(frame); - if (!out) - goto error; - - mp_audio_copy_config(out, af->data); - - if (frame->pts != AV_NOPTS_VALUE) { - double in_time = p->samples_in / (double)af->fmt_in.rate; - double out_time = frame->pts * av_q2d(p->timebase_out); - // Need pts past the last output sample. - out_time += out->samples / (double)out->rate; - - af->delay = in_time - out_time; - } - - get_metadata_from_av_frame(af, frame); - af_add_output_frame(af, out); - av_frame_free(&frame); - return 0; -error: - av_frame_free(&frame); - return -1; -} - -static void uninit(struct af_instance *af) -{ - destroy_graph(af); -} - -static int af_open(struct af_instance *af) -{ - struct priv *p = af->priv; - - af->control = control; - af->uninit = uninit; - af->filter_frame = filter_frame; - af->filter_out = filter_out; - - if (p->is_bridge) { - if (!p->cfg_filter_name) { - MP_ERR(af, "Filter name not set!\n"); - return 0; - } - if (!avfilter_get_by_name(p->cfg_filter_name)) { - MP_ERR(af, "libavfilter filter '%s' not found!\n", p->cfg_filter_name); - return 0; - } - } - return AF_OK; -} - -#define OPT_BASE_STRUCT struct priv - -const struct af_info af_info_lavfi = { - .info = "libavfilter bridge", - .name = "lavfi", - .open = af_open, - .priv_size = sizeof(struct priv), - .options = (const struct m_option[]) { - OPT_STRING("graph", cfg_graph, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} - }, -}; - -const struct af_info af_info_lavfi_bridge = { - .info = "libavfilter bridge (explicit options)", - .name = "lavfi-bridge", - .open = af_open, - .priv_size = sizeof(struct priv), - .priv_defaults = &(const struct priv){ - .is_bridge = true, - }, - .options = (const struct m_option[]) { - OPT_STRING("name", cfg_filter_name, M_OPT_MIN, .min = 1), - OPT_KEYVALUELIST("opts", cfg_filter_opts, 0), - OPT_STRING("graph", cfg_graph, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} - }, -}; diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c index f13093da40..baa78acb6e 100644 --- a/audio/filter/af_lavrresample.c +++ b/audio/filter/af_lavrresample.c @@ -32,132 +32,19 @@ #include "common/av_common.h" #include "common/msg.h" +#include "filters/f_swresample.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "options/m_config.h" #include "options/m_option.h" -#include "audio/filter/af.h" -#include "audio/fmt-conversion.h" -#include "osdep/endian.h" -#include "audio/aconverter.h" +#include "options/options.h" struct af_resample { int allow_detach; - double playback_speed; struct mp_resample_opts opts; int global_normalize; - struct mp_aconverter *converter; - int deprecation_warning; }; -static int control(struct af_instance *af, int cmd, void *arg) -{ - struct af_resample *s = af->priv; - - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio *out = af->data; - struct mp_audio orig_in = *in; - - if (((out->rate == in->rate) || (out->rate == 0)) && - (out->format == in->format) && - (mp_chmap_equals(&out->channels, &in->channels) || out->nch == 0) && - s->allow_detach && s->playback_speed == 1.0) - return AF_DETACH; - - if (out->rate == 0) - out->rate = in->rate; - - if (mp_chmap_is_empty(&out->channels)) - mp_audio_set_channels(out, &in->channels); - - if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE) - mp_audio_set_format(in, AF_FORMAT_FLOAT); - if (af_to_avformat(out->format) == AV_SAMPLE_FMT_NONE) - mp_audio_set_format(out, in->format); - - int r = ((in->format == orig_in.format) && - mp_chmap_equals(&in->channels, &orig_in.channels)) - ? AF_OK : AF_FALSE; - - if (r == AF_OK) { - if (!mp_aconverter_reconfig(s->converter, - in->rate, in->format, in->channels, - out->rate, out->format, out->channels)) - r = AF_ERROR; - } - return r; - } - case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: { - s->playback_speed = *(double *)arg; - return AF_OK; - } - case AF_CONTROL_RESET: - mp_aconverter_flush(s->converter); - return AF_OK; - } - return AF_UNKNOWN; -} - -static void uninit(struct af_instance *af) -{ - struct af_resample *s = af->priv; - - talloc_free(s->converter); -} - -static int filter(struct af_instance *af, struct mp_audio *in) -{ - struct af_resample *s = af->priv; - - mp_aconverter_set_speed(s->converter, s->playback_speed); - - af->filter_out(af); - - struct mp_aframe *aframe = mp_audio_to_aframe(in); - if (!aframe && in) - return -1; - talloc_free(in); - bool ok = mp_aconverter_write_input(s->converter, aframe); - if (!ok) - talloc_free(aframe); - - return ok ? 0 : -1; -} - -static int filter_out(struct af_instance *af) -{ - struct af_resample *s = af->priv; - bool eof; - struct mp_aframe *out = mp_aconverter_read_output(s->converter, &eof); - if (out) - af_add_output_frame(af, mp_audio_from_aframe(out)); - talloc_free(out); - af->delay = mp_aconverter_get_latency(s->converter); - return 0; -} - -static int af_open(struct af_instance *af) -{ - struct af_resample *s = af->priv; - - af->control = control; - af->uninit = uninit; - af->filter_frame = filter; - af->filter_out = filter_out; - - if (s->opts.normalize < 0) - s->opts.normalize = s->global_normalize; - - s->converter = mp_aconverter_create(af->global, af->log, &s->opts); - - if (s->deprecation_warning) { - MP_WARN(af, "This filter is deprecated! Use the --audio-resample- options" - " to customize resampling, or the --af=aresample filter.\n"); - } - - return AF_OK; -} - static void set_defaults(struct mpv_global *global, void *p) { struct af_resample *s = p; @@ -165,7 +52,7 @@ static void set_defaults(struct mpv_global *global, void *p) struct mp_resample_opts *opts = &s->opts; struct mp_resample_opts *src_opts = - mp_get_config_group(s, global, &resample_config); + mp_get_config_group(s, global, &resample_conf); s->global_normalize = src_opts->normalize; @@ -180,28 +67,46 @@ static void set_defaults(struct mpv_global *global, void *p) #define OPT_BASE_STRUCT struct af_resample -const struct af_info af_info_lavrresample = { - .info = "Sample frequency conversion using libavresample", - .name = "lavrresample", - .open = af_open, - .priv_size = sizeof(struct af_resample), - .priv_defaults = &(const struct af_resample) { - .opts = MP_RESAMPLE_OPTS_DEF, - .playback_speed = 1.0, - .allow_detach = 1, - .deprecation_warning = 1, - }, - .options = (const struct m_option[]) { - OPT_INTRANGE("filter-size", opts.filter_size, 0, 0, 32), - OPT_INTRANGE("phase-shift", opts.phase_shift, 0, 0, 30), - OPT_FLAG("linear", opts.linear, 0), - OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1), - OPT_FLAG("detach", allow_detach, 0), - OPT_CHOICE("normalize", opts.normalize, 0, - ({"no", 0}, {"yes", 1}, {"auto", -1})), - OPT_KEYVALUELIST("o", opts.avopts, 0), - OPT_FLAG("deprecation-warning", deprecation_warning, 0), - {0} +static struct mp_filter *af_lavrresample_create(struct mp_filter *parent, + void *options) +{ + struct af_resample *s = options; + + if (s->opts.normalize < 0) + s->opts.normalize = s->global_normalize; + + struct mp_swresample *swr = mp_swresample_create(parent, &s->opts); + if (!swr) + abort(); + + MP_WARN(swr->f, "This filter is deprecated! Use the --audio-resample- options" + " to customize resampling, or the --af=aresample filter.\n"); + + talloc_free(s); + return swr->f; +} + +const struct mp_user_filter_entry af_lavrresample = { + .desc = { + .description = "Sample frequency conversion using libavresample", + .name = "lavrresample", + .priv_size = sizeof(struct af_resample), + .priv_defaults = &(const struct af_resample) { + .opts = MP_RESAMPLE_OPTS_DEF, + .allow_detach = 1, + }, + .options = (const struct m_option[]) { + OPT_INTRANGE("filter-size", opts.filter_size, 0, 0, 32), + OPT_INTRANGE("phase-shift", opts.phase_shift, 0, 0, 30), + OPT_FLAG("linear", opts.linear, 0), + OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1), + OPT_FLAG("detach", allow_detach, 0), // does nothing + OPT_CHOICE("normalize", opts.normalize, 0, + ({"no", 0}, {"yes", 1}, {"auto", -1})), + OPT_KEYVALUELIST("o", opts.avopts, 0), + {0} + }, + .set_defaults = set_defaults, }, - .set_defaults = set_defaults, + .create = af_lavrresample_create, }; diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c index 58cf077d8b..6c8c773e62 100644 --- a/audio/filter/af_rubberband.c +++ b/audio/filter/af_rubberband.c @@ -20,242 +20,348 @@ #include <rubberband/rubberband-c.h> +#include "audio/aframe.h" +#include "audio/format.h" #include "common/common.h" -#include "af.h" +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" +#include "options/m_option.h" + +// command line options +struct f_opts { + int transients, detector, phase, window, + smoothing, formant, pitch, channels; + double scale; +}; struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe_pool *out_pool; + bool sent_final; RubberBandState rubber; double speed; double pitch; - struct mp_audio *pending; - bool needs_reset; + struct mp_aframe *pending; // Estimate how much librubberband has buffered internally. // I could not find a way to do this with the librubberband API. double rubber_delay; - // command line options - int opt_transients, opt_detector, opt_phase, opt_window, - opt_smoothing, opt_formant, opt_pitch, opt_channels; }; -static void update_speed(struct af_instance *af, double new_speed) +static void update_speed(struct priv *p, double new_speed) { - struct priv *p = af->priv; - p->speed = new_speed; - rubberband_set_time_ratio(p->rubber, 1.0 / p->speed); + if (p->rubber) + rubberband_set_time_ratio(p->rubber, 1.0 / p->speed); } -static bool update_pitch(struct af_instance *af, double new_pitch) +static bool update_pitch(struct priv *p, double new_pitch) { if (new_pitch < 0.01 || new_pitch > 100.0) return false; - struct priv *p = af->priv; - p->pitch = new_pitch; - rubberband_set_pitch_scale(p->rubber, p->pitch); + if (p->rubber) + rubberband_set_pitch_scale(p->rubber, p->pitch); return true; } -static int control(struct af_instance *af, int cmd, void *arg) +static bool init_rubberband(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; + assert(!p->rubber); + assert(p->pending); - in->format = AF_FORMAT_FLOATP; - mp_audio_copy_config(out, in); - - if (p->rubber) - rubberband_delete(p->rubber); - - int opts = p->opt_transients | p->opt_detector | p->opt_phase | - p->opt_window | p->opt_smoothing | p->opt_formant | - p->opt_pitch | p-> opt_channels | - RubberBandOptionProcessRealTime; - - p->rubber = rubberband_new(in->rate, in->channels.num, opts, 1.0, 1.0); - if (!p->rubber) { - MP_FATAL(af, "librubberband initialization failed.\n"); - return AF_ERROR; - } + int opts = p->opts->transients | p->opts->detector | p->opts->phase | + p->opts->window | p->opts->smoothing | p->opts->formant | + p->opts->pitch | p-> opts->channels | + RubberBandOptionProcessRealTime; - update_speed(af, p->speed); - update_pitch(af, p->pitch); - control(af, AF_CONTROL_RESET, NULL); + int rate = mp_aframe_get_rate(p->pending); + int channels = mp_aframe_get_channels(p->pending); + if (mp_aframe_get_format(p->pending) != AF_FORMAT_FLOATP) + return false; - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - case AF_CONTROL_SET_PLAYBACK_SPEED: { - update_speed(af, *(double *)arg); - return AF_OK; - } - case AF_CONTROL_RESET: - if (p->rubber) - rubberband_reset(p->rubber); - talloc_free(p->pending); - p->pending = NULL; - p->rubber_delay = 0; - return AF_OK; - case AF_CONTROL_COMMAND: { - char **args = arg; - char *endptr; - double pitch = p->pitch; - if (!strcmp(args[0], "set-pitch")) { - pitch = strtod(args[1], &endptr); - if (*endptr) - return CONTROL_ERROR; - return update_pitch(af, pitch) ? CONTROL_OK : CONTROL_ERROR; - } else if (!strcmp(args[0], "multiply-pitch")) { - double mult = strtod(args[1], &endptr); - if (*endptr || mult <= 0) - return CONTROL_ERROR; - pitch *= mult; - return update_pitch(af, pitch) ? CONTROL_OK : CONTROL_ERROR; - } else { - return CONTROL_ERROR; - } - } + p->rubber = rubberband_new(rate, channels, opts, 1.0, 1.0); + if (!p->rubber) { + MP_FATAL(f, "librubberband initialization failed.\n"); + return false; } - return AF_UNKNOWN; -} -static int filter_frame(struct af_instance *af, struct mp_audio *data) -{ - struct priv *p = af->priv; + mp_aframe_config_copy(p->cur_format, p->pending); - talloc_free(p->pending); - p->pending = data; + update_speed(p, p->speed); + update_pitch(p, p->pitch); - return 0; + return true; } -static int filter_out(struct af_instance *af) +static void process(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; + + if (!mp_pin_in_needs_data(f->ppins[1])) + return; - while (rubberband_available(p->rubber) <= 0) { + while (!p->rubber || !p->pending || rubberband_available(p->rubber) <= 0) { const float *dummy[MP_NUM_CHANNELS] = {0}; const float **in_data = dummy; size_t in_samples = 0; - if (p->pending) { - if (!p->pending->samples) - break; - // recover from previous EOF - if (p->needs_reset) { - rubberband_reset(p->rubber); - p->rubber_delay = 0; + bool eof = false; + if (!p->pending || !mp_aframe_get_size(p->pending)) { + struct mp_frame frame = mp_pin_out_read(p->in_pin); + if (frame.type == MP_FRAME_AUDIO) { + TA_FREEP(&p->pending); + p->pending = frame.data; + } else if (frame.type == MP_FRAME_EOF) { + eof = true; + } else if (frame.type) { + MP_ERR(f, "unexpected frame type\n"); + goto error; + } else { + return; // no new data yet } - p->needs_reset = false; + } + assert(p->pending || eof); + if (!p->rubber) { + if (!p->pending) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + return; + } + if (!init_rubberband(f)) + goto error; + } + + bool format_change = + p->pending && !mp_aframe_config_equals(p->pending, p->cur_format); + + if (p->pending && !format_change) { size_t needs = rubberband_get_samples_required(p->rubber); - in_data = (void *)&p->pending->planes; - in_samples = MPMIN(p->pending->samples, needs); + uint8_t **planes = mp_aframe_get_data_ro(p->pending); + int num_planes = mp_aframe_get_planes(p->pending); + for (int n = 0; n < num_planes; n++) + in_data[n] = (void *)planes[n]; + in_samples = MPMIN(mp_aframe_get_size(p->pending), needs); } - if (p->needs_reset) - break; // previous EOF - p->needs_reset = !p->pending; // EOF + bool final = format_change || eof; + if (!p->sent_final) + rubberband_process(p->rubber, in_data, in_samples, final); + p->sent_final |= final; - rubberband_process(p->rubber, in_data, in_samples, p->needs_reset); p->rubber_delay += in_samples; - if (!p->pending) - break; - mp_audio_skip_samples(p->pending, in_samples); + if (p->pending && !format_change) + mp_aframe_skip_samples(p->pending, in_samples); + + if (rubberband_available(p->rubber) > 0) { + if (eof) + mp_pin_out_repeat_eof(p->in_pin); // drain more next time + } else { + if (eof) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + rubberband_reset(p->rubber); + TA_FREEP(&p->pending); + p->sent_final = false; + return; + } else if (format_change) { + // go on with proper reinit on the next iteration + rubberband_delete(p->rubber); + p->sent_final = false; + p->rubber = NULL; + } + } } + assert(p->pending); + int out_samples = rubberband_available(p->rubber); if (out_samples > 0) { - struct mp_audio *out = - mp_audio_pool_get(af->out_pool, af->data, out_samples); - if (!out) - return -1; - if (p->pending) - mp_audio_copy_config(out, p->pending); - - float **out_data = (void *)&out->planes; - out->samples = rubberband_retrieve(p->rubber, out_data, out->samples); - p->rubber_delay -= out->samples * p->speed; - - af_add_output_frame(af, out); + struct mp_aframe *out = mp_aframe_new_ref(p->cur_format); + if (mp_aframe_pool_allocate(p->out_pool, out, out_samples) < 0) { + talloc_free(out); + goto error; + } + + mp_aframe_copy_attributes(out, p->pending); + + float *out_data[MP_NUM_CHANNELS] = {0}; + uint8_t **planes = mp_aframe_get_data_rw(out); + assert(planes); + int num_planes = mp_aframe_get_planes(out); + for (int n = 0; n < num_planes; n++) + out_data[n] = (void *)planes[n]; + + out_samples = rubberband_retrieve(p->rubber, out_data, out_samples); + + if (!out_samples) { + mp_filter_internal_mark_progress(f); // unexpected, just try again + talloc_free(out); + return; + } + + mp_aframe_set_size(out, out_samples); + + p->rubber_delay -= out_samples * p->speed; + + double pts = mp_aframe_get_pts(p->pending); + if (pts != MP_NOPTS_VALUE) { + // Note: rubberband_get_latency() does not do what you'd expect. + double delay = p->rubber_delay / mp_aframe_get_effective_rate(out); + mp_aframe_set_pts(out, pts - delay); + } + + mp_aframe_mul_speed(out, p->speed); + + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); } - int delay_samples = p->rubber_delay; - if (p->pending) - delay_samples += p->pending->samples; - af->delay = delay_samples / (af->data->rate * p->speed); + return; +error: + mp_filter_internal_mark_failed(f); +} - return 0; +static bool command(struct mp_filter *f, struct mp_filter_command *cmd) +{ + struct priv *p = f->priv; + + switch (cmd->type) { + case MP_FILTER_COMMAND_TEXT: { + char *endptr = NULL; + double pitch = p->pitch; + if (!strcmp(cmd->cmd, "set-pitch")) { + pitch = strtod(cmd->arg, &endptr); + if (*endptr) + return false; + return update_pitch(p, pitch); + } else if (!strcmp(cmd->cmd, "multiply-pitch")) { + double mult = strtod(cmd->arg, &endptr); + if (*endptr || mult <= 0) + return false; + pitch *= mult; + return update_pitch(p, pitch); + } + return false; + } + case MP_FILTER_COMMAND_SET_SPEED: + update_speed(p, cmd->speed); + return true; + } + + return false; } -static void uninit(struct af_instance *af) +static void reset(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; if (p->rubber) - rubberband_delete(p->rubber); - talloc_free(p->pending); + rubberband_reset(p->rubber); + p->sent_final = false; + TA_FREEP(&p->pending); } -static int af_open(struct af_instance *af) +static void destroy(struct mp_filter *f) { - af->control = control; - af->filter_frame = filter_frame; - af->filter_out = filter_out; - af->uninit = uninit; - return AF_OK; + struct priv *p = f->priv; + + if (p->rubber) + rubberband_delete(p->rubber); + talloc_free(p->pending); } -#define OPT_BASE_STRUCT struct priv -const struct af_info af_info_rubberband = { - .info = "Pitch conversion with librubberband", +static const struct mp_filter_info af_rubberband_filter = { .name = "rubberband", - .open = af_open, .priv_size = sizeof(struct priv), - .priv_defaults = &(const struct priv) { - .speed = 1.0, - .pitch = 1.0, - .opt_pitch = RubberBandOptionPitchHighConsistency, - .opt_transients = RubberBandOptionTransientsMixed, - .opt_formant = RubberBandOptionFormantPreserved, - .opt_channels = RubberBandOptionChannelsTogether, - }, - .options = (const struct m_option[]) { - OPT_CHOICE("transients", opt_transients, 0, - ({"crisp", RubberBandOptionTransientsCrisp}, - {"mixed", RubberBandOptionTransientsMixed}, - {"smooth", RubberBandOptionTransientsSmooth})), - OPT_CHOICE("detector", opt_detector, 0, - ({"compound", RubberBandOptionDetectorCompound}, - {"percussive", RubberBandOptionDetectorPercussive}, - {"soft", RubberBandOptionDetectorSoft})), - OPT_CHOICE("phase", opt_phase, 0, - ({"laminar", RubberBandOptionPhaseLaminar}, - {"independent", RubberBandOptionPhaseIndependent})), - OPT_CHOICE("window", opt_window, 0, - ({"standard", RubberBandOptionWindowStandard}, - {"short", RubberBandOptionWindowShort}, - {"long", RubberBandOptionWindowLong})), - OPT_CHOICE("smoothing", opt_smoothing, 0, - ({"off", RubberBandOptionSmoothingOff}, - {"on", RubberBandOptionSmoothingOn})), - OPT_CHOICE("formant", opt_formant, 0, - ({"shifted", RubberBandOptionFormantShifted}, - {"preserved", RubberBandOptionFormantPreserved})), - OPT_CHOICE("pitch", opt_pitch, 0, - ({"quality", RubberBandOptionPitchHighQuality}, - {"speed", RubberBandOptionPitchHighSpeed}, - {"consistency", RubberBandOptionPitchHighConsistency})), - OPT_CHOICE("channels", opt_channels, 0, - ({"apart", RubberBandOptionChannelsApart}, - {"together", RubberBandOptionChannelsTogether})), - OPT_DOUBLE("pitch-scale", pitch, M_OPT_RANGE, .min = 0.01, .max = 100), - {0} + .process = process, + .command = command, + .reset = reset, + .destroy = destroy, +}; + +static struct mp_filter *af_rubberband_create(struct mp_filter *parent, + void *options) +{ + struct mp_filter *f = mp_filter_create(parent, &af_rubberband_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->opts = talloc_steal(p, options); + p->speed = 1.0; + p->pitch = p->opts->scale; + p->cur_format = talloc_steal(p, mp_aframe_create()); + p->out_pool = mp_aframe_pool_create(p); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); + + mp_autoconvert_add_afmt(conv, AF_FORMAT_FLOATP); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + p->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_rubberband = { + .desc = { + .description = "Pitch conversion with librubberband", + .name = "rubberband", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .scale = 1.0, + .pitch = RubberBandOptionPitchHighConsistency, + .transients = RubberBandOptionTransientsMixed, + .formant = RubberBandOptionFormantPreserved, + .channels = RubberBandOptionChannelsTogether, + }, + .options = (const struct m_option[]) { + OPT_CHOICE("transients", transients, 0, + ({"crisp", RubberBandOptionTransientsCrisp}, + {"mixed", RubberBandOptionTransientsMixed}, + {"smooth", RubberBandOptionTransientsSmooth})), + OPT_CHOICE("detector", detector, 0, + ({"compound", RubberBandOptionDetectorCompound}, + {"percussive", RubberBandOptionDetectorPercussive}, + {"soft", RubberBandOptionDetectorSoft})), + OPT_CHOICE("phase", phase, 0, + ({"laminar", RubberBandOptionPhaseLaminar}, + {"independent", RubberBandOptionPhaseIndependent})), + OPT_CHOICE("window", window, 0, + ({"standard", RubberBandOptionWindowStandard}, + {"short", RubberBandOptionWindowShort}, + {"long", RubberBandOptionWindowLong})), + OPT_CHOICE("smoothing", smoothing, 0, + ({"off", RubberBandOptionSmoothingOff}, + {"on", RubberBandOptionSmoothingOn})), + OPT_CHOICE("formant", formant, 0, + ({"shifted", RubberBandOptionFormantShifted}, + {"preserved", RubberBandOptionFormantPreserved})), + OPT_CHOICE("pitch", pitch, 0, + ({"quality", RubberBandOptionPitchHighQuality}, + {"speed", RubberBandOptionPitchHighSpeed}, + {"consistency", RubberBandOptionPitchHighConsistency})), + OPT_CHOICE("channels", channels, 0, + ({"apart", RubberBandOptionChannelsApart}, + {"together", RubberBandOptionChannelsTogether})), + OPT_DOUBLE("pitch-scale", scale, M_OPT_RANGE, .min = 0.01, .max = 100), + {0} + }, }, + .create = af_rubberband_create, }; diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c index 0499631ea9..4e48e6168b 100644 --- a/audio/filter/af_scaletempo.c +++ b/audio/filter/af_scaletempo.c @@ -35,14 +35,32 @@ #include <limits.h> #include <assert.h> +#include "audio/aframe.h" +#include "audio/format.h" #include "common/common.h" - -#include "af.h" +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "options/m_option.h" -// Data for specific instances of this filter -typedef struct af_scaletempo_s -{ +struct f_opts { + float scale_nominal; + float ms_stride; + float ms_search; + float percent_overlap; +#define SCALE_TEMPO 1 +#define SCALE_PITCH 2 + int speed_opt; +}; + +struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe_pool *out_pool; + double current_pts; + // stride float scale; float speed; @@ -62,28 +80,21 @@ typedef struct af_scaletempo_s int bytes_standing; void *buf_overlap; void *table_blend; - void (*output_overlap)(struct af_scaletempo_s *s, void *out_buf, + void (*output_overlap)(struct priv *s, void *out_buf, int bytes_off); // best overlap int frames_search; int num_channels; void *buf_pre_corr; void *table_window; - int (*best_overlap_offset)(struct af_scaletempo_s *s); - // command line - float scale_nominal; - float ms_stride; - float percent_overlap; - float ms_search; -#define SCALE_TEMPO 1 -#define SCALE_PITCH 2 - int speed_opt; -} af_scaletempo_t; + int (*best_overlap_offset)(struct priv *s); +}; -static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset) +static bool reinit(struct mp_filter *f, struct mp_aframe *in); + +static int fill_queue(struct priv *s, struct mp_aframe *in, int offset) { - af_scaletempo_t *s = af->priv; - int bytes_in = (data ? mp_audio_psize(data) : 0) - offset; + int bytes_in = in ? mp_aframe_get_size(in) * s->bytes_per_frame - offset : 0; int offset_unchanged = offset; if (s->bytes_to_slide > 0) { @@ -106,8 +117,8 @@ static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset) if (bytes_in > 0) { int bytes_copy = MPMIN(s->bytes_queue - s->bytes_queued, bytes_in); assert(bytes_copy >= 0); - memcpy(s->buf_queue + s->bytes_queued, - (int8_t *)data->planes[0] + offset, bytes_copy); + uint8_t **planes = mp_aframe_get_data_ro(in); + memcpy(s->buf_queue + s->bytes_queued, planes[0] + offset, bytes_copy); s->bytes_queued += bytes_copy; offset += bytes_copy; } @@ -117,7 +128,7 @@ static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset) #define UNROLL_PADDING (4 * 4) -static int best_overlap_offset_float(af_scaletempo_t *s) +static int best_overlap_offset_float(struct priv *s) { float best_corr = INT_MIN; int best_off = 0; @@ -146,7 +157,7 @@ static int best_overlap_offset_float(af_scaletempo_t *s) return best_off * 4 * s->num_channels; } -static int best_overlap_offset_s16(af_scaletempo_t *s) +static int best_overlap_offset_s16(struct priv *s) { int64_t best_corr = INT64_MIN; int best_off = 0; @@ -183,7 +194,7 @@ static int best_overlap_offset_s16(af_scaletempo_t *s) return best_off * 2 * s->num_channels; } -static void output_overlap_float(af_scaletempo_t *s, void *buf_out, +static void output_overlap_float(struct priv *s, void *buf_out, int bytes_off) { float *pout = buf_out; @@ -196,7 +207,7 @@ static void output_overlap_float(af_scaletempo_t *s, void *buf_out, } } -static void output_overlap_s16(af_scaletempo_t *s, void *buf_out, +static void output_overlap_s16(struct priv *s, void *buf_out, int bytes_off) { int16_t *pout = buf_out; @@ -209,28 +220,65 @@ static void output_overlap_s16(af_scaletempo_t *s, void *buf_out, } } -static int filter(struct af_instance *af, struct mp_audio *data) +static void process(struct mp_filter *f) { - af_scaletempo_t *s = af->priv; + struct priv *s = f->priv; + + if (!mp_pin_can_transfer_data(f->ppins[1], s->in_pin)) + return; - if (s->scale == 1.0) { - af->delay = 0; - af_add_output_frame(af, data); - return 0; + struct mp_aframe *in = NULL, *out = NULL; + + struct mp_frame frame = mp_pin_out_read(s->in_pin); + if (frame.type != MP_FRAME_AUDIO && frame.type != MP_FRAME_EOF) { + MP_ERR(f, "unexpected frame type\n"); + goto error; + } + + in = frame.type == MP_FRAME_AUDIO ? frame.data : NULL; + bool is_eof = !in; + + // EOF before it was even initialized once. + if (is_eof && !mp_aframe_config_is_valid(s->cur_format)) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + return; + } + + if (in && !mp_aframe_config_equals(in, s->cur_format)) { + if (s->bytes_queued) { + // Drain remaining data before executing the format change. + MP_VERBOSE(f, "draining\n"); + mp_pin_out_unread(s->in_pin, frame); + in = NULL; + } else { + if (!reinit(f, in)) { + MP_ERR(f, "initialization failed\n"); + goto error; + } + } } - int in_samples = data ? data->samples : 0; - struct mp_audio *out = mp_audio_pool_get(af->out_pool, af->data, - ((int)(in_samples / s->frames_stride_scaled) + 1) * s->frames_stride); - if (!out) { - talloc_free(data); - return -1; + int in_samples = in ? mp_aframe_get_size(in) : 0; + + int max_out_samples = + ((int)(in_samples / s->frames_stride_scaled) + 1) * s->frames_stride; + if (!in) + max_out_samples += s->bytes_queued; + out = mp_aframe_new_ref(s->cur_format); + if (mp_aframe_pool_allocate(s->out_pool, out, max_out_samples) < 0) + goto error; + + if (in) { + mp_aframe_copy_attributes(out, in); + s->current_pts = mp_aframe_end_pts(in); } - if (data) - mp_audio_copy_attributes(out, data); - int offset_in = fill_queue(af, data, 0); - int8_t *pout = out->planes[0]; + int offset_in = fill_queue(s, in, 0); + uint8_t **out_planes = mp_aframe_get_data_rw(out); + if (!out_planes) + goto error; + int8_t *pout = out_planes[0]; + int out_offset = 0; while (s->bytes_queued >= s->bytes_queue) { int ti; float tf; @@ -240,12 +288,12 @@ static int filter(struct af_instance *af, struct mp_audio *data) if (s->output_overlap) { if (s->best_overlap_offset) bytes_off = s->best_overlap_offset(s); - s->output_overlap(s, pout, bytes_off); + s->output_overlap(s, pout + out_offset, bytes_off); } - memcpy(pout + s->bytes_overlap, + memcpy(pout + out_offset + s->bytes_overlap, s->buf_queue + bytes_off + s->bytes_overlap, s->bytes_standing); - pout += s->bytes_stride; + out_offset += s->bytes_stride; // input stride memcpy(s->buf_overlap, @@ -256,239 +304,302 @@ static int filter(struct af_instance *af, struct mp_audio *data) s->frames_stride_error = tf - ti; s->bytes_to_slide = ti * s->bytes_per_frame; - offset_in += fill_queue(af, data, offset_in); + offset_in += fill_queue(s, in, offset_in); } + // Drain remaining buffered data. + if (!in && s->bytes_queued) { + memcpy(pout + out_offset, s->buf_queue, s->bytes_queued); + out_offset += s->bytes_queued; + s->bytes_queued = 0; + } + mp_aframe_set_size(out, out_offset / s->bytes_per_frame); // This filter can have a negative delay when scale > 1: // output corresponding to some length of input can be decided and written // after receiving only a part of that input. - af->delay = (s->bytes_queued - s->bytes_to_slide) / s->scale - / out->sstride / out->rate; + double delay = (out_offset * s->speed + s->bytes_queued - s->bytes_to_slide) / + s->bytes_per_frame / mp_aframe_get_effective_rate(out); - out->samples = (pout - (int8_t *)out->planes[0]) / out->sstride; - talloc_free(data); - if (out->samples) { - af_add_output_frame(af, out); - } else { - talloc_free(out); + if (s->current_pts != MP_NOPTS_VALUE) + mp_aframe_set_pts(out, s->current_pts - delay); + + mp_aframe_mul_speed(out, s->speed); + + if (!mp_aframe_get_size(out)) + TA_FREEP(&out); + + if (is_eof && out) { + mp_pin_out_repeat_eof(s->in_pin); + } else if (is_eof && !out) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + } else if (!is_eof && !out) { + mp_pin_out_request_data(s->in_pin); } - return 0; + + if (out) + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); + + talloc_free(in); + return; + +error: + talloc_free(in); + talloc_free(out); + mp_filter_internal_mark_failed(f); } -static void update_speed(struct af_instance *af, float speed) +static void update_speed(struct priv *s, float speed) { - af_scaletempo_t *s = af->priv; - s->speed = speed; - double factor = (s->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed; - s->scale = factor * s->scale_nominal; + double factor = (s->opts->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed; + s->scale = factor * s->opts->scale_nominal; s->frames_stride_scaled = s->scale * s->frames_stride; s->frames_stride_error = MPMIN(s->frames_stride_error, s->frames_stride_scaled); } -// Initialization and runtime control -static int control(struct af_instance *af, int cmd, void *arg) +static bool reinit(struct mp_filter *f, struct mp_aframe *in) { - af_scaletempo_t *s = af->priv; - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *data = (struct mp_audio *)arg; - float srate = data->rate / 1000.0; - int nch = data->nch; - int use_int = 0; - - mp_audio_force_interleaved_format(data); - mp_audio_copy_config(af->data, data); - - if (data->format == AF_FORMAT_S16) { - use_int = 1; - } else { - mp_audio_set_format(af->data, AF_FORMAT_FLOAT); - } - int bps = af->data->bps; + struct priv *s = f->priv; - s->frames_stride = srate * s->ms_stride; - s->bytes_stride = s->frames_stride * bps * nch; - af->delay = 0; + mp_aframe_reset(s->cur_format); - update_speed(af, s->speed); + float srate = mp_aframe_get_rate(in) / 1000.0; + int nch = mp_aframe_get_channels(in); + int format = mp_aframe_get_format(in); - int frames_overlap = s->frames_stride * s->percent_overlap; - if (frames_overlap <= 0) { - s->bytes_standing = s->bytes_stride; - s->samples_standing = s->bytes_standing / bps; - s->output_overlap = NULL; - s->bytes_overlap = 0; - } else { - s->samples_overlap = frames_overlap * nch; - s->bytes_overlap = frames_overlap * nch * bps; - s->bytes_standing = s->bytes_stride - s->bytes_overlap; - s->samples_standing = s->bytes_standing / bps; - s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap); - s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4); - if (!s->buf_overlap || !s->table_blend) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; + int use_int = 0; + if (format == AF_FORMAT_S16) { + use_int = 1; + } else if (format != AF_FORMAT_FLOAT) { + return false; + } + int bps = use_int ? 2 : 4; + + s->frames_stride = srate * s->opts->ms_stride; + s->bytes_stride = s->frames_stride * bps * nch; + + update_speed(s, s->speed); + + int frames_overlap = s->frames_stride * s->opts->percent_overlap; + if (frames_overlap <= 0) { + s->bytes_standing = s->bytes_stride; + s->samples_standing = s->bytes_standing / bps; + s->output_overlap = NULL; + s->bytes_overlap = 0; + } else { + s->samples_overlap = frames_overlap * nch; + s->bytes_overlap = frames_overlap * nch * bps; + s->bytes_standing = s->bytes_stride - s->bytes_overlap; + s->samples_standing = s->bytes_standing / bps; + s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap); + s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4); + if (!s->buf_overlap || !s->table_blend) { + MP_FATAL(f, "Out of memory\n"); + return false; + } + memset(s->buf_overlap, 0, s->bytes_overlap); + if (use_int) { + int32_t *pb = s->table_blend; + int64_t blend = 0; + for (int i = 0; i < frames_overlap; i++) { + int32_t v = blend / frames_overlap; + for (int j = 0; j < nch; j++) + *pb++ = v; + blend += 65536; // 2^16 } - memset(s->buf_overlap, 0, s->bytes_overlap); - if (use_int) { - int32_t *pb = s->table_blend; - int64_t blend = 0; - for (int i = 0; i < frames_overlap; i++) { - int32_t v = blend / frames_overlap; - for (int j = 0; j < nch; j++) - *pb++ = v; - blend += 65536; // 2^16 - } - s->output_overlap = output_overlap_s16; - } else { - float *pb = s->table_blend; - for (int i = 0; i < frames_overlap; i++) { - float v = i / (float)frames_overlap; - for (int j = 0; j < nch; j++) - *pb++ = v; - } - s->output_overlap = output_overlap_float; + s->output_overlap = output_overlap_s16; + } else { + float *pb = s->table_blend; + for (int i = 0; i < frames_overlap; i++) { + float v = i / (float)frames_overlap; + for (int j = 0; j < nch; j++) + *pb++ = v; } + s->output_overlap = output_overlap_float; } + } - s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0; - if (s->frames_search <= 0) - s->best_overlap_offset = NULL; - else { - if (use_int) { - int64_t t = frames_overlap; - int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2 - s->buf_pre_corr = realloc(s->buf_pre_corr, - s->bytes_overlap * 2 + UNROLL_PADDING); - s->table_window = realloc(s->table_window, - s->bytes_overlap * 2 - nch * bps * 2); - if (!s->buf_pre_corr || !s->table_window) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; - } - memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, - UNROLL_PADDING); - int32_t *pw = s->table_window; - for (int i = 1; i < frames_overlap; i++) { - int32_t v = (i * (t - i) * n) >> 15; - for (int j = 0; j < nch; j++) - *pw++ = v; - } - s->best_overlap_offset = best_overlap_offset_s16; - } else { - s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap); - s->table_window = realloc(s->table_window, - s->bytes_overlap - nch * bps); - if (!s->buf_pre_corr || !s->table_window) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; - } - float *pw = s->table_window; - for (int i = 1; i < frames_overlap; i++) { - float v = i * (frames_overlap - i); - for (int j = 0; j < nch; j++) - *pw++ = v; - } - s->best_overlap_offset = best_overlap_offset_float; + s->frames_search = (frames_overlap > 1) ? srate * s->opts->ms_search : 0; + if (s->frames_search <= 0) + s->best_overlap_offset = NULL; + else { + if (use_int) { + int64_t t = frames_overlap; + int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2 + s->buf_pre_corr = realloc(s->buf_pre_corr, + s->bytes_overlap * 2 + UNROLL_PADDING); + s->table_window = realloc(s->table_window, + s->bytes_overlap * 2 - nch * bps * 2); + if (!s->buf_pre_corr || !s->table_window) { + MP_FATAL(f, "Out of memory\n"); + return false; + } + memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, + UNROLL_PADDING); + int32_t *pw = s->table_window; + for (int i = 1; i < frames_overlap; i++) { + int32_t v = (i * (t - i) * n) >> 15; + for (int j = 0; j < nch; j++) + *pw++ = v; } + s->best_overlap_offset = best_overlap_offset_s16; + } else { + s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap); + s->table_window = realloc(s->table_window, + s->bytes_overlap - nch * bps); + if (!s->buf_pre_corr || !s->table_window) { + MP_FATAL(f, "Out of memory\n"); + return false; + } + float *pw = s->table_window; + for (int i = 1; i < frames_overlap; i++) { + float v = i * (frames_overlap - i); + for (int j = 0; j < nch; j++) + *pw++ = v; + } + s->best_overlap_offset = best_overlap_offset_float; } + } - s->bytes_per_frame = bps * nch; - s->num_channels = nch; - - s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap) - * bps * nch; - s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING); - if (!s->buf_queue) { - MP_FATAL(af, "Out of memory\n"); - return AF_ERROR; - } + s->bytes_per_frame = bps * nch; + s->num_channels = nch; - s->bytes_queued = 0; - s->bytes_to_slide = 0; - - MP_DBG(af, "" - "%.2f stride_in, %i stride_out, %i standing, " - "%i overlap, %i search, %i queue, %s mode\n", - s->frames_stride_scaled, - (int)(s->bytes_stride / nch / bps), - (int)(s->bytes_standing / nch / bps), - (int)(s->bytes_overlap / nch / bps), - s->frames_search, - (int)(s->bytes_queue / nch / bps), - (use_int ? "s16" : "float")); - - return af_test_output(af, (struct mp_audio *)arg); + s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap) + * bps * nch; + s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING); + if (!s->buf_queue) { + MP_FATAL(f, "Out of memory\n"); + return false; } - case AF_CONTROL_SET_PLAYBACK_SPEED: { - double speed = *(double *)arg; - if (s->speed_opt & SCALE_TEMPO) { - if (s->speed_opt & SCALE_PITCH) - break; - update_speed(af, speed); - } else if (s->speed_opt & SCALE_PITCH) { - update_speed(af, speed); - break; // do not signal OK + + s->bytes_queued = 0; + s->bytes_to_slide = 0; + + MP_DBG(f, "" + "%.2f stride_in, %i stride_out, %i standing, " + "%i overlap, %i search, %i queue, %s mode\n", + s->frames_stride_scaled, + (int)(s->bytes_stride / nch / bps), + (int)(s->bytes_standing / nch / bps), + (int)(s->bytes_overlap / nch / bps), + s->frames_search, + (int)(s->bytes_queue / nch / bps), + (use_int ? "s16" : "float")); + + mp_aframe_config_copy(s->cur_format, in); + + return true; +} + +static bool command(struct mp_filter *f, struct mp_filter_command *cmd) +{ + struct priv *s = f->priv; + + if (cmd->type == MP_FILTER_COMMAND_SET_SPEED) { + if (s->opts->speed_opt & SCALE_TEMPO) { + if (s->opts->speed_opt & SCALE_PITCH) + return false; + update_speed(s, cmd->speed); + return true; + } else if (s->opts->speed_opt & SCALE_PITCH) { + update_speed(s, cmd->speed); + return false; // do not signal OK } - return AF_OK; - } - case AF_CONTROL_RESET: - s->bytes_queued = 0; - s->bytes_to_slide = 0; - s->frames_stride_error = 0; - memset(s->buf_overlap, 0, s->bytes_overlap); } - return AF_UNKNOWN; + + return false; } -// Deallocate memory -static void uninit(struct af_instance *af) +static void reset(struct mp_filter *f) { - af_scaletempo_t *s = af->priv; + struct priv *s = f->priv; + + s->current_pts = MP_NOPTS_VALUE; + s->bytes_queued = 0; + s->bytes_to_slide = 0; + s->frames_stride_error = 0; + memset(s->buf_overlap, 0, s->bytes_overlap); +} + +static void destroy(struct mp_filter *f) +{ + struct priv *s = f->priv; free(s->buf_queue); free(s->buf_overlap); free(s->buf_pre_corr); free(s->table_blend); free(s->table_window); + mp_filter_free_children(f); } -// Allocate memory and set function pointers -static int af_open(struct af_instance *af) +static const struct mp_filter_info af_scaletempo_filter = { + .name = "scaletempo", + .priv_size = sizeof(struct priv), + .process = process, + .command = command, + .reset = reset, + .destroy = destroy, +}; + +static struct mp_filter *af_scaletempo_create(struct mp_filter *parent, + void *options) { - af->control = control; - af->uninit = uninit; - af->filter_frame = filter; - return AF_OK; -} + struct mp_filter *f = mp_filter_create(parent, &af_scaletempo_filter); + if (!f) { + talloc_free(options); + return NULL; + } -#define OPT_BASE_STRUCT af_scaletempo_t + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); -const struct af_info af_info_scaletempo = { - .info = "Scale audio tempo while maintaining pitch", - .name = "scaletempo", - .open = af_open, - .priv_size = sizeof(af_scaletempo_t), - .priv_defaults = &(const af_scaletempo_t) { - .ms_stride = 60, - .percent_overlap = .20, - .ms_search = 14, - .speed_opt = SCALE_TEMPO, - .speed = 1.0, - .scale_nominal = 1.0, - }, - .options = (const struct m_option[]) { - OPT_FLOAT("scale", scale_nominal, M_OPT_MIN, .min = 0.01), - OPT_FLOAT("stride", ms_stride, M_OPT_MIN, .min = 0.01), - OPT_FLOAT("overlap", percent_overlap, M_OPT_RANGE, .min = 0, .max = 1), - OPT_FLOAT("search", ms_search, M_OPT_MIN, .min = 0), - OPT_CHOICE("speed", speed_opt, 0, - ({"pitch", SCALE_PITCH}, - {"tempo", SCALE_TEMPO}, - {"none", 0}, - {"both", SCALE_TEMPO | SCALE_PITCH})), - {0} + struct priv *s = f->priv; + s->opts = talloc_steal(s, options); + s->speed = 1.0; + s->cur_format = talloc_steal(s, mp_aframe_create()); + s->out_pool = mp_aframe_pool_create(s); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); + + mp_autoconvert_add_afmt(conv, AF_FORMAT_S16); + mp_autoconvert_add_afmt(conv, AF_FORMAT_FLOAT); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + s->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_scaletempo = { + .desc = { + .description = "Scale audio tempo while maintaining pitch", + .name = "scaletempo", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .ms_stride = 60, + .percent_overlap = .20, + .ms_search = 14, + .speed_opt = SCALE_TEMPO, + .scale_nominal = 1.0, + }, + .options = (const struct m_option[]) { + OPT_FLOAT("scale", scale_nominal, M_OPT_MIN, .min = 0.01), + OPT_FLOAT("stride", ms_stride, M_OPT_MIN, .min = 0.01), + OPT_FLOAT("overlap", percent_overlap, M_OPT_RANGE, .min = 0, .max = 1), + OPT_FLOAT("search", ms_search, M_OPT_MIN, .min = 0), + OPT_CHOICE("speed", speed_opt, 0, + ({"pitch", SCALE_PITCH}, + {"tempo", SCALE_TEMPO}, + {"none", 0}, + {"both", SCALE_TEMPO | SCALE_PITCH})), + {0} + }, }, + .create = af_scaletempo_create, }; diff --git a/audio/filter/tools.c b/audio/filter/tools.c deleted file mode 100644 index 4ebea64d4a..0000000000 --- a/audio/filter/tools.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <math.h> -#include <string.h> - -#include "common/common.h" -#include "af.h" - -/* Convert from ms to sample time */ -int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma) -{ - int i = 0; - // Sanity check - if(!in || !out) - return AF_ERROR; - - for(i=0;i<n;i++) - out[i]=(int)((float)rate * MPCLAMP(in[i],mi,ma)/1000.0); - - return AF_OK; -} - -/* - * test if output format matches - * af: audio filter - * out: needed format, will be overwritten by available - * format if they do not match - * returns: AF_FALSE if formats do not match, AF_OK if they match - * - * compares the format, rate and nch values of af->data with out - * Note: logically, *out=*af->data always happens, because out contains the - * format only, no actual audio data or memory allocations. *out always - * contains the parameters from af->data after the function returns. - */ -int af_test_output(struct af_instance* af, struct mp_audio* out) -{ - if((af->data->format != out->format) || - (af->data->bps != out->bps) || - (af->data->rate != out->rate) || - !mp_chmap_equals(&af->data->channels, &out->channels)){ - *out = *af->data; - return AF_FALSE; - } - return AF_OK; -} - -/* Soft clipping, the sound of a dream, thanks to Jon Wattes - post to Musicdsp.org */ -float af_softclip(float a) -{ - if (a >= M_PI/2) - return 1.0; - else if (a <= -M_PI/2) - return -1.0; - else - return sin(a); -} diff --git a/audio/format.c b/audio/format.c index b6d6761b65..8a13698ff7 100644 --- a/audio/format.c +++ b/audio/format.c @@ -154,7 +154,7 @@ void af_fill_silence(void *dst, size_t bytes, int format) // If the formats are equal, 1024 is returned. If they are gravely incompatible // (like s16<->ac3), INT_MIN is returned. If there is implied loss of precision // (like s16->s8), a value <0 is returned. -static int af_format_conversion_score(int dst_format, int src_format) +int af_format_conversion_score(int dst_format, int src_format) { if (dst_format == AF_FORMAT_UNKNOWN || src_format == AF_FORMAT_UNKNOWN) return INT_MIN; diff --git a/audio/format.h b/audio/format.h index 8c620226df..0afc6567c9 100644 --- a/audio/format.h +++ b/audio/format.h @@ -70,6 +70,7 @@ int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int sampler void af_fill_silence(void *dst, size_t bytes, int format); void af_get_best_sample_formats(int src_format, int *out_formats); +int af_format_conversion_score(int dst_format, int src_format); int af_select_best_samplerate(int src_sampelrate, const int *available); int af_format_sample_alignment(int format); |