diff mbox

Initial implementation of a mpeg1 layer2 streaming audio driver.

Message ID 1EDFDC6F-1836-4870-A5B3-47ED41060224@free.fr
State New
Headers show

Commit Message

François Revol Nov. 7, 2010, 5:08 p.m. UTC
Initial implementation of a mpeg1 layer2 streaming audio driver.
It is based on the twolame library <http://www.twolame.org/>.
It allows one to listen to the audio produced by a VM from an mp3 http streaming client.
I just noticed esdaudio.c which I used as template on was under BSD licence, which is fine by me for this one as well.
For now it almost works with a Haiku guest (with HDA at 22050Hz and the WAKEEN patch I just sent), except with a 1min delay and missing frames, so it's possible buffers get queued up somewhere.


From 759ce26b14b7c9c5a24fba43b01cfb5d335086be Mon Sep 17 00:00:00 2001

Initial implementation of a mpeg1 layer2 streaming audio driver.
It is based on the twolame library <http://www.twolame.org/>.
Added a check for libtwolame to configure.


Signed-off-by: François Revol <revol@free.fr>
---
 Makefile.objs        |    1 +
 audio/audio.c        |    3 +
 audio/audio_int.h    |    1 +
 audio/twolameaudio.c |  393 ++++++++++++++++++++++++++++++++++++++++++++++++++
 configure            |   20 +++
 5 files changed, 418 insertions(+), 0 deletions(-)
 create mode 100644 audio/twolameaudio.c

Comments

malc Nov. 7, 2010, 6:09 p.m. UTC | #1
On Sun, 7 Nov 2010, Fran?ois Revol wrote:

Please CC audio related stuff to audio maintainer.

