On 05/24/2011 08:14 AM, Anton Khirnov wrote:

> From: Nicolas George <[email protected]>
> 
> Currently, only S16 quad, 5.1 and 7.1 are implemented.
> Implementing support for other formats/layouts and capture should be
> straightforward.
> 
> 7.1 support by Carl Eugen Hoyos.
> 
> Signed-off-by: Anton Khirnov <[email protected]>
> ---
>  libavdevice/alsa-audio-common.c |   90 
> +++++++++++++++++++++++++++++++++++++++
>  libavdevice/alsa-audio-enc.c    |   10 ++++-
>  libavdevice/alsa-audio.h        |    7 +++
>  3 files changed, 106 insertions(+), 1 deletions(-)
> 
> diff --git a/libavdevice/alsa-audio-common.c b/libavdevice/alsa-audio-common.c
> index ff6c9f8..e7fee7b 100644
> --- a/libavdevice/alsa-audio-common.c
> +++ b/libavdevice/alsa-audio-common.c
> @@ -43,6 +43,59 @@ static av_cold snd_pcm_format_t codec_id_to_pcm_format(int 
> codec_id)
>      }
>  }
>  
> +static void alsa_reorder_s16_out_51(const void *in_v, void *out_v, int n)
> +{
> +    const int16_t *in = in_v;
> +    int16_t *out = out_v;
> +
> +    while (n-- > 0) {
> +        out[0] = in[0];
> +        out[1] = in[1];
> +        out[2] = in[4];
> +        out[3] = in[5];
> +        out[4] = in[2];
> +        out[5] = in[3];
> +        in  += 6;
> +        out += 6;
> +    }
> +}
> +
> +static void alsa_reorder_s16_out_71(const void *in_v, void *out_v, int n)
> +{
> +    const int16_t *in = in_v;
> +    int16_t *out = out_v;
> +
> +    while (n-- > 0) {
> +        out[0] = in[0];
> +        out[1] = in[1];
> +        out[2] = in[4];
> +        out[3] = in[5];
> +        out[4] = in[2];
> +        out[5] = in[3];
> +        out[6] = in[6];
> +        out[7] = in[7];
> +        in  += 8;
> +        out += 8;
> +    }
> +}


I'm sure these could be more optimized, but I guess it doesn't matter
that much with ALSA output.  First thing that comes to mind is memcpy()
then loop doing FFSWAP() of 2/3 and 4/5 as int32_t.  These are also
great candidates for SIMD.  But then again... maybe not that important
in this case.

> +
> +#define REORDER_DUMMY ((void *)1)
> +
> +static av_cold ff_reorder_func find_reorder_func(int codec_id,
> +                                                 int64_t layout,
> +                                                 int out)
> +{
> +    return
> +    codec_id == CODEC_ID_PCM_S16LE || codec_id == CODEC_ID_PCM_S16BE ?
> +        layout == AV_CH_LAYOUT_QUAD ? REORDER_DUMMY :
> +        layout == AV_CH_LAYOUT_5POINT1_BACK || layout == 
> AV_CH_LAYOUT_5POINT1 ?
> +            out ? alsa_reorder_s16_out_51 : NULL :
> +        layout == AV_CH_LAYOUT_7POINT1 ?
> +            out ? alsa_reorder_s16_out_71 : NULL :
> +            NULL :
> +        NULL;
> +}
> +


This is one of the ugliest pieces of C I've seen in a while... how about
something like:

static av_cold ff_reorder_func find_reorder_func(int codec_id,
                                                 int64_t layout,
                                                 int out)
{
    /* only 16-bit channel reordering is currently supported */
    if (!(codec_id == CODEC_ID_PCM_S16LE ||
          codec_id == CODEC_ID_PCM_S16BE))
        return NULL;

    /* reordering is not needed for QUAD layout */
    if (layout == AV_CH_LAYOUT_QUAD)
        return REORDER_DUMMY;

    /* reordering input is not currently supported */
    if (!out)
        return NULL;

    if (layout == AV_CH_LAYOUT_5POINT1_BACK ||
        layout == AV_CH_LAYOUT_5POINT1)
        return alsa_reorder_s16_out_51;
    if (layout == AV_CH_LAYOUT_7POINT1)
        return alsa_reorder_s16_out_71;

    return NULL;
}

