[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [Try2][PATCH] Initial implementation of a mpeg1 layer2 stre
From: |
François Revol |
Subject: |
[Qemu-devel] [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver. |
Date: |
Mon, 8 Nov 2010 00:08:20 +0100 |
Initial implementation of a mpeg1 layer2 streaming audio driver.
2nd try: Style & other fixes from malc's comments.
It is based on the twolame library <http://www.twolame.org/>.
Since twolame is very similar to lame (on purpose), one might easily create a
lame version from it for better quality.
It allows one to listen to the audio produced by a VM from an mp3 http
streaming client (layer2 is compatible).
I 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 20s delay and missing frames, so it's
possible buffers get queued up somewhere.
From ee55900f8ceb86a96878a60086e8a4da19c645a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= <address@hidden>
Date: Mon, 8 Nov 2010 00:01:43 +0100
Subject: [PATCH] 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.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: François Revol <address@hidden>
---
Makefile.objs | 1 +
audio/audio.c | 3 +
audio/audio_int.h | 1 +
audio/twolameaudio.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 20 +++
5 files changed, 442 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..4372fc4
--- /dev/null
+++ b/audio/twolameaudio.c
@@ -0,0 +1,417 @@
+/*
+ * QEMU twolame streaming audio driver
+ *
+ * Copyright (c) 2010 François Revol <address@hidden>
+ *
+ * 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 sock;
+ 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\n"
+ "Content-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);
+ int csock;
+
+ if (twolame->sock > -1) {
+ return;
+ }
+
+ csock = qemu_accept(twolame->lsock, (struct sockaddr *)&addr, &addrlen);
+ if (csock != -1) {
+ twolame->sock = csock;
+ dolog ("Accepted peer\n");
+ if (write (twolame->sock, http_header, sizeof(http_header) - 1) <
sizeof(http_header) - 1) {
+ qtwolame_logerr (errno, "write failed for http headers\n");
+ /* sending headers failed, just close the connection */
+ closesocket (twolame->sock);
+ twolame->sock = -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->sock > -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 failed\n");
+ return NULL;
+ }
+ }
+
+ again:
+ if (twolame->sock > -1) {
+ written = write (twolame->sock, twolame->mpg_buf, converted);
+ if (written == -1) {
+ if (errno == EPIPE) {
+ dolog ("Lost peer\n");
+ closesocket (twolame->sock);
+ twolame->sock = -1;
+ } else if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ } else {
+ 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;
+ char listen_port[256];
+
+ twolame->sock = -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->options = twolame_init();
+ if (twolame->options == NULL) {
+ dolog ("Could not initialize twolame\n");
+ return -1;
+ }
+ 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, conf.rate);
+
+ if (twolame_init_params(twolame->options)) {
+ dolog ("Could not set twolame options\n");
+ goto fail1;
+ }
+
+ 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);
+ goto fail1;
+ }
+
+ 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);
+ goto fail2;
+ }
+
+ sprintf(listen_port, ":%d", conf.port);
+ twolame->lsock = inet_listen (listen_port, listen_port, 256, SOCK_STREAM,
0);
+ if (twolame->lsock == -1) {
+ dolog ("Could not listen on port %d\n", conf.port);
+ goto fail3;
+ }
+
+ 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 fail4;
+ }
+
+ return 0;
+
+ fail4:
+ qemu_set_fd_handler2(twolame->lsock, NULL, NULL, NULL, NULL);
+ if (closesocket (twolame->lsock)) {
+ qtwolame_logerr (socket_error(), "%s: close on socket(%d) failed\n",
+ AUDIO_FUNC, twolame->sock);
+ }
+ twolame->lsock = -1;
+
+ fail3:
+ qemu_free (twolame->mpg_buf);
+ twolame->mpg_buf = NULL;
+
+ fail2:
+ qemu_free (twolame->pcm_buf);
+ twolame->pcm_buf = NULL;
+
+ fail1:
+ twolame_close (&twolame->options);
+
+ 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->sock >= 0) {
+ if (closesocket (twolame->sock)) {
+ qtwolame_logerr (socket_error(), "failed to close socket\n");
+ }
+ twolame->sock = -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
--
1.7.2.2
- [Qemu-devel] [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver.,
François Revol <=
- Message not available
- [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., François Revol, 2010/11/07
- [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., malc, 2010/11/07
- [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., François Revol, 2010/11/08
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., Anthony Liguori, 2010/11/12
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., François Revol, 2010/11/12
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., Anthony Liguori, 2010/11/15
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., François Revol, 2010/11/15
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., Andreas Färber, 2010/11/16
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., Anthony Liguori, 2010/11/16
- Re: [Qemu-devel] Re: [Try2][PATCH] Initial implementation of a mpeg1 layer2 streaming audio driver., François Revol, 2010/11/16