On Mon, 5 May 2014 16:11:33 +0200
Luca Barbato <[email protected]> wrote:
> ---
> libavresample/avresample.h | 72 +++++++++++++++++++++++++
> libavresample/utils.c | 128
> +++++++++++++++++++++++++++++++++++++++++++++
> libavutil/error.h | 2 +
> 3 files changed, 202 insertions(+)
>
> diff --git a/libavresample/avresample.h b/libavresample/avresample.h
> index 0d42e88..37b92d3 100644
> --- a/libavresample/avresample.h
> +++ b/libavresample/avresample.h
> @@ -95,6 +95,7 @@
> #include "libavutil/avutil.h"
> #include "libavutil/channel_layout.h"
> #include "libavutil/dict.h"
> +#include "libavutil/frame.h"
> #include "libavutil/log.h"
> #include "libavutil/mathematics.h"
>
> @@ -165,6 +166,10 @@ AVAudioResampleContext *avresample_alloc_context(void);
>
> /**
> * Initialize AVAudioResampleContext.
> + * @note The context must be configured using the AVOption api.
> + *
> + * @see av_opt_set_int()
> + * @see av_opt_set_dict()
> *
> * @param avr audio resample context
> * @return 0 on success, negative AVERROR code on failure
> @@ -424,6 +429,73 @@ int avresample_available(AVAudioResampleContext *avr);
> */
> int avresample_read(AVAudioResampleContext *avr, uint8_t **output, int
> nb_samples);
>
> +
> +
> +/**
> + * Convert the samples in the input AVFrame and write them to the output
> AVFrame.
> + *
> + * Input and output AVFrames must have channel_layout, sample_rate and
> format set.
What about other things. Will side-data be used? Both
AV_FRAME_DATA_MATRIXENCODING and AV_FRAME_DATA_DOWNMIX_INFO
seem relevant.
(I know the patch currently doesn't implement them. But should they in
the future? If so, should that be reflected in the documentation? There
needs to be forward-compatibility too.)
> + *
> + * The upper bound on the number of output samples is given by
> + * avresample_available() + (avresample_get_delay() + number of input
> samples) *
> + * output sample rate / input sample rate.
> + *
You should also mention avresample_max_output_samples().
> + * If the output AVFrame does not have the data pointers allocated a the
> nb_samples
> + * will be set as described above and av_frame_get_buffer() will be called.
> + *
Annoying corner cases: what if the format is planar and only some
pointers are allocated? I guess this should be considered invalid.
> + * The output AVFrame can be NULL or have fewer allocated samples than
> required.
> + *
> + * In this case, any remaining samples not written to the output will be
> added
> + * to an internal FIFO buffer, to be returned at the next call to this
> function
> + * or to avresample_convert() or to avresample_read().
> + *
> + * If converting sample rate, there may be data remaining in the internal
> + * resampling delay buffer. avresample_get_delay() tells the number of
> + * remaining samples. To get this data as output, call this function or
> + * avresample_convert() with NULL input.
> + *
> + * At the end of the conversion process, there may be data remaining in the
> + * internal FIFO buffer. avresample_available() tells the number of remaining
> + * samples. To get this data as output, either call this function or
> + * avresample_convert() with NULL input or call avresample_read().
> + *
> + * If the AVAudioResampleContext configuration does not match the output and
> + * input AVFrame settings the conversion does not take place and depending on
> + * which AVFrame is not matching AVERROR_OUTPUT_CHANGED,
> AVERROR_INPUT_CHANGED
> + * or AVERROR_OUTPUT_CHANGED|AVERROR_INPUT_CHANGED is returned.
> + *
> + * @see avresample_available()
> + * @see avresample_convert()
> + * @see avresample_read()
> + * @see avresample_get_delay()
> + *
IMO this absolutely should provide a example how to convert a stream of
input AVFrame (in any format). The code fragment should fully handle
format changes. It's best to provide this right in the docs, instead of
requiring every API user to reinvent it badly.
> + * @param avr audio resample context
> + * @param output output AVFrame
> + * @param input input AVFrame
> + * @return 0 on success, AVERROR on failure or nonmatching
> + * configuration.
> + */
> +int avresample_convert_frame(AVAudioResampleContext *avr,
> + AVFrame *output, AVFrame *input);
> +
> +/**
> + * Configure or reconfigure the AVAudioResampleContext using the information
> + * provided by the AVFrames and an optional AVDictionary containing
> additional
> + * resampler options.
> + *
> + * The original resampling context is reset even on failure.
> + * The function calls internally avresample_open().
> + *
> + * @see avresample_open();
> + *
> + * @param avr audio resample context
> + * @param output output AVFrame
> + * @param input input AVFrame
Forgets opts.
Should opts be allowed to be NULL?
> + * @return 0 on success, AVERROR on failure.
> + */
> +int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame *in,
> + AVDictionary **opts);
> +
> /**
> * @}
> */
> diff --git a/libavresample/utils.c b/libavresample/utils.c
> index 04c4f46..40a3760 100644
> --- a/libavresample/utils.c
> +++ b/libavresample/utils.c
> @@ -21,6 +21,7 @@
> #include "libavutil/common.h"
> #include "libavutil/dict.h"
> #include "libavutil/error.h"
> +#include "libavutil/frame.h"
> #include "libavutil/log.h"
> #include "libavutil/mem.h"
> #include "libavutil/opt.h"
> @@ -506,6 +507,133 @@ int attribute_align_arg
> avresample_convert(AVAudioResampleContext *avr,
> current_buffer);
> }
>
> +
> +int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame *in,
> + AVDictionary **opts)
> +{
> + int ret;
> +
> + if (avresample_is_open(avr)) {
> + avresample_close(avr);
> + }
> +
> + if (in) {
> + avr->in_channel_layout = in->channel_layout;
> + avr->in_sample_rate = in->sample_rate;
> + avr->in_sample_fmt = in->format;
> + }
> +
> + if (out) {
> + avr->out_channel_layout = out->channel_layout;
> + avr->out_sample_rate = out->sample_rate;
> + avr->out_sample_fmt = out->format;
> + }
> +
> + if ((ret = av_opt_set_dict(avr, opts)) < 0)
> + return ret;
> +
> + return avresample_open(avr);
> +}
> +
> +static int config_changed(AVAudioResampleContext *avr,
> + AVFrame *out, AVFrame *in)
> +{
> + int ret = 0;
> +
> + if (in) {
> + if (avr->in_channel_layout != in->channel_layout ||
> + avr->in_sample_rate != in->sample_rate ||
> + avr->in_sample_fmt != in->format) {
> + ret |= AVERROR_INPUT_CHANGED;
> + }
> + }
> +
> + if (out) {
> + if (avr->out_channel_layout != out->channel_layout ||
> + avr->out_sample_rate != out->sample_rate ||
> + avr->out_sample_fmt != out->format) {
> + ret |= AVERROR_OUTPUT_CHANGED;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static inline int convert_frame(AVAudioResampleContext *avr,
> + AVFrame *out, AVFrame *in)
> +{
> + int ret;
> + uint8_t **out_data = NULL, **in_data = NULL;
> + int out_linesize = 0, in_linesize = 0;
> + int out_nb_samples = 0, in_nb_samples = 0;
> +
> + if (out) {
> + out_data = out->extended_data;
> + out_linesize = out->linesize[0];
> + out_nb_samples = out->nb_samples;
> + }
> +
> + if (in) {
> + in_data = in->extended_data;
> + in_linesize = in->linesize[0];
> + in_nb_samples = in->nb_samples;
> + }
> +
> + ret = avresample_convert(avr, out_data, out_linesize,
> + out_nb_samples,
> + in_data, in_linesize,
> + in_nb_samples);
> +
> + if (ret > 0) {
> + out->nb_samples = ret;
> + return 0;
> + }
> +
> + return ret;
> +}
> +
> +static inline int available_samples(AVFrame *out)
> +{
> + int bytes_per_sample = av_get_bytes_per_sample(out->format);
> + int samples = out->linesize[0] / bytes_per_sample;
> +
> + if (av_sample_fmt_is_planar(out->format)) {
> + return samples;
> + } else {
> + int channels =
> av_get_channel_layout_nb_channels(out->channel_layout);
> + return samples / channels;
> + }
> +}
> +
> +int avresample_convert_frame(AVAudioResampleContext *avr,
> + AVFrame *out, AVFrame *in)
> +{
> + int ret, setup = 0;
> +
> + if (!avresample_is_open(avr)) {
> + if ((ret = avresample_config(avr, out, in, NULL)) < 0)
> + return ret;
> + setup = 1;
> + } else {
> + // return as is or reconfigure for input changes?
> + if ((ret = config_changed(avr, out, in)))
> + return ret;
> + }
> +
> + if (out && !out->linesize[0]) {
> + out->nb_samples = avresample_max_output_samples(avr, in->nb_samples);
> + if ((ret = av_frame_get_buffer(out, 0)) < 0) {
> + if (setup)
> + avresample_close(avr);
> + return ret;
> + }
> + } else {
> + out->nb_samples = available_samples(out);
> + }
> +
> + return convert_frame(avr, out, in);
> +}
> +
> int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix,
> int stride)
> {
> diff --git a/libavutil/error.h b/libavutil/error.h
> index 268a032..8be87cb 100644
> --- a/libavutil/error.h
> +++ b/libavutil/error.h
> @@ -60,6 +60,8 @@
> #define AVERROR_BUG (-0x5fb8aabe) ///< Bug detected, please
> report the issue
> #define AVERROR_UNKNOWN (-0x31b4b1ab) ///< Unknown error,
> typically from an external library
> #define AVERROR_EXPERIMENTAL (-0x2bb2afa8) ///< Requested feature is
> flagged experimental. Set strict_std_compliance if you really want to use it.
> +#define AVERROR_INPUT_CHANGED (-0x636e6701) ///< Input changed between
> calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED)
> +#define AVERROR_OUTPUT_CHANGED (-0x636e6702) ///< Output changed between
> calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED)
>
> /**
> * Put a description of the AVERROR code errnum in errbuf.
IMO there absolutely should be a public function that should check
whether two AVFrames have the same configuration, and/or a function
which checks whether an AVFrame is the same as the current configured
in/out format of the context (like the private config_changed).
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel