On 16/04/14 15:47, wm4 wrote:
> On Wed, 16 Apr 2014 09:00:15 +0200
> Luca Barbato <[email protected]> wrote:
> 
>> ---
>>  libavresample/avresample.h |  54 +++++++++++++++++++++
>>  libavresample/utils.c      | 118 
>> +++++++++++++++++++++++++++++++++++++++++++++
>>  libavutil/error.h          |   2 +
>>  3 files changed, 174 insertions(+)
>>
>> diff --git a/libavresample/avresample.h b/libavresample/avresample.h
>> index 3358628..702a767 100644
>> --- a/libavresample/avresample.h
>> +++ b/libavresample/avresample.h
>> @@ -96,6 +96,7 @@
>>  #include "libavutil/avutil.h"
>>  #include "libavutil/channel_layout.h"
>>  #include "libavutil/dict.h"
>> +#include "libavutil/frame.h"
>>  #include "libavutil/log.h"
>>  
>>  #include "libavresample/version.h"
>> @@ -410,6 +411,59 @@ 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.
>> + *
>> + * 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.
> 
> This isn't an exact count and should specify rounding. If the result of
> the division is not an integer, should it be rounded up or not? (If you
> do defensive programming, you'd round it up always, but maybe it's
> better not to rely on the user getting these things right.)
> 
>> + * If the output AVFrame does not have the data pointers allocated a the 
>> nb_samples
> 
> "a the" -> typo?
> 
>> + * will be set as described above and av_frame_get_buffer() will be called.
> 
> You should specify whether a memory pool is used or not - I imagine it
> might matter for performance.
> 
>> + * 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.
> 
> You should also mention that the context needs to be initialized, and
> _how_ (maybe just mentioning avresample_config() is enough).

Actually if the context is not it would.

> IMO there should be a way to test whether the AVFrame configuration
> changes, so that the user can do its own flushing.

The function used to check can be made public, it is another point I'd
like to have feedback to.

> As far as I understand, avresample_config() actually flushes
> everything, so it's not enough. So maybe something like
> int avresample_needs_config(AVFrame *in, AVFrame *out) should be added.

avresample_config() resets the internal state and can be omitted since
the convert_frame would call it for you.

> The user should be encouraged to actually do the checking, maybe even
> provide example code for a proper conversion loop. Almost every API
> user will need such a conversion loop, but it's probably better than
> making the API trickier.

Yes, I will =)

> (Here's an alternative suggestion though: allow avresample_convert_frame
> to accept partial input... then it could handle flushing itself easily,
> though the user still needs to check whether the input frame was
> consumed. It also would change the AVFrame data pointers to advance the
> sample positions... well, maybe this suggestion is a bad idea.)

avresample_conver_frame(avr, out, NULL) should work for the purpose.

> As far as I can tell, it actually returns 0 on success, and sets
> nb_samples on the output frame instead.

It is one of the open questions, which do you like better?

>> + */
>> +int avresample_convert_frame(AVAudioResampleContext *avr,
>> +                             AVFrame *output, AVFrame *input);
>> +
>> +int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame 
>> *in,
>> +                      AVDictionary **opts);
> 
> Obviously missing doxygen. But it seems straightforward enough. I find
> the opts parameter a bit strange. You could just set such options on
> the avr context itself. I guess the question is, how do we handle if
> certain parameters get moved to AVFrame: is the AVFrame value
> preferred? Or the context options? Do the context options even indicate
> always whether they were set by the user, or is it not possible to
> distinguish set options from options that are just initialized to their
> default value?

This function would match

avresample_open2(avr, opts)

That will be introduced later (see the blueprint on the wiki).

> Depending on the answer, going with opts would be best, but then the
> avr context AVOptions mechanism becomes at least partially
> dangerous/useless: what happens if the user sets such an AVOption on
> the context, but a later extension of AVFrame overrides it?

The options should set details that the avframe does not deliver (e.g.
resampler specific tunings).



>> +int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame 
>> *in,
>> +                      AVDictionary **opts)
>> +{
>> +    int ret;
>> +
>> +    if (avresample_is_open(avr)) {
>> +        avresample_close(avr);
>> +    }
> 
> This sure looks strange. Maybe avresample_close() should work even on a
> closed context.

Actually it should, we might introduce an avresample_reset though.


>> +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;
>> +}

This one could be exposed in case one wants do its own wrappers, maybe.

>> +
>> +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 output_frame_samples(AVAudioResampleContext *avr,
>> +                                       AVFrame *out, AVFrame *in)
>> +{
>> +    return avresample_available(avr) +
>> +           (avresample_get_delay(avr) + in->nb_samples) *
>> +           out->sample_rate / in->sample_rate;
>> +}
>> +
>> +int avresample_convert_frame(AVAudioResampleContext *avr,
>> +                             AVFrame *out, AVFrame *in)
>> +{
>> +    int ret;
>> +
>> +    if (!avresample_is_open(avr)) {
>> +        if ((ret = avresample_config(avr, out, in, NULL)) < 0)
>> +            return ret;
>> +        if ((ret = avresample_open(avr)) < 0)
>> +            return ret;
> 
> Doesn't avresample_config already open the context?

I was undecided where to put the open call. Thus the rfc =)

> It's very unusual that AVERROR codes can be bitwise or-ed, so maybe
> this should be documented. What's with these random looking magic
> numbers anyway?

Low collision chances =) Initially those were having the AVRESAMPLE_
namespace.

I'll add more documentation anyway.

Thanks for the review =)

lu
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to