>  av_cold int ff_alsa_open(AVFormatContext *ctx, snd_pcm_stream_t mode,
>                           unsigned int *sample_rate,
>                           int channels, enum CodecID *codec_id)
> @@ -54,6 +107,7 @@ av_cold int ff_alsa_open(AVFormatContext *ctx, 
> snd_pcm_stream_t mode,
>      snd_pcm_t *h;
>      snd_pcm_hw_params_t *hw_params;
>      snd_pcm_uframes_t buffer_size, period_size;
> +    int64_t layout = ctx->streams[0]->codec->channel_layout;
>  
>      if (ctx->filename[0] == 0) audio_device = "default";
>      else                       audio_device = ctx->filename;
> @@ -146,6 +200,26 @@ av_cold int ff_alsa_open(AVFormatContext *ctx, 
> snd_pcm_stream_t mode,
>  
>      snd_pcm_hw_params_free(hw_params);
>  
> +    if (channels > 2 && layout) {
> +        s->reorder_func = find_reorder_func(*codec_id, layout,
> +                                            mode == SND_PCM_STREAM_PLAYBACK);
> +        if (s->reorder_func == REORDER_DUMMY) {
> +            s->reorder_func = NULL;
> +        } else if (s->reorder_func) {
> +            s->reorder_buf_size = buffer_size;
> +            s->reorder_buf = av_malloc(s->reorder_buf_size * s->frame_size);
> +            if (!s->reorder_buf)
> +                goto fail1;
> +        } else {
> +            char name[16];


16 is too small. should be at least 32 if not more. channel layout names
can be things like: "FL|FR|FC|LFE|BL|BR|FLC|FRC"

> +            av_get_channel_layout_string(name, sizeof(name), channels, 
> layout);
> +            av_log(ctx, AV_LOG_WARNING,
> +                   "ALSA channel layout unknown or unimplemented for %s 
> %s.\n",
> +                   name,
> +                   mode == SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
> +        }
> +    }
> +
>      s->h = h;
>      return 0;
>  
> @@ -160,6 +234,7 @@ av_cold int ff_alsa_close(AVFormatContext *s1)
>  {
>      AlsaData *s = s1->priv_data;
>  
> +    av_freep(&s->reorder_buf);
>      snd_pcm_close(s->h);
>      return 0;
>  }
> @@ -184,3 +259,18 @@ int ff_alsa_xrun_recover(AVFormatContext *s1, int err)
>      }
>      return err;
>  }
> +
> +int ff_alsa_extend_reorder_buf(AlsaData *s, int min_size)
> +{
> +    int size = s->reorder_buf_size;
> +    void *r;
> +
> +    while (size < min_size)
> +        size *= 2;
> +    r = av_realloc(s->reorder_buf, size * s->frame_size);
> +    if (!r)
> +        return AVERROR(ENOMEM);
> +    s->reorder_buf = r;
> +    s->reorder_buf_size = size;
> +    return 0;
> +}
> diff --git a/libavdevice/alsa-audio-enc.c b/libavdevice/alsa-audio-enc.c
> index ebe598c..f3782c5 100644
> --- a/libavdevice/alsa-audio-enc.c
> +++ b/libavdevice/alsa-audio-enc.c
> @@ -76,7 +76,15 @@ static int audio_write_packet(AVFormatContext *s1, 
> AVPacket *pkt)
>      int size     = pkt->size;
>      uint8_t *buf = pkt->data;
>  
> -    while((res = snd_pcm_writei(s->h, buf, size / s->frame_size)) < 0) {
> +    size /= s->frame_size;
> +    if (s->reorder_func) {
> +        if (size > s->reorder_buf_size)
> +            if (ff_alsa_extend_reorder_buf(s, size))
> +                return AVERROR(ENOMEM);
> +        s->reorder_func(buf, s->reorder_buf, size);
> +        buf = s->reorder_buf;
> +    }
> +    while ((res = snd_pcm_writei(s->h, buf, size)) < 0) {
>          if (res == -EAGAIN) {
>  
>              return AVERROR(EAGAIN);
> diff --git a/libavdevice/alsa-audio.h b/libavdevice/alsa-audio.h
> index 7a1b018..2f25ce4 100644
> --- a/libavdevice/alsa-audio.h
> +++ b/libavdevice/alsa-audio.h
> @@ -39,10 +39,15 @@
>          other formats */
>  #define DEFAULT_CODEC_ID AV_NE(CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE)
>  
> +typedef void (*ff_reorder_func)(const void *, void *, int);
> +
>  typedef struct {
>      snd_pcm_t *h;
>      int frame_size;  ///< preferred size for reads and writes
>      int period_size; ///< bytes per sample * channels
> +    ff_reorder_func reorder_func;
> +    void *reorder_buf;
> +    int reorder_buf_size; ///< in frames
>  } AlsaData;
>  
>  /**
> @@ -82,4 +87,6 @@ int ff_alsa_close(AVFormatContext *s1);
>   */
>  int ff_alsa_xrun_recover(AVFormatContext *s1, int err);
>  
> +int ff_alsa_extend_reorder_buf(AlsaData *s, int size);
> +
>  #endif /* AVDEVICE_ALSA_AUDIO_H */


patch looks alright otherwise.

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

Reply via email to