> Initial implementation of a mpeg1 layer2 streaming audio driver.
> It is based on the twolame library <http://www.twolame.org/>.
> It allows one to listen to the audio produced by a VM from an mp3 http streaming client.
> I just noticed esdaudio.c which I used as template on was under BSD licence, which is fine by me for this one as well.
> For now it almost works with a Haiku guest (with HDA at 22050Hz and the WAKEEN patch I just sent), except with a 1min delay and missing frames, so it's possible buffers get queued up somewhere.
> 
> 
> From 759ce26b14b7c9c5a24fba43b01cfb5d335086be Mon Sep 17 00:00:00 2001
> 
> Initial implementation of a mpeg1 layer2 streaming audio driver.
> It is based on the twolame library <http://www.twolame.org/>.
> Added a check for libtwolame to configure.
> 
> 
> Signed-off-by: Fran?ois Revol <revol@free.fr>
> ---
>  Makefile.objs        |    1 +
>  audio/audio.c        |    3 +
>  audio/audio_int.h    |    1 +
>  audio/twolameaudio.c |  393 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  configure            |   20 +++
>  5 files changed, 418 insertions(+), 0 deletions(-)
>  create mode 100644 audio/twolameaudio.c
> 
> diff --git a/Makefile.objs b/Makefile.objs
> index faf485e..370d59a 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -109,6 +109,7 @@ audio-obj-$(CONFIG_FMOD) += fmodaudio.o
>  audio-obj-$(CONFIG_ESD) += esdaudio.o
>  audio-obj-$(CONFIG_PA) += paaudio.o
>  audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
> +audio-obj-$(CONFIG_TWOLAME) += twolameaudio.o
>  audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
>  audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
>  audio-obj-y += wavcapture.o
> diff --git a/audio/audio.c b/audio/audio.c
> index ad51077..0c2c304 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -46,6 +46,9 @@
>  static struct audio_driver *drvtab[] = {
>      CONFIG_AUDIO_DRIVERS
>      &no_audio_driver,
> +#ifdef CONFIG_TWOLAME
> +    &twolame_audio_driver,
> +#endif
>      &wav_audio_driver
>  };
>  
> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index d8560b6..337188b 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -210,6 +210,7 @@ extern struct audio_driver dsound_audio_driver;
>  extern struct audio_driver esd_audio_driver;
>  extern struct audio_driver pa_audio_driver;
>  extern struct audio_driver winwave_audio_driver;
> +extern struct audio_driver twolame_audio_driver;
>  extern struct mixeng_volume nominal_volume;
>  
>  void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
> diff --git a/audio/twolameaudio.c b/audio/twolameaudio.c
> new file mode 100644
> index 0000000..e121a91
> --- /dev/null
> +++ b/audio/twolameaudio.c
> @@ -0,0 +1,393 @@
> +/*
> + * QEMU twolame streaming audio driver
> + *
> + * Copyright (c) 2010 Fran?ois Revol <revol@free.fr>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "config-host.h"
> +#include "qemu-common.h"
> +#include "qemu-char.h"
> +#include "qemu_socket.h"
> +#include "audio.h"
> +
> +#define AUDIO_CAP "twolame"
> +#include "audio_int.h"
> +#include "audio_pt_int.h"
> +
> +#include <twolame.h>
> +
> +typedef struct {
> +    HWVoiceOut hw;
> +    int done;
> +    int live;
> +    int decr;
> +    int rpos;
> +    void *pcm_buf;
> +    void *mpg_buf;
> +    int lsock;
> +    int fd;
> +    struct audio_pt pt;
> +    twolame_options *options;
> +} LAMEVoiceOut;
> +
> +static struct {
> +    int samples;
> +    int divisor;
> +    int port;
> +    int rate;
> +} conf = {
> +    .samples = 1024,
> +    .divisor = 2,
> +    .port = 8080,
> +    .rate = 160
> +};
> +
> +static const char http_header[] = "HTTP/1.1 200 OK\r\nServer: QEMU\r\nContent-Type: audio/mpeg\r\n\r\n";

Line is too long.

> +
> +static void GCC_FMT_ATTR (2, 3) qtwolame_logerr (int err, const char *fmt, ...)
> +{
> +    va_list ap;
> +
> +    va_start (ap, fmt);
> +    AUD_vlog (AUDIO_CAP, fmt, ap);
> +    va_end (ap);
> +
> +    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
> +}
> +
> +static void qtwolame_listen_read(void *opaque)
> +{
> +    LAMEVoiceOut *twolame = opaque;
> +    struct sockaddr_in addr;
> +    socklen_t addrlen = sizeof(addr);
> +
> +    if (twolame->fd > -1)
> +        return;

Style.

> +
> +    int csock = qemu_accept(twolame->lsock, (struct sockaddr *)&addr, &addrlen);

C99 intermixed declartion and initialization is not allowed.

> +    if (csock != -1) {
> +        twolame->fd = csock;
> +        dolog ("Accepted peer\n");
> +        write (twolame->fd, http_header, sizeof(http_header) - 1);

Write returns value which should be checked.

> +    }
> +}
> +
> +/* playback */
> +static void *qtwolame_thread_out (void *arg)
> +{
> +    LAMEVoiceOut *twolame = arg;
> +    HWVoiceOut *hw = &twolame->hw;
> +    int threshold;
> +
> +    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
> +
> +    if (audio_pt_lock (&twolame->pt, AUDIO_FUNC)) {
> +        return NULL;
> +    }
> +
> +    for (;;) {
> +        int decr, to_mix, rpos;
> +
> +        for (;;) {
> +            if (twolame->done) {
> +                goto exit;
> +            }
> +
> +            if (twolame->live > threshold) {
> +                break;
> +            }
> +
> +            if (audio_pt_wait (&twolame->pt, AUDIO_FUNC)) {
> +                goto exit;
> +            }
> +
> +        }
> +
> +        decr = to_mix = twolame->live;
> +        rpos = hw->rpos;
> +
> +        if (audio_pt_unlock (&twolame->pt, AUDIO_FUNC)) {
> +            return NULL;
> +        }
> +
> +        while (to_mix) {
> +            ssize_t converted, written;
> +            int chunk = audio_MIN (to_mix, hw->samples - rpos);
> +            struct st_sample *src = hw->mix_buf + rpos;
> +
> +            hw->clip (twolame->pcm_buf, src, chunk);
> +
> +            if (twolame->fd > -1) {
> +                converted = twolame_encode_buffer_interleaved (twolame->options, twolame->pcm_buf,
> +                    chunk, twolame->mpg_buf, hw->samples << hw->info.shift);
> +                if (converted < 0) {
> +                    qtwolame_logerr (converted, "twolame_encode_buffer_interleaved failed\n");
> +                    return NULL;
> +                }
> +            }
> +
> +        again:
> +            if (twolame->fd > -1) {
> +                written = write (twolame->fd, twolame->mpg_buf, converted);
> +                if (written == -1) {
> +                    if (errno == EPIPE) {
> +                        dolog ("Lost peer\n");
> +                        close (twolame->fd);
> +                        twolame->fd = -1;
> +                        goto again;

This goto is obfuscated.

> +                    }
> +                    if (errno == EINTR || errno == EAGAIN) {
> +                        goto again;
> +                    }
> +                    qtwolame_logerr (errno, "write failed\n");
> +                    return NULL;
> +                }
> +            }
> +
> +            rpos = (rpos + chunk) % hw->samples;
> +            to_mix -= chunk;
> +        }
> +
> +        if (audio_pt_lock (&twolame->pt, AUDIO_FUNC)) {
> +            return NULL;
> +        }
> +
> +        twolame->rpos = rpos;
> +        twolame->live -= decr;
> +        twolame->decr += decr;
> +    }
> +
> + exit:
> +    audio_pt_unlock (&twolame->pt, AUDIO_FUNC);
> +    return NULL;
> +}
> +
> +static int qtwolame_run_out (HWVoiceOut *hw, int live)
> +{
> +    int decr;
> +    LAMEVoiceOut *twolame = (LAMEVoiceOut *) hw;
> +
> +    if (audio_pt_lock (&twolame->pt, AUDIO_FUNC)) {
> +        return 0;
> +    }
> +
> +    decr = audio_MIN (live, twolame->decr);
> +    twolame->decr -= decr;
> +    twolame->live = live - decr;
> +    hw->rpos = twolame->rpos;
> +    if (twolame->live > 0) {
> +        audio_pt_unlock_and_signal (&twolame->pt, AUDIO_FUNC);
> +    }
> +    else {
> +        audio_pt_unlock (&twolame->pt, AUDIO_FUNC);
> +    }
> +    return decr;
> +}
> +
> +static int qtwolame_write (SWVoiceOut *sw, void *buf, int len)
> +{
> +    return audio_pcm_sw_write (sw, buf, len);
> +}
> +
> +static int qtwolame_init_out (HWVoiceOut *hw, struct audsettings *as)
> +{
> +    LAMEVoiceOut *twolame = (LAMEVoiceOut *) hw;
> +    struct audsettings obt_as = *as;
> +
> +    twolame->options = twolame_init();
> +    twolame->fd = -1;
> +
> +    switch (as->fmt) {
> +    case AUD_FMT_S8:
> +    case AUD_FMT_U8:
> +        dolog ("Will use 16 instead of 8 bit samples\n");
> +        goto deffmt;
> +
> +    case AUD_FMT_S32:
> +    case AUD_FMT_U32:
> +        dolog ("Will use 16 instead of 32 bit samples\n");
> +
> +    case AUD_FMT_S16:
> +    case AUD_FMT_U16:
> +    deffmt:
> +        obt_as.fmt = AUD_FMT_S16;
> +        break;
> +
> +    default:
> +        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
> +        goto deffmt;
> +
> +    }
> +    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
> +
> +    audio_pcm_init_info (&hw->info, &obt_as);
> +
> +    twolame_set_mode(twolame->options, (as->nchannels == 2) ? TWOLAME_STEREO : TWOLAME_MONO);
> +    twolame_set_num_channels(twolame->options, as->nchannels);
> +    twolame_set_in_samplerate(twolame->options, as->freq);
> +    twolame_set_out_samplerate(twolame->options, as->freq);
> +    twolame_set_bitrate(twolame->options, 160); //XXX:conf.
> +
> +    if (twolame_init_params(twolame->options)) {
> +        dolog ("Could not set twolame options\n");
> +        return -1;
> +    }

Inconsistent space before opening paren.

> +
> +    hw->samples = conf.samples;
> +    twolame->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
> +    if (!twolame->pcm_buf) {
> +        dolog ("Could not allocate buffer (%d bytes)\n",
> +               hw->samples << hw->info.shift);
> +        return -1;
> +    }
> +
> +    twolame->mpg_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
> +    if (!twolame->mpg_buf) {

pcm_buf is not freed.

> +        dolog ("Could not allocate mpeg buffer (%d bytes)\n",
> +               hw->samples << hw->info.shift);
> +        return -1;
> +    }
> +
> +    char l[256];

Intermixed...

> +    sprintf(l, ":%d", conf.port);
> +    twolame->lsock = inet_listen (l, l, 256, SOCK_STREAM, 0);
> +
> +    qemu_set_fd_handler2(twolame->lsock, NULL, qtwolame_listen_read, NULL, twolame);
> +
> +    if (audio_pt_init (&twolame->pt, qtwolame_thread_out, twolame, AUDIO_CAP, AUDIO_FUNC)) {
> +        goto fail2;
> +    }
> +
> +    return 0;
> +
> + fail2:
> +    if (close (twolame->fd)) {
> +        qtwolame_logerr (errno, "%s: close on socket(%d) failed\n",
> +                     AUDIO_FUNC, twolame->fd);
> +    }
> +    twolame->fd = -1;
> +
> +// fail1:

Do not use C99 style comments.

> +
> +    qemu_free (twolame->mpg_buf);
> +    twolame->mpg_buf = NULL;
> +
> +    qemu_free (twolame->pcm_buf);
> +    twolame->pcm_buf = NULL;
> +    return -1;
> +}
> +
> +static void qtwolame_fini_out (HWVoiceOut *hw)
> +{
> +    void *ret;
> +    LAMEVoiceOut *twolame = (LAMEVoiceOut *) hw;
> +
> +    audio_pt_lock (&twolame->pt, AUDIO_FUNC);
> +    twolame->done = 1;
> +    audio_pt_unlock_and_signal (&twolame->pt, AUDIO_FUNC);
> +    audio_pt_join (&twolame->pt, &ret, AUDIO_FUNC);
> +
> +    if (twolame->fd >= 0) {
> +        if (close (twolame->fd)) {

close result is not checked consistently trhoughout this code

> +            qtwolame_logerr (errno, "failed to close socket\n");
> +        }
> +        twolame->fd = -1;
> +    }
> +
> +    if (twolame->options)
> +        twolame_close(&twolame->options);

Style.

> +    twolame->options = NULL;
> +
> +    audio_pt_fini (&twolame->pt, AUDIO_FUNC);
> +
> +    qemu_free (twolame->pcm_buf);
> +    twolame->pcm_buf = NULL;
> +    qemu_free (twolame->mpg_buf);
> +    twolame->mpg_buf = NULL;
> +}
> +
> +static int qtwolame_ctl_out (HWVoiceOut *hw, int cmd, ...)
> +{
> +    (void) hw;
> +    (void) cmd;
> +    return 0;
> +}
> +
> +/* common */
> +static void *qtwolame_audio_init (void)
> +{
> +    return &conf;
> +}
> +
> +static void qtwolame_audio_fini (void *opaque)
> +{
> +    (void) opaque;
> +    ldebug ("twolame_fini");
> +}
> +
> +struct audio_option qtwolame_options[] = {
> +    {
> +        .name  = "SAMPLES",
> +        .tag   = AUD_OPT_INT,
> +        .valp  = &conf.samples,
> +        .descr = "buffer size in samples"
> +    },
> +    {
> +        .name  = "DIVISOR",
> +        .tag   = AUD_OPT_INT,
> +        .valp  = &conf.divisor,
> +        .descr = "threshold divisor"
> +    },
> +    {
> +        .name  = "PORT",
> +        .tag   = AUD_OPT_INT,
> +        .valp  = &conf.port,
> +        .descr = "streamer port"
> +    },
> +    {
> +        .name  = "RATE",
> +        .tag   = AUD_OPT_INT,
> +        .valp  = &conf.rate,
> +        .descr = "bitrate"
> +    },
> +    { /* End of list */ }
> +};
> +
> +static struct audio_pcm_ops qtwolame_pcm_ops = {
> +    .init_out = qtwolame_init_out,
> +    .fini_out = qtwolame_fini_out,
> +    .run_out  = qtwolame_run_out,
> +    .write    = qtwolame_write,
> +    .ctl_out  = qtwolame_ctl_out,
> +};
> +
> +struct audio_driver twolame_audio_driver = {
> +    .name           = "twolame",
> +    .descr          = "mpeg1 layer2 streamer http://www.twolame.org/",
> +    .options        = qtwolame_options,
> +    .init           = qtwolame_audio_init,
> +    .fini           = qtwolame_audio_fini,
> +    .pcm_ops        = &qtwolame_pcm_ops,
> +    .can_be_default = 0,
> +    .max_voices_out = 1,
> +    .max_voices_in  = 0,
> +    .voice_size_out = sizeof (LAMEVoiceOut),
> +    .voice_size_in  = 0
> +};
> diff --git a/configure b/configure
> index 7025d2b..ca8e980 100755
> --- a/configure
> +++ b/configure
> @@ -285,6 +285,7 @@ vnc_jpeg=""
>  vnc_png=""
>  vnc_thread="no"
>  xen=""
> +twolame=""
>  linux_aio=""
>  attr=""
>  vhost_net=""
> @@ -1155,6 +1156,21 @@ EOF
>  fi
>  
>  ##########################################
> +#
> +
> +cat > $TMPC <<EOF
> +#include <twolame.h>
> +int main(void) { twolame_options *encodeOptions; encodeOptions = twolame_init(); return 0; }
> +EOF
> +if compile_prog "" "-ltwolame" ; then
> +  twolame="yes"
> +  audio_pt_int="yes"
> +  libs_softmmu="-ltwolame $libs_softmmu"
> +else
> +  twolame="no"
> +fi
> +
> +##########################################
>  # pkgconfig probe
>  
>  pkgconfig="${cross_prefix}pkg-config"
> @@ -2314,6 +2330,7 @@ if test -n "$sparc_cpu"; then
>      echo "Target Sparc Arch $sparc_cpu"
>  fi
>  echo "xen support       $xen"
> +echo "twolame streaming $twolame"
>  echo "brlapi support    $brlapi"
>  echo "bluez  support    $bluez"
>  echo "Documentation     $docs"
> @@ -2551,6 +2568,9 @@ fi
>  if test "$xen" = "yes" ; then
>    echo "CONFIG_XEN=y" >> $config_host_mak
>  fi
> +if test "$twolame" = "yes" ; then
> +  echo "CONFIG_TWOLAME=y" >> $config_host_mak
> +fi
>  if test "$io_thread" = "yes" ; then
>    echo "CONFIG_IOTHREAD=y" >> $config_host_mak
>    echo "CONFIG_THREAD=y" >> $config_host_mak
>
François Revol Nov. 7, 2010, 6:29 p.m. UTC | #2
Le 7 nov. 2010 à 19:09, malc a écrit :

