"Fossies" - the Fresh Open Source Software Archive

Member "alsa-plugins-1.2.5/a52/pcm_a52.c" (27 May 2021, 25083 Bytes) of package /linux/misc/alsa-plugins-1.2.5.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pcm_a52.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.2_vs_1.2.5.

    1 /*
    2  * A52 Output Plugin
    3  *
    4  * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
    5  *
    6  * This library is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU Lesser General Public License as
    8  * published by the Free Software Foundation; either version 2.1 of
    9  * the License, or (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with this library; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   19  */
   20 
   21 #include <stdio.h>
   22 #include <string.h>
   23 #define __USE_XOPEN
   24 #include <unistd.h>
   25 #include <alsa/asoundlib.h>
   26 #include <alsa/pcm_external.h>
   27 #include <alsa/pcm_plugin.h>
   28 #include <libavcodec/avcodec.h>
   29 #include <libavutil/avutil.h>
   30 
   31 /* some compatibility wrappers */
   32 #ifndef AV_VERSION_INT
   33 #define AV_VERSION_INT(a, b, c) (((a) << 16) | ((b) << 8) | (c))
   34 #endif
   35 #ifndef LIBAVCODEC_VERSION_INT
   36 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
   37                                                LIBAVCODEC_VERSION_MINOR, \
   38                                                LIBAVCODEC_VERSION_MICRO)
   39 #endif
   40 
   41 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 34, 0)
   42 #include <libavutil/channel_layout.h>
   43 #include <libavutil/mem.h>
   44 #define USE_AVCODEC_FRAME
   45 #endif
   46 
   47 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 0, 0)
   48 #ifndef AV_CH_LAYOUT_STEREO
   49 #define AV_CH_LAYOUT_STEREO CH_LAYOUT_STEREO
   50 #define AV_CH_LAYOUT_QUAD   CH_LAYOUT_QUAD
   51 #define AV_CH_LAYOUT_5POINT1    CH_LAYOUT_5POINT1
   52 #endif
   53 #endif
   54 
   55 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 95, 0)
   56 #ifndef AV_SAMPLE_FMT_S16
   57 #define AV_SAMPLE_FMT_S16   SAMPLE_FMT_S16
   58 #endif
   59 #endif
   60 
   61 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 25, 0)
   62 #define AV_CODEC_ID_AC3 CODEC_ID_AC3
   63 #endif
   64 
   65 #if LIBAVCODEC_VERSION_INT < 0x371c01
   66 #define av_frame_alloc avcodec_alloc_frame
   67 #define av_frame_free avcodec_free_frame
   68 #endif
   69 
   70 struct a52_ctx {
   71     snd_pcm_ioplug_t io;
   72     snd_pcm_t *slave;
   73     AVCodec *codec;
   74     AVCodecContext *avctx;
   75     snd_pcm_format_t format;
   76     int av_format;
   77     unsigned int channels;
   78     unsigned int rate;
   79     unsigned int bitrate;
   80     short *inbuf;
   81     unsigned char *outbuf;
   82     int outbuf_size;
   83     snd_pcm_uframes_t transfer;
   84     int remain;
   85     int filled;
   86     unsigned int slave_period_size;
   87     unsigned int slave_buffer_size;
   88     snd_pcm_hw_params_t *hw_params;
   89 #ifdef USE_AVCODEC_FRAME
   90     AVFrame *frame;
   91     int is_planar;
   92 #endif
   93 };
   94 
   95 #ifdef USE_AVCODEC_FRAME
   96 #define use_planar(rec)     (rec)->is_planar
   97 #else
   98 #define use_planar(rec)     0
   99 #endif
  100 
  101 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 34, 0)
  102 static int do_encode(struct a52_ctx *rec)
  103 {
  104     AVPacket pkt = {
  105         .data = rec->outbuf + 8,
  106         .size = rec->outbuf_size - 8
  107     };
  108     int got_frame;
  109 
  110     avcodec_encode_audio2(rec->avctx, &pkt, rec->frame, &got_frame);
  111     return pkt.size;
  112 }
  113 #else
  114 static int do_encode(struct a52_ctx *rec)
  115 {
  116     return avcodec_encode_audio(rec->avctx, rec->outbuf + 8,
  117                     rec->outbuf_size - 8,
  118                     rec->inbuf);
  119 }
  120 #endif
  121 
  122 /* convert the PCM data to A52 stream in IEC958 */
  123 static void convert_data(struct a52_ctx *rec)
  124 {
  125     int out_bytes = do_encode(rec);
  126 
  127     rec->outbuf[0] = 0xf8; /* sync words */
  128     rec->outbuf[1] = 0x72;
  129     rec->outbuf[2] = 0x4e;
  130     rec->outbuf[3] = 0x1f;
  131     rec->outbuf[4] = rec->outbuf[13] & 7; /* bsmod */
  132     rec->outbuf[5] = 0x01; /* data type */
  133     rec->outbuf[6] = ((out_bytes * 8) >> 8) & 0xff;
  134     rec->outbuf[7] = (out_bytes * 8) & 0xff;
  135     /* swap bytes for little-endian 16bit */
  136     if (rec->format == SND_PCM_FORMAT_S16_LE)
  137         swab(rec->outbuf, rec->outbuf, out_bytes + 8);
  138     memset(rec->outbuf +  8 + out_bytes, 0,
  139            rec->outbuf_size - 8 - out_bytes);
  140     rec->remain = rec->outbuf_size / 4;
  141     rec->filled = 0;
  142 }
  143 
  144 /* write pending encoded data to the slave pcm */
  145 static int write_out_pending(snd_pcm_ioplug_t *io, struct a52_ctx *rec)
  146 {
  147     int err, ofs = (rec->avctx->frame_size - rec->remain) * 4;
  148 
  149     if (! rec->remain)
  150         return 0;
  151 
  152     while (rec->remain) {
  153         err = snd_pcm_writei(rec->slave, rec->outbuf + ofs, rec->remain);
  154         if (err < 0) {
  155             if (err == -EPIPE)
  156                 io->state = SND_PCM_STATE_XRUN;
  157             if (err == -EAGAIN)
  158                 break;
  159             return err;
  160         } else if (! err)
  161             break;
  162         if (err < rec->remain)
  163             ofs += (rec->remain - err) * 4;
  164         rec->remain -= err;
  165     }
  166     return 0;
  167 }
  168 
  169 /*
  170  * drain callback
  171  */
  172 #ifdef USE_AVCODEC_FRAME
  173 static void clear_remaining_planar_data(snd_pcm_ioplug_t *io)
  174 {
  175     struct a52_ctx *rec = io->private_data;
  176     unsigned int i;
  177 
  178     for (i = 0; i < io->channels; i++)
  179         memset(rec->frame->data[i] + rec->filled * 2, 0,
  180                (rec->avctx->frame_size - rec->filled) * 2);
  181 }
  182 #else
  183 #define clear_remaining_planar_data(io) /*NOP*/
  184 #endif
  185 
  186 static int a52_drain(snd_pcm_ioplug_t *io)
  187 {
  188     struct a52_ctx *rec = io->private_data;
  189     int err;
  190 
  191     if (rec->filled) {
  192         if ((err = write_out_pending(io, rec)) < 0)
  193             return err;
  194         /* remaining data must be converted and sent out */
  195         if (use_planar(rec))
  196             clear_remaining_planar_data(io);
  197         else {
  198             memset(rec->inbuf + rec->filled * io->channels, 0,
  199                    (rec->avctx->frame_size - rec->filled) * io->channels * 2);
  200         }
  201         convert_data(rec);
  202     }
  203     err = write_out_pending(io, rec);
  204     if (err < 0)
  205         return err;
  206 
  207     return snd_pcm_drain(rec->slave);
  208 }
  209 
  210 /* check whether the areas consist of a continuous interleaved stream */
  211 static int check_interleaved(const snd_pcm_channel_area_t *areas,
  212                  unsigned int channels)
  213 {
  214     unsigned int ch;
  215 
  216     if (channels > 4) /* we need re-routing for 6 channels */
  217         return 0;
  218 
  219     for (ch = 0; ch < channels; ch++) {
  220         if (areas[ch].addr != areas[0].addr ||
  221             areas[ch].first != ch * 16 ||
  222             areas[ch].step != channels * 16)
  223             return 0;
  224     }
  225     return 1;
  226 }
  227 
  228 /* Fill the input PCM to the internal buffer until a52 frames,
  229  * then covert and write it out.
  230  *
  231  * Returns the number of processed frames.
  232  */
  233 static int fill_data(snd_pcm_ioplug_t *io,
  234              const snd_pcm_channel_area_t *areas,
  235              unsigned int offset, unsigned int size,
  236              int interleaved)
  237 {
  238     struct a52_ctx *rec = io->private_data;
  239     unsigned int len = rec->avctx->frame_size - rec->filled;
  240     short *src, *dst;
  241     unsigned int src_step;
  242     int err;
  243     static unsigned int ch_index[3][6] = {
  244         { 0, 1 },
  245         { 0, 1, 2, 3 },
  246 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 26, 0)
  247         /* current libavcodec expects SMPTE order */
  248         { 0, 1, 4, 5, 2, 3 },
  249 #else
  250         /* libavcodec older than r18540 expects A52 order */
  251         { 0, 4, 1, 2, 3, 5 },
  252 #endif
  253     };
  254 
  255     if ((err = write_out_pending(io, rec)) < 0)
  256         return err;
  257 
  258     /* If there are still frames left in outbuf, we can't
  259      * accept a full a52 frame, because this would overwrite
  260      * the frames in outbuf. */
  261     if (rec->remain && len)
  262         len--;
  263 
  264     if (size > len)
  265         size = len;
  266 
  267     dst = rec->inbuf + rec->filled * io->channels;
  268     if (!use_planar(rec) && interleaved) {
  269         memcpy(dst, areas->addr + offset * io->channels * 2,
  270                size * io->channels * 2);
  271     } else {
  272         unsigned int i, ch, dst_step;
  273         short *dst1;
  274 
  275         /* flatten copy to n-channel interleaved */
  276         dst_step = io->channels;
  277         for (ch = 0; ch < io->channels; ch++, dst++) {
  278             const snd_pcm_channel_area_t *ap;
  279             ap = &areas[ch_index[io->channels / 2 - 1][ch]];
  280             src = (short *)(ap->addr +
  281                     (ap->first + offset * ap->step) / 8);
  282 
  283 #ifdef USE_AVCODEC_FRAME
  284             if (use_planar(rec)) {
  285                 memcpy(rec->frame->data[ch], src, size * 2);
  286                 continue;
  287             }
  288 #endif
  289             dst1 = dst;
  290             src_step = ap->step / 16; /* in word */
  291             for (i = 0; i < size; i++) {
  292                 *dst1 = *src;
  293                 src += src_step;
  294                 dst1 += dst_step;
  295             }
  296         }
  297     }
  298     rec->filled += size;
  299     if (rec->filled == rec->avctx->frame_size) {
  300         convert_data(rec);
  301         write_out_pending(io, rec);
  302     }
  303     return (int)size;
  304 }
  305 
  306 /*
  307  * transfer callback
  308  */
  309 static snd_pcm_sframes_t a52_transfer(snd_pcm_ioplug_t *io,
  310                       const snd_pcm_channel_area_t *areas,
  311                       snd_pcm_uframes_t offset,
  312                       snd_pcm_uframes_t size)
  313 {
  314     struct a52_ctx *rec = io->private_data;
  315     snd_pcm_sframes_t result = 0;
  316     int err = 0;
  317     int interleaved = check_interleaved(areas, io->channels);
  318 
  319     do {
  320         err = fill_data(io, areas, offset, size, interleaved);
  321         if (err <= 0)
  322             break;
  323         offset += (unsigned int)err;
  324         size -= (unsigned int)err;
  325         result += err;
  326         rec->transfer += err;
  327     } while (size);
  328     return result > 0 ? result : err;
  329 }
  330 
  331 /*
  332  * pointer callback
  333  *
  334  * Calculate the current position from the available frames of slave PCM
  335  */
  336 static snd_pcm_sframes_t a52_pointer(snd_pcm_ioplug_t *io)
  337 {
  338     struct a52_ctx *rec = io->private_data;
  339     snd_pcm_sframes_t avail;
  340     snd_pcm_state_t state;
  341 
  342     state = snd_pcm_state(rec->slave);
  343     switch (state) {
  344     case SND_PCM_STATE_RUNNING:
  345     case SND_PCM_STATE_DRAINING:
  346         break;
  347     case SND_PCM_STATE_XRUN:
  348     case SND_PCM_STATE_SUSPENDED:
  349         return -EPIPE;
  350     default:
  351         return 0;
  352     }
  353 
  354     avail = 0;
  355 
  356     /* Write what we have from outbuf. */
  357     write_out_pending(io, rec);
  358 
  359     /* If there is anything remaining in outbuf, we can't
  360      * accept any full packets. */
  361     if (rec->remain == 0)
  362     {
  363         /* Round the slave frames to multiples of the packet size. */
  364         avail += (snd_pcm_avail_update(rec->slave) / rec->avctx->frame_size) * rec->avctx->frame_size;
  365     }
  366 
  367     if (avail < 0)
  368         avail = 0;
  369     else if (avail >= io->buffer_size)
  370         avail = io->buffer_size - 1;
  371 
  372     return (io->appl_ptr + avail) % io->buffer_size;
  373 }
  374 
  375 /* set up the fixed parameters of slave PCM hw_parmas */
  376 static int a52_slave_hw_params_half(struct a52_ctx *rec)
  377 {
  378     int err;
  379 
  380     if ((err = snd_pcm_hw_params_malloc(&rec->hw_params)) < 0)
  381         return err;
  382 
  383     if ((err = snd_pcm_hw_params_any(rec->slave, rec->hw_params)) < 0) {
  384         SNDERR("Cannot get slave hw_params");
  385         goto out;
  386     }
  387     if ((err = snd_pcm_hw_params_set_access(rec->slave, rec->hw_params,
  388                         SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
  389         SNDERR("Cannot set slave access RW_INTERLEAVED");
  390         goto out;
  391     }
  392     if ((err = snd_pcm_hw_params_set_channels(rec->slave, rec->hw_params, 2)) < 0) {
  393         SNDERR("Cannot set slave channels 2");
  394         goto out;
  395     }
  396     if ((err = snd_pcm_hw_params_set_format(rec->slave, rec->hw_params,
  397                         rec->format)) < 0) {
  398         SNDERR("Cannot set slave format");
  399         goto out;
  400     }
  401     if ((err = snd_pcm_hw_params_set_rate(rec->slave, rec->hw_params, rec->rate, 0)) < 0) {
  402         SNDERR("Cannot set slave rate %d", rec->rate);
  403         goto out;
  404     }
  405     return 0;
  406 
  407  out:
  408     free(rec->hw_params);
  409     rec->hw_params = NULL;
  410     return err;
  411 }
  412 
  413 /*
  414  * hw_params callback
  415  *
  416  * Set up slave PCM according to the current parameters
  417  */
  418 static int a52_hw_params(snd_pcm_ioplug_t *io,
  419              snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
  420 {
  421     struct a52_ctx *rec = io->private_data;
  422     snd_pcm_uframes_t period_size;
  423     snd_pcm_uframes_t buffer_size;
  424     int err;
  425 
  426     if (! rec->hw_params) {
  427         err = a52_slave_hw_params_half(rec);
  428         if (err < 0)
  429             return err;
  430     }
  431     period_size = io->period_size;
  432     if ((err = snd_pcm_hw_params_set_period_size_near(rec->slave, rec->hw_params,
  433                               &period_size, NULL)) < 0) {
  434         SNDERR("Cannot set slave period size %ld", period_size);
  435         return err;
  436     }
  437     buffer_size = io->buffer_size;
  438     if ((err = snd_pcm_hw_params_set_buffer_size_near(rec->slave, rec->hw_params,
  439                               &buffer_size)) < 0) {
  440         SNDERR("Cannot set slave buffer size %ld", buffer_size);
  441         return err;
  442     }
  443     if ((err = snd_pcm_hw_params(rec->slave, rec->hw_params)) < 0) {
  444         SNDERR("Cannot set slave hw_params");
  445         return err;
  446     }
  447     rec->slave_period_size = period_size;
  448     rec->slave_buffer_size = buffer_size;
  449 
  450     return 0;
  451 }
  452 
  453 /*
  454  * hw_free callback
  455  */
  456 static int a52_hw_free(snd_pcm_ioplug_t *io)
  457 {
  458     struct a52_ctx *rec = io->private_data;
  459 
  460     free(rec->hw_params);
  461     rec->hw_params = NULL;
  462     return snd_pcm_hw_free(rec->slave);
  463 }
  464 
  465 /*
  466  * sw_params callback
  467  *
  468  * Set up slave PCM sw_params
  469  */
  470 static int a52_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params)
  471 {
  472     struct a52_ctx *rec = io->private_data;
  473     snd_pcm_sw_params_t *sparams;
  474     snd_pcm_uframes_t avail_min, start_threshold;
  475     int len;
  476 
  477     snd_pcm_sw_params_get_avail_min(params, &avail_min);
  478     snd_pcm_sw_params_get_start_threshold(params, &start_threshold);
  479 
  480     len = avail_min;
  481     len += (int)rec->slave_buffer_size - (int)io->buffer_size;
  482     if (len < 0)
  483         avail_min = 1;
  484     else
  485         avail_min = len;
  486     snd_pcm_sw_params_alloca(&sparams);
  487     snd_pcm_sw_params_current(rec->slave, sparams);
  488     snd_pcm_sw_params_set_avail_min(rec->slave, sparams, avail_min);
  489     snd_pcm_sw_params_set_start_threshold(rec->slave, sparams,
  490                           start_threshold);
  491 
  492     return snd_pcm_sw_params(rec->slave, sparams);
  493 }
  494 
  495 /*
  496  * start and stop callbacks - just trigger slave PCM
  497  */
  498 static int a52_start(snd_pcm_ioplug_t *io)
  499 {
  500     struct a52_ctx *rec = io->private_data;
  501 
  502     /* When trying to start a PCM that's already running, the result is
  503        EBADFD. We might have implicitly started the buffer by filling it
  504        up, so just ignore this request if we're already running. */
  505     if (snd_pcm_state(rec->slave) == SND_PCM_STATE_RUNNING)
  506         return 0;
  507 
  508     return snd_pcm_start(rec->slave);
  509 }
  510 
  511 static int a52_stop(snd_pcm_ioplug_t *io)
  512 {
  513     struct a52_ctx *rec = io->private_data;
  514 
  515     return snd_pcm_drop(rec->slave);
  516 }
  517 
  518 /* release resources */
  519 static void a52_free(struct a52_ctx *rec)
  520 {
  521     if (rec->avctx) {
  522         avcodec_close(rec->avctx);
  523         av_free(rec->avctx);
  524         rec->avctx = NULL;
  525     }
  526 
  527 #ifdef USE_AVCODEC_FRAME
  528     if (rec->frame) {
  529         av_freep(&rec->frame->data[0]);
  530         rec->inbuf = NULL;
  531     }
  532 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
  533     av_frame_free(&rec->frame);
  534 #else
  535     av_freep(&rec->frame);
  536 #endif
  537 #endif
  538 
  539     free(rec->inbuf);
  540     rec->inbuf = NULL;
  541     free(rec->outbuf);
  542     rec->outbuf = NULL;
  543 }
  544 
  545 /*
  546  * prepare callback
  547  *
  548  * Allocate internal buffers and set up libavcodec
  549  */
  550 
  551 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 3, 0)
  552 static void set_channel_layout(snd_pcm_ioplug_t *io)
  553 {
  554     struct a52_ctx *rec = io->private_data;
  555     switch (io->channels) {
  556     case 2:
  557         rec->avctx->channel_layout = AV_CH_LAYOUT_STEREO;
  558         break;
  559     case 4:
  560         rec->avctx->channel_layout = AV_CH_LAYOUT_QUAD;
  561         break;
  562     case 6:
  563         rec->avctx->channel_layout = AV_CH_LAYOUT_5POINT1;
  564         break;
  565     default:
  566         break;
  567     }
  568 }
  569 #else
  570 #define set_channel_layout(io) /* NOP */
  571 #endif
  572 
  573 static int alloc_input_buffer(snd_pcm_ioplug_t *io)
  574 {
  575     struct a52_ctx *rec = io->private_data;
  576 #ifdef USE_AVCODEC_FRAME
  577     rec->frame = av_frame_alloc();
  578     if (!rec->frame)
  579         return -ENOMEM;
  580     if (av_samples_alloc(rec->frame->data, rec->frame->linesize,
  581                  io->channels, rec->avctx->frame_size,
  582                  rec->avctx->sample_fmt, 0) < 0)
  583         return -ENOMEM;
  584     rec->frame->nb_samples = rec->avctx->frame_size;
  585     rec->inbuf = (short *)rec->frame->data[0];
  586 #else
  587     rec->inbuf = malloc(rec->avctx->frame_size * 2 * io->channels);
  588 #endif
  589     if (!rec->inbuf)
  590         return -ENOMEM;
  591     return 0;
  592 }
  593 
  594 static int a52_prepare(snd_pcm_ioplug_t *io)
  595 {
  596     struct a52_ctx *rec = io->private_data;
  597     int err;
  598 
  599     a52_free(rec);
  600 
  601 #ifdef USE_AVCODEC_FRAME
  602     rec->avctx = avcodec_alloc_context3(rec->codec);
  603 #else
  604     rec->avctx = avcodec_alloc_context();
  605 #endif
  606     if (!rec->avctx)
  607         return -ENOMEM;
  608 
  609     rec->avctx->bit_rate = rec->bitrate * 1000;
  610     rec->avctx->sample_rate = io->rate;
  611     rec->avctx->channels = io->channels;
  612     rec->avctx->sample_fmt = rec->av_format;
  613 
  614     set_channel_layout(io);
  615 
  616 
  617 #ifdef USE_AVCODEC_FRAME
  618     err = avcodec_open2(rec->avctx, rec->codec, NULL);
  619 #else
  620     err = avcodec_open(rec->avctx, rec->codec);
  621 #endif
  622     if (err < 0)
  623         return -EINVAL;
  624 
  625     rec->outbuf_size = rec->avctx->frame_size * 4;
  626     rec->outbuf = malloc(rec->outbuf_size);
  627     if (! rec->outbuf)
  628         return -ENOMEM;
  629 
  630     if (alloc_input_buffer(io))
  631         return -ENOMEM;
  632 
  633     rec->transfer = 0;
  634     rec->remain = 0;
  635     rec->filled = 0;
  636 
  637     return snd_pcm_prepare(rec->slave);
  638 }
  639 
  640 /*
  641  * poll-related callbacks - just pass to slave PCM
  642  */
  643 static int a52_poll_descriptors_count(snd_pcm_ioplug_t *io)
  644 {
  645     struct a52_ctx *rec = io->private_data;
  646     return snd_pcm_poll_descriptors_count(rec->slave);
  647 }
  648 
  649 static int a52_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
  650                 unsigned int space)
  651 {
  652     struct a52_ctx *rec = io->private_data;
  653     return snd_pcm_poll_descriptors(rec->slave, pfd, space);
  654 }
  655 
  656 static int a52_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
  657                 unsigned int nfds, unsigned short *revents)
  658 {
  659     struct a52_ctx *rec = io->private_data;
  660     return snd_pcm_poll_descriptors_revents(rec->slave, pfd, nfds, revents);
  661 }
  662 
  663 /*
  664  * close callback
  665  */
  666 static int a52_close(snd_pcm_ioplug_t *io)
  667 {
  668     struct a52_ctx *rec = io->private_data;
  669     snd_pcm_t *slave = rec->slave;
  670 
  671     a52_free(rec);
  672     if (slave) {
  673         rec->slave = NULL;
  674         return snd_pcm_close(slave);
  675     }
  676     return 0;
  677 }
  678                   
  679 #if SND_PCM_IOPLUG_VERSION >= 0x10002
  680 static unsigned int chmap4[4] = {
  681     SND_CHMAP_FL, SND_CHMAP_FR,
  682     SND_CHMAP_RL, SND_CHMAP_RR,
  683 };
  684 static unsigned int chmap6[6] = {
  685     SND_CHMAP_FL, SND_CHMAP_FR,
  686     SND_CHMAP_RL, SND_CHMAP_RR,
  687     SND_CHMAP_FC, SND_CHMAP_LFE,
  688 };
  689 
  690 static snd_pcm_chmap_query_t **a52_query_chmaps(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED)
  691 {
  692     snd_pcm_chmap_query_t **maps;
  693     int i;
  694 
  695     maps = calloc(4, sizeof(void *));
  696     if (!maps)
  697         return NULL;
  698     for (i = 0; i < 3; i++) {
  699         snd_pcm_chmap_query_t *p;
  700         p = maps[i] = calloc((i + 1) * 2 + 2, sizeof(int));
  701         if (!p) {
  702             snd_pcm_free_chmaps(maps);
  703             return NULL;
  704         }
  705         p->type = SND_CHMAP_TYPE_FIXED;
  706         p->map.channels = (i + 1) * 2;
  707         memcpy(p->map.pos, i < 2 ? chmap4 : chmap6,
  708                (i + 1) * 2 * sizeof(int));
  709     }
  710     return maps;
  711 }
  712 
  713 static snd_pcm_chmap_t *a52_get_chmap(snd_pcm_ioplug_t *io)
  714 {
  715     snd_pcm_chmap_t *map;
  716 
  717     if ((io->channels % 2) || io->channels < 2 || io->channels > 6)
  718         return NULL;
  719     map = malloc((io->channels + 1) * sizeof(int));
  720     if (!map)
  721         return NULL;
  722     map->channels = io->channels;
  723     memcpy(map->pos, io->channels < 6 ? chmap4 : chmap6,
  724            io->channels * sizeof(int));
  725     return map;
  726 }
  727 #endif /* SND_PCM_IOPLUG_VERSION >= 0x10002 */
  728 
  729 /*
  730  * callback table
  731  */
  732 static snd_pcm_ioplug_callback_t a52_ops = {
  733     .start = a52_start,
  734     .stop = a52_stop,
  735     .pointer = a52_pointer,
  736     .transfer = a52_transfer,
  737     .close = a52_close,
  738     .hw_params = a52_hw_params,
  739     .hw_free = a52_hw_free,
  740     .sw_params = a52_sw_params,
  741     .prepare = a52_prepare,
  742     .drain = a52_drain,
  743     .poll_descriptors_count = a52_poll_descriptors_count,
  744     .poll_descriptors = a52_poll_descriptors,
  745     .poll_revents = a52_poll_revents,
  746 #if SND_PCM_IOPLUG_VERSION >= 0x10002
  747     .query_chmaps = a52_query_chmaps,
  748     .get_chmap = a52_get_chmap,
  749 #endif /* SND_PCM_IOPLUG_VERSION >= 0x10002 */
  750 };
  751 
  752 /*
  753  * set up h/w constraints
  754  *
  755  * set the period size identical with A52 frame size.
  756  * the max buffer size is calculated from the max buffer size
  757  * of the slave PCM
  758  */
  759 
  760 #define A52_FRAME_SIZE  1536
  761 
  762 #define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0]))
  763 
  764 static int a52_set_hw_constraint(struct a52_ctx *rec)
  765 {
  766     static unsigned int accesses[] = {
  767         SND_PCM_ACCESS_MMAP_INTERLEAVED,
  768         SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
  769         SND_PCM_ACCESS_RW_INTERLEAVED,
  770         SND_PCM_ACCESS_RW_NONINTERLEAVED
  771     };
  772     static unsigned int accesses_planar[] = {
  773         SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
  774         SND_PCM_ACCESS_RW_NONINTERLEAVED
  775     };
  776     unsigned int formats[] = { SND_PCM_FORMAT_S16 };
  777     int err;
  778     snd_pcm_uframes_t buffer_max;
  779     unsigned int period_bytes, max_periods;
  780 
  781     if (use_planar(rec))
  782         err = snd_pcm_ioplug_set_param_list(&rec->io,
  783                             SND_PCM_IOPLUG_HW_ACCESS,
  784                             ARRAY_SIZE(accesses_planar),
  785                             accesses_planar);
  786     else
  787         err = snd_pcm_ioplug_set_param_list(&rec->io,
  788                             SND_PCM_IOPLUG_HW_ACCESS,
  789                             ARRAY_SIZE(accesses),
  790                             accesses);
  791     if (err < 0)
  792         return err;
  793 
  794     if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT,
  795                          ARRAY_SIZE(formats), formats)) < 0 ||
  796         (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_CHANNELS,
  797                            rec->channels, rec->channels)) < 0 ||
  798         (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_RATE,
  799                            rec->rate, rec->rate)) < 0)
  800         return err;
  801 
  802     if ((err = a52_slave_hw_params_half(rec)) < 0)
  803         return err;
  804 
  805     snd_pcm_hw_params_get_buffer_size_max(rec->hw_params, &buffer_max);
  806     period_bytes = A52_FRAME_SIZE * 2 * rec->channels;
  807     max_periods = buffer_max / A52_FRAME_SIZE;
  808 
  809     if ((err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
  810                            period_bytes, period_bytes)) < 0 ||
  811         (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIODS,
  812                            2, max_periods)) < 0)
  813         return err;
  814 
  815     return 0;
  816 }
  817 
  818 /*
  819  * Main entry point
  820  */
  821 SND_PCM_PLUGIN_DEFINE_FUNC(a52)
  822 {
  823     snd_config_iterator_t i, next;
  824     int err;
  825     const char *card = NULL;
  826     const char *pcm_string = NULL;
  827     unsigned int rate = 48000;
  828     unsigned int bitrate = 448;
  829     unsigned int channels = 6;
  830     snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
  831     char devstr[128], tmpcard[8];
  832     struct a52_ctx *rec;
  833     
  834     if (stream != SND_PCM_STREAM_PLAYBACK) {
  835         SNDERR("a52 is only for playback");
  836         return -EINVAL;
  837     }
  838 
  839     snd_config_for_each(i, next, conf) {
  840         snd_config_t *n = snd_config_iterator_entry(i);
  841         const char *id;
  842         if (snd_config_get_id(n, &id) < 0)
  843             continue;
  844         if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
  845             continue;
  846         if (strcmp(id, "card") == 0) {
  847             if (snd_config_get_string(n, &card) < 0) {
  848                 long val;
  849                 err = snd_config_get_integer(n, &val);
  850                 if (err < 0) {
  851                     SNDERR("Invalid type for %s", id);
  852                     return -EINVAL;
  853                 }
  854                 snprintf(tmpcard, sizeof(tmpcard), "%ld", val);
  855                 card = tmpcard;
  856             }
  857             continue;
  858         }
  859         if (strcmp(id, "slavepcm") == 0) {
  860             if (snd_config_get_string(n, &pcm_string) < 0) {
  861                 SNDERR("a52 slavepcm must be a string");
  862                 return -EINVAL;
  863             }
  864             continue;
  865         }
  866         if (strcmp(id, "rate") == 0) {
  867             long val;
  868             if (snd_config_get_integer(n, &val) < 0) {
  869                 SNDERR("Invalid type for %s", id);
  870                 return -EINVAL;
  871             }
  872             rate = val;
  873             if (rate != 44100 && rate != 48000) {
  874                 SNDERR("rate must be 44100 or 48000");
  875                 return -EINVAL;
  876             }
  877             continue;
  878         }
  879         if (strcmp(id, "bitrate") == 0) {
  880             long val;
  881             if (snd_config_get_integer(n, &val) < 0) {
  882                 SNDERR("Invalid type for %s", id);
  883                 return -EINVAL;
  884             }
  885             bitrate = val;
  886             if (bitrate < 128 || bitrate > 1000) {
  887                 SNDERR("Invalid bitrate value %d", bitrate);
  888                 return -EINVAL;
  889             }
  890             continue;
  891         }
  892         if (strcmp(id, "channels") == 0) {
  893             long val;
  894             if (snd_config_get_integer(n, &val) < 0) {
  895                 SNDERR("Invalid type for %s", id);
  896                 return -EINVAL;
  897             }
  898             channels = val;
  899             if (channels != 2 && channels != 4 && channels != 6) {
  900                 SNDERR("channels must be 2, 4 or 6");
  901                 return -EINVAL;
  902             }
  903             continue;
  904         }
  905         if (strcmp(id, "format") == 0) {
  906             const char *str;
  907             err = snd_config_get_string(n, &str);
  908             if (err < 0) {
  909                 SNDERR("invalid type for %s", id);
  910                 return -EINVAL;
  911             }
  912             format = snd_pcm_format_value(str);
  913             if (format == SND_PCM_FORMAT_UNKNOWN) {
  914                 SNDERR("unknown format %s", str);
  915                 return -EINVAL;
  916             }
  917             if (format != SND_PCM_FORMAT_S16_LE &&
  918                 format != SND_PCM_FORMAT_S16_BE) {
  919                 SNDERR("Only S16_LE/BE formats are allowed");
  920                 return -EINVAL;
  921             }
  922             continue;
  923         }
  924         SNDERR("Unknown field %s", id);
  925         return -EINVAL;
  926     }
  927 
  928     rec = calloc(1, sizeof(*rec));
  929     if (! rec) {
  930         SNDERR("cannot allocate");
  931         return -ENOMEM;
  932     }
  933 
  934     rec->rate = rate;
  935     rec->bitrate = bitrate;
  936     rec->channels = channels;
  937     rec->format = format;
  938 
  939 #ifndef USE_AVCODEC_FRAME
  940     avcodec_init();
  941 #endif
  942     avcodec_register_all();
  943 
  944     rec->codec = avcodec_find_encoder_by_name("ac3_fixed");
  945     if (rec->codec == NULL)
  946         rec->codec = avcodec_find_encoder_by_name("ac3");
  947     if (rec->codec == NULL) 
  948         rec->codec = avcodec_find_encoder(AV_CODEC_ID_AC3);
  949     if (rec->codec == NULL) {
  950         SNDERR("Cannot find codec engine");
  951         err = -EINVAL;
  952         goto error;
  953     }
  954 
  955     if (! pcm_string || pcm_string[0] == '\0') {
  956         snprintf(devstr, sizeof(devstr),
  957              "iec958:{AES0 0x%x AES1 0x%x AES2 0x%x AES3 0x%x %s%s}",
  958              IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO |
  959              IEC958_AES0_CON_NOT_COPYRIGHT,
  960              IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
  961              0, rate == 48000 ? IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100,
  962              card ? " CARD " : "",
  963              card ? card : "");
  964         err = snd_pcm_open(&rec->slave, devstr, stream, mode);
  965         if (err < 0)
  966             goto error;
  967         /* in case the slave doesn't support S16 format */
  968         err = snd_pcm_linear_open(&rec->slave, NULL, SND_PCM_FORMAT_S16,
  969                       rec->slave, 1);
  970         if (err < 0)
  971             goto error;
  972     } else {
  973         err = snd_pcm_open(&rec->slave, pcm_string, stream, mode);
  974         if (err < 0)
  975             goto error;
  976     }
  977 
  978     rec->io.version = SND_PCM_IOPLUG_VERSION;
  979     rec->io.name = "A52 Output Plugin";
  980     rec->io.mmap_rw = 0;
  981     rec->io.callback = &a52_ops;
  982     rec->io.private_data = rec;
  983 #ifdef USE_AVCODEC_FRAME
  984     rec->av_format = rec->codec->sample_fmts[0];
  985     rec->is_planar = av_sample_fmt_is_planar(rec->av_format);
  986 #else
  987     rec->av_format = AV_SAMPLE_FMT_S16;
  988 #endif
  989 
  990     err = snd_pcm_ioplug_create(&rec->io, name, stream, mode);
  991     if (err < 0)
  992         goto error;
  993 
  994     if ((err = a52_set_hw_constraint(rec)) < 0) {
  995         snd_pcm_ioplug_delete(&rec->io);
  996         goto error;
  997     }
  998 
  999     *pcmp = rec->io.pcm;
 1000     return 0;
 1001 
 1002  error:
 1003     if (rec->slave)
 1004         snd_pcm_close(rec->slave);
 1005     free(rec);
 1006     return err;
 1007 }
 1008 
 1009 SND_PCM_PLUGIN_SYMBOL(a52);