> On Sun, 7 Nov 2010, Fran?ois Revol wrote:
> 
> Please CC audio related stuff to audio maintainer.

And that'd be you according to MAINTAINERS ?

>> +static const char http_header[] = "HTTP/1.1 200 OK\r\nServer: QEMU\r\nContent-Type: audio/mpeg\r\n\r\n";
> 
> Line is too long.

Ok I'll break at 80col.

>> +    if (twolame->fd > -1)
>> +        return;
> 
> Style.

That is ?

>> +
>> +    int csock = qemu_accept(twolame->lsock, (struct sockaddr *)&addr, &addrlen);
> 
> C99 intermixed declartion and initialization is not allowed.

This line I copied form ui/vnc.c which does violate C89 too btw...

>> +
>> +        again:
>> +            if (twolame->fd > -1) {
>> +                written = write (twolame->fd, twolame->mpg_buf, converted);
>> +                if (written == -1) {
>> +                    if (errno == EPIPE) {
>> +                        dolog ("Lost peer\n");
>> +                        close (twolame->fd);
>> +                        twolame->fd = -1;
>> +                        goto again;
> 
> This goto is obfuscated.

Not much more than in esdaudio.c



>> +    }
>> +    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
>> +
>> +    audio_pcm_init_info (&hw->info, &obt_as);
>> +
>> +    twolame_set_mode(twolame->options, (as->nchannels == 2) ? TWOLAME_STEREO : TWOLAME_MONO);
>> +    twolame_set_num_channels(twolame->options, as->nchannels);
>> +    twolame_set_in_samplerate(twolame->options, as->freq);
>> +    twolame_set_out_samplerate(twolame->options, as->freq);
>> +    twolame_set_bitrate(twolame->options, 160); //XXX:conf.
>> +
>> +    if (twolame_init_params(twolame->options)) {
>> +        dolog ("Could not set twolame options\n");
>> +        return -1;
>> +    }
> 
> Inconsistent space before opening paren.

Sorry, used to the Haiku style without space before but it seemed to be different around.


>> +    twolame->mpg_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
>> +    if (!twolame->mpg_buf) {
> 
> pcm_buf is not freed.
> 
>> +
>> +// fail1:
> 
> Do not use C99 style comments.

Oh that's leftover from copied error handling, which isn't correct anyway.

François.
malc Nov. 7, 2010, 6:34 p.m. UTC | #3
On Sun, 7 Nov 2010, Fran?ois Revol wrote:

> 
> Le 7 nov. 2010 ? 19:09, malc a ?crit :
> 
> > On Sun, 7 Nov 2010, Fran?ois Revol wrote:
> > 
> > Please CC audio related stuff to audio maintainer.
> 
> And that'd be you according to MAINTAINERS ?
> 
> >> +static const char http_header[] = "HTTP/1.1 200 OK\r\nServer: QEMU\r\nContent-Type: audio/mpeg\r\n\r\n";
> > 
> > Line is too long.
> 
> Ok I'll break at 80col.
> 
> >> +    if (twolame->fd > -1)
> >> +        return;
> > 
> > Style.
> 
> That is ?

Braces around statements.

> >> +
> >> +    int csock = qemu_accept(twolame->lsock, (struct sockaddr *)&addr, &addrlen);
> > 
> > C99 intermixed declartion and initialization is not allowed.
> 
> This line I copied form ui/vnc.c which does violate C89 too btw...

I do not maintain ui/vnc.c.

> 
> >> +
> >> +        again:
> >> +            if (twolame->fd > -1) {
> >> +                written = write (twolame->fd, twolame->mpg_buf, converted);
> >> +                if (written == -1) {
> >> +                    if (errno == EPIPE) {
> >> +                        dolog ("Lost peer\n");
> >> +                        close (twolame->fd);
> >> +                        twolame->fd = -1;
> >> +                        goto again;
> > 
> > This goto is obfuscated.
> 
> Not much more than in esdaudio.c
> 

Yes much more, esdaudio doesn't close the descriptor before jumping.

> 
> >> +    }
> >> +    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
> >> +
> >> +    audio_pcm_init_info (&hw->info, &obt_as);
> >> +
> >> +    twolame_set_mode(twolame->options, (as->nchannels == 2) ? TWOLAME_STEREO : TWOLAME_MONO);
> >> +    twolame_set_num_channels(twolame->options, as->nchannels);
> >> +    twolame_set_in_samplerate(twolame->options, as->freq);
> >> +    twolame_set_out_samplerate(twolame->options, as->freq);
> >> +    twolame_set_bitrate(twolame->options, 160); //XXX:conf.
> >> +
> >> +    if (twolame_init_params(twolame->options)) {
> >> +        dolog ("Could not set twolame options\n");
> >> +        return -1;
> >> +    }
> > 
> > Inconsistent space before opening paren.
> 
> Sorry, used to the Haiku style without space before but it seemed to be 
> different around.
> 
> 
> >> +    twolame->mpg_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
> >> +    if (!twolame->mpg_buf) {
> > 
> > pcm_buf is not freed.
> > 
> >> +
> >> +// fail1:
> > 
> > Do not use C99 style comments.
> 
> Oh that's leftover from copied error handling, which isn't correct anyway.
> 
> Fran?ois.
>
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index faf485e..370d59a 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -109,6 +109,7 @@  audio-obj-$(CONFIG_FMOD) += fmodaudio.o
 audio-obj-$(CONFIG_ESD) += esdaudio.o
 audio-obj-$(CONFIG_PA) += paaudio.o
 audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
+audio-obj-$(CONFIG_TWOLAME) += twolameaudio.o
 audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
 audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
 audio-obj-y += wavcapture.o
diff --git a/audio/audio.c b/audio/audio.c
index ad51077..0c2c304 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -46,6 +46,9 @@ 
 static struct audio_driver *drvtab[] = {
     CONFIG_AUDIO_DRIVERS
     &no_audio_driver,
+#ifdef CONFIG_TWOLAME
+    &twolame_audio_driver,
+#endif
     &wav_audio_driver
 };
 
diff --git a/audio/audio_int.h b/audio/audio_int.h
index d8560b6..337188b 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -210,6 +210,7 @@  extern struct audio_driver dsound_audio_driver;
 extern struct audio_driver esd_audio_driver;
 extern struct audio_driver pa_audio_driver;
 extern struct audio_driver winwave_audio_driver;
+extern struct audio_driver twolame_audio_driver;
 extern struct mixeng_volume nominal_volume;
 
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
diff --git a/audio/twolameaudio.c b/audio/twolameaudio.c
new file mode 100644
index 0000000..e121a91
--- /dev/null
+++ b/audio/twolameaudio.c
@@ -0,0 +1,393 @@ 
+/*
+ * QEMU twolame streaming audio driver
+ *
+ * Copyright (c) 2010 François Revol <revol@free.fr>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "config-host.h"
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "qemu_socket.h"
+#include "audio.h"
+
+#define AUDIO_CAP "twolame"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+#include <twolame.h>
+
+typedef struct {
+    HWVoiceOut hw;
+    int done;
+    int live;
+    int decr;
+    int rpos;
+    void *pcm_buf;
+    void *mpg_buf;
+    int lsock;
+    int fd;
+    struct audio_pt pt;
+    twolame_options *options;
+} LAMEVoiceOut;
+
+static struct {
+    int samples;
+    int divisor;
+    int port;
+    int rate;
+} conf = {
+    .samples = 1024,
+    .divisor = 2,
+    .port = 8080,
+    .rate = 160
+};
+
+static const char http_header[] = "HTTP/1.1 200 OK\r\nServer: QEMU\r\nContent-Type: audio/mpeg\r\n\r\n";
+
+static void GCC_FMT_ATTR (2, 3) qtwolame_logerr (int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void qtwolame_listen_read(void *opaque)
+{
+    LAMEVoiceOut *twolame = opaque;
+    struct sockaddr_in addr;
+    socklen_t addrlen = sizeof(addr);
+
+    if (twolame->fd > -1)
+        return;
+
+    int csock = qemu_accept(twolame->lsock, (struct sockaddr *)&addr, &addrlen);
+    if (csock != -1) {
+        twolame->fd = csock;
+        dolog ("Accepted peer\n");
+        write (twolame->fd, http_header, sizeof(http_header) - 1);
+    }
+}
+
+/* playback */
+static void *qtwolame_thread_out (void *arg)
+{
+    LAMEVoiceOut *twolame = arg;
+    HWVoiceOut *hw = &twolame->hw;
+    int threshold;
+
+    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+    if (audio_pt_lock (&twolame->pt, AUDIO_FUNC)) {
+        return NULL;
+    }
+
+    for (;;) {
+        int decr, to_mix, rpos;
+
+        for (;;) {
+            if (twolame->done) {
+                goto exit;
+            }
+
+            if (twolame->live > threshold) {
+                break;
+            }
+
+            if (audio_pt_wait (&twolame->pt, AUDIO_FUNC)) {
+                goto exit;
+            }
+
+        }
+
+        decr = to_mix = twolame->live;
+        rpos = hw->rpos;
+
+        if (audio_pt_unlock (&twolame->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        while (to_mix) {
+            ssize_t converted, written;
+            int chunk = audio_MIN (to_mix, hw->samples - rpos);
+            struct st_sample *src = hw->mix_buf + rpos;
+
+            hw->clip (twolame->pcm_buf, src, chunk);
+
+            if (twolame->fd > -1) {
+                converted = twolame_encode_buffer_interleaved (twolame->options, twolame->pcm_buf,
+                    chunk, twolame->mpg_buf, hw->samples << hw->info.shift);
+                if (converted < 0) {
+                    qtwolame_logerr (converted, "twolame_encode_buffer_interleaved failed\n");
+                    return NULL;
+                }
+            }
+
+        again:
+            if (twolame->fd > -1) {
+                written = write (twolame->fd, twolame->mpg_buf, converted);
+                if (written == -1) {
+                    if (errno == EPIPE) {
+                        dolog ("Lost peer\n");
+                        close (twolame->fd);
+                        twolame->fd = -1;
+                        goto again;
+                    }
+                    if (errno == EINTR || errno == EAGAIN) {
+                        goto again;
+                    }
+                    qtwolame_logerr (errno, "write failed\n");
+                    return NULL;
+                }
+            }
+
+            rpos = (rpos + chunk) % hw->samples;
+            to_mix -= chunk;
+        }
+
+        if (audio_pt_lock (&twolame->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        twolame->rpos = rpos;
+        twolame->live -= decr;
+        twolame->decr += decr;
+    }
+
+ exit:
+    audio_pt_unlock (&twolame->pt, AUDIO_FUNC);
+    return NULL;
+}
+
+static int qtwolame_run_out (HWVoiceOut *hw, int live)
+{
+    int decr;
+    LAMEVoiceOut *twolame = (LAMEVoiceOut *) hw;
+
+    if (audio_pt_lock (&twolame->pt, AUDIO_FUNC)) {
+        return 0;
+    }
+
+    decr = audio_MIN (live, twolame->decr);
+    twolame->decr -= decr;
+    twolame->live = live - decr;
+    hw->rpos = twolame->rpos;
+    if (twolame->live > 0) {
+        audio_pt_unlock_and_signal (&twolame->pt, AUDIO_FUNC);
+    }
+    else {
+        audio_pt_unlock (&twolame->pt, AUDIO_FUNC);
+    }
+    return decr;
+}
+
+static int qtwolame_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int qtwolame_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+    LAMEVoiceOut *twolame = (LAMEVoiceOut *) hw;
+    struct audsettings obt_as = *as;
+
+    twolame->options = twolame_init();
+    twolame->fd = -1;
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        dolog ("Will use 16 instead of 8 bit samples\n");
+        goto deffmt;
+
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        dolog ("Will use 16 instead of 32 bit samples\n");
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+    deffmt:
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
+        goto deffmt;
+
+    }
+    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+
+    twolame_set_mode(twolame->options, (as->nchannels == 2) ? TWOLAME_STEREO : TWOLAME_MONO);
+    twolame_set_num_channels(twolame->options, as->nchannels);
+    twolame_set_in_samplerate(twolame->options, as->freq);
+    twolame_set_out_samplerate(twolame->options, as->freq);
+    twolame_set_bitrate(twolame->options, 160); //XXX:conf.
+
+    if (twolame_init_params(twolame->options)) {
+        dolog ("Could not set twolame options\n");
+        return -1;
+    }
+
+    hw->samples = conf.samples;
+    twolame->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!twolame->pcm_buf) {
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        return -1;
+    }
+
+    twolame->mpg_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!twolame->mpg_buf) {
+        dolog ("Could not allocate mpeg buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        return -1;
+    }
+
+    char l[256];
+    sprintf(l, ":%d", conf.port);
+    twolame->lsock = inet_listen (l, l, 256, SOCK_STREAM, 0);
+
+    qemu_set_fd_handler2(twolame->lsock, NULL, qtwolame_listen_read, NULL, twolame);
+
+    if (audio_pt_init (&twolame->pt, qtwolame_thread_out, twolame, AUDIO_CAP, AUDIO_FUNC)) {
+        goto fail2;
+    }
+
+    return 0;
+
+ fail2:
+    if (close (twolame->fd)) {
+        qtwolame_logerr (errno, "%s: close on socket(%d) failed\n",
+                     AUDIO_FUNC, twolame->fd);
+    }
+    twolame->fd = -1;
+
+// fail1:
+
+    qemu_free (twolame->mpg_buf);
+    twolame->mpg_buf = NULL;
+
+    qemu_free (twolame->pcm_buf);
+    twolame->pcm_buf = NULL;
+    return -1;
+}
+
+static void qtwolame_fini_out (HWVoiceOut *hw)
+{
+    void *ret;
+    LAMEVoiceOut *twolame = (LAMEVoiceOut *) hw;
+
+    audio_pt_lock (&twolame->pt, AUDIO_FUNC);
+    twolame->done = 1;
+    audio_pt_unlock_and_signal (&twolame->pt, AUDIO_FUNC);
+    audio_pt_join (&twolame->pt, &ret, AUDIO_FUNC);
+
+    if (twolame->fd >= 0) {
+        if (close (twolame->fd)) {
+            qtwolame_logerr (errno, "failed to close socket\n");
+        }
+        twolame->fd = -1;
+    }
+
+    if (twolame->options)
+        twolame_close(&twolame->options);
+    twolame->options = NULL;
+
+    audio_pt_fini (&twolame->pt, AUDIO_FUNC);
+
+    qemu_free (twolame->pcm_buf);
+    twolame->pcm_buf = NULL;
+    qemu_free (twolame->mpg_buf);
+    twolame->mpg_buf = NULL;
+}
+
+static int qtwolame_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+/* common */
+static void *qtwolame_audio_init (void)
+{
+    return &conf;
+}
+
+static void qtwolame_audio_fini (void *opaque)
+{
+    (void) opaque;
+    ldebug ("twolame_fini");
+}
+
+struct audio_option qtwolame_options[] = {
+    {
+        .name  = "SAMPLES",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.samples,
+        .descr = "buffer size in samples"
+    },
+    {
+        .name  = "DIVISOR",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.divisor,
+        .descr = "threshold divisor"
+    },
+    {
+        .name  = "PORT",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.port,
+        .descr = "streamer port"
+    },
+    {
+        .name  = "RATE",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.rate,
+        .descr = "bitrate"
+    },
+    { /* End of list */ }
+};
+
+static struct audio_pcm_ops qtwolame_pcm_ops = {
+    .init_out = qtwolame_init_out,
+    .fini_out = qtwolame_fini_out,
+    .run_out  = qtwolame_run_out,
+    .write    = qtwolame_write,
+    .ctl_out  = qtwolame_ctl_out,
+};
+
+struct audio_driver twolame_audio_driver = {
+    .name           = "twolame",
+    .descr          = "mpeg1 layer2 streamer http://www.twolame.org/",
+    .options        = qtwolame_options,
+    .init           = qtwolame_audio_init,
+    .fini           = qtwolame_audio_fini,
+    .pcm_ops        = &qtwolame_pcm_ops,
+    .can_be_default = 0,
+    .max_voices_out = 1,
+    .max_voices_in  = 0,
+    .voice_size_out = sizeof (LAMEVoiceOut),
+    .voice_size_in  = 0
+};
diff --git a/configure b/configure
index 7025d2b..ca8e980 100755
--- a/configure
+++ b/configure
@@ -285,6 +285,7 @@  vnc_jpeg=""
 vnc_png=""
 vnc_thread="no"
 xen=""
+twolame=""
 linux_aio=""
 attr=""
 vhost_net=""
@@ -1155,6 +1156,21 @@  EOF
 fi
 
 ##########################################
+#
+
+cat > $TMPC <<EOF
+#include <twolame.h>
+int main(void) { twolame_options *encodeOptions; encodeOptions = twolame_init(); return 0; }
+EOF
+if compile_prog "" "-ltwolame" ; then
+  twolame="yes"
+  audio_pt_int="yes"
+  libs_softmmu="-ltwolame $libs_softmmu"
+else
+  twolame="no"
+fi
+
+##########################################
 # pkgconfig probe
 
 pkgconfig="${cross_prefix}pkg-config"
@@ -2314,6 +2330,7 @@  if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
 fi
 echo "xen support       $xen"
+echo "twolame streaming $twolame"
 echo "brlapi support    $brlapi"
 echo "bluez  support    $bluez"
 echo "Documentation     $docs"
@@ -2551,6 +2568,9 @@  fi
 if test "$xen" = "yes" ; then
   echo "CONFIG_XEN=y" >> $config_host_mak
 fi
+if test "$twolame" = "yes" ; then
+  echo "CONFIG_TWOLAME=y" >> $config_host_mak
+fi
 if test "$io_thread" = "yes" ; then
   echo "CONFIG_IOTHREAD=y" >> $config_host_mak
   echo "CONFIG_THREAD=y" >> $config_host_mak