[Asterisk-Dev] Explicit endianness support.
David Woodhouse
dwmw2 at infradead.org
Fri Mar 25 04:18:47 MST 2005
In a few places we make assumptions that AST_FORMAT_SLINEAR is
native-endian. In fact it varies -- the audio/L16 MIME type is
big-endian, while most I/O is little-endian. I 'fixed' the OSS and ALSA
drivers by making them use big-endian mode on big-endian machines, but
that's not really a proper answer in general; some hardware just can't
do that.
One option is a quick hack to _every_ driver which deals with
AST_FORMAT_SLINEAR, to make each one byte-swap to native-endian. But
that seems a little silly in the relatively common case where frames are
both arriving and departing in wrong-endian form.
Another way to do it is probably to recognise both forms explicitly as
AST_FORMAT_SLINEAR_LE and AST_FORMAT_SLINEAR_BE, and be able to convert
between them as appropriate. This patch does that, and preserves the
original "native" AST_FORMAT_SLINEAR by aliasing it to one or the other
as appropriate for the host.
However, having fixed dsp.c as an afterthought to work with
reverse-endian input, I'm less convinced that the latter approach is
ideal. ast_dsp_process() will now be making its _own_ native-endian copy
of the data from an incoming LE channel on a BE machine. It might be
better to just swap the frame to native-endian and _leave_ it like that
until/unless someone else wants it swapped back again.
So I wonder if we should handle byte order with different format types
as this patch does, but do the format conversion with a special-case
converter instead of a normal codec. So the DSP code would call a new
function ast_slinear_check_endian(fr, AST_FORMAT_SLINEAR) to ensure that
the frame is swapped to native endianness, and we'd need to call that
function in other places where we need to be in a specific format, such
as before feeding to channels which require one or the other.
Are we allowed to byteswap the data in-place and just modify fr->subclass?
Index: include/asterisk/frame.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/frame.h,v
retrieving revision 1.44
diff -u -r1.44 frame.h
--- include/asterisk/frame.h 21 Mar 2005 04:30:57 -0000 1.44
+++ include/asterisk/frame.h 25 Mar 2005 11:10:19 -0000
@@ -172,8 +172,8 @@
#define AST_FORMAT_G726 (1 << 4)
/*! ADPCM (IMA) */
#define AST_FORMAT_ADPCM (1 << 5)
-/*! Raw 16-bit Signed Linear (8000 Hz) PCM */
-#define AST_FORMAT_SLINEAR (1 << 6)
+/*! Raw 16-bit Signed Linear (8000 Hz) PCM, little-endian */
+#define AST_FORMAT_SLINEAR_LE (1 << 6)
/*! LPC10, 180 samples/frame */
#define AST_FORMAT_LPC10 (1 << 7)
/*! G.729A audio */
@@ -182,6 +182,8 @@
#define AST_FORMAT_SPEEX (1 << 9)
/*! iLBC Free Compression */
#define AST_FORMAT_ILBC (1 << 10)
+/*! Raw 16-bit Signed Linear (8000 Hz) PCM, big-endian */
+#define AST_FORMAT_SLINEAR_BE (1 << 11)
/*! Maximum audio format */
#define AST_FORMAT_MAX_AUDIO (1 << 15)
/*! JPEG Images */
@@ -196,6 +198,15 @@
#define AST_FORMAT_H263_PLUS (1 << 20)
/*! Max one */
#define AST_FORMAT_MAX_VIDEO (1 << 24)
+
+/* A lot of code assumes that AST_FORMAT_SLINEAR is native-endian. */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define AST_FORMAT_SLINEAR AST_FORMAT_SLINEAR_LE /* Native is LE*/
+#define AST_FORMAT_SLINEAR_RE AST_FORMAT_SLINEAR_BE /* Reverse is BE */
+#else
+#define AST_FORMAT_SLINEAR AST_FORMAT_SLINEAR_BE /* Native is BE */
+#define AST_FORMAT_SLINEAR_RE AST_FORMAT_SLINEAR_LE /* Reverse is LE */
+#endif
/* Control frame types */
/*! Other end has hungup */
Index: frame.c
===================================================================
RCS file: /usr/cvsroot/asterisk/frame.c,v
retrieving revision 1.48
diff -u -r1.48 frame.c
--- frame.c 21 Mar 2005 04:30:57 -0000 1.48
+++ frame.c 25 Mar 2005 11:10:20 -0000
@@ -406,12 +406,12 @@
{ 1, AST_FORMAT_ALAW, "alaw", "G.711 A-law" },
{ 1, AST_FORMAT_G726, "g726", "G.726" },
{ 1, AST_FORMAT_ADPCM, "adpcm" , "ADPCM"},
- { 1, AST_FORMAT_SLINEAR, "slin", "16 bit Signed Linear PCM"},
+ { 1, AST_FORMAT_SLINEAR_LE, "slin", "16 bit Signed Linear PCM"},
{ 1, AST_FORMAT_LPC10, "lpc10", "LPC10" },
{ 1, AST_FORMAT_G729A, "g729", "G.729A" },
{ 1, AST_FORMAT_SPEEX, "speex", "SpeeX" },
{ 1, AST_FORMAT_ILBC, "ilbc", "iLBC"},
- { 0, 0, "nothing", "undefined" },
+ { 1, AST_FORMAT_SLINEAR_BE, "slin_be", "16 bit Signed Linear PCM (big-endian)" },
{ 0, 0, "nothing", "undefined" },
{ 0, 0, "nothing", "undefined" },
{ 0, 0, "nothing", "undefined" },
@@ -442,12 +442,16 @@
char* ast_getformatname(int format)
{
int x = 0;
- char *ret = "unknown";
+ char *ret = NULL;
for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) {
if(AST_FORMAT_LIST[x].visible && AST_FORMAT_LIST[x].bits == format) {
ret = AST_FORMAT_LIST[x].name;
break;
}
+ }
+ if (!ret) {
+ ret = "unknown";
+ ast_log(LOG_DEBUG, "Format %d is unknown\n", format);
}
return ret;
}
Index: dsp.c
===================================================================
RCS file: /usr/cvsroot/asterisk/dsp.c,v
retrieving revision 1.38
diff -u -r1.38 dsp.c
--- dsp.c 4 Mar 2005 06:47:23 -0000 1.38
+++ dsp.c 25 Mar 2005 11:10:20 -0000
@@ -1296,6 +1296,11 @@
return __ast_dsp_silence(dsp, s, len, totalsilence);
}
+static inline unsigned short swap16(unsigned short s)
+{
+ return (s<<8) | (s>>8);
+}
+
struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af)
{
int silence;
@@ -1312,6 +1317,10 @@
switch(inf->subclass) { \
case AST_FORMAT_SLINEAR: \
break; \
+ case AST_FORMAT_SLINEAR_RE: \
+ for (x=0;x<len;x++) \
+ odata[x] = swap16(shortdata[x]); \
+ break; \
case AST_FORMAT_ULAW: \
for (x=0;x<len;x++) \
odata[x] = AST_LIN2MU(shortdata[x]); \
@@ -1335,6 +1344,16 @@
case AST_FORMAT_SLINEAR:
shortdata = af->data;
len = af->datalen / 2;
+ break;
+ case AST_FORMAT_SLINEAR_RE:
+ shortdata = alloca(af->datalen * 2);
+ len = af->datalen / 2;
+ if (!shortdata) {
+ ast_log(LOG_WARNING, "Unable to allocate stack space for data: %s\n", strerror(errno));
+ return af;
+ }
+ for (x=0;x<len;x++)
+ shortdata[x] = swap16(odata[x]);
break;
case AST_FORMAT_ULAW:
shortdata = alloca(af->datalen * 2);
Index: codecs/Makefile
===================================================================
RCS file: /usr/cvsroot/asterisk/codecs/Makefile,v
retrieving revision 1.27
diff -u -r1.27 Makefile
--- codecs/Makefile 12 Mar 2005 05:53:32 -0000 1.27
+++ codecs/Makefile 25 Mar 2005 11:10:20 -0000
@@ -37,7 +37,7 @@
CODECS+=$(MODG723) $(MODSPEEX) $(MODILBC) codec_gsm.so codec_lpc10.so \
codec_adpcm.so codec_ulaw.so codec_alaw.so codec_a_mu.so \
- codec_g726.so
+ codec_g726.so codec_slin_swap.so
all: depend $(CODECS)
--- /dev/null 2005-03-24 19:56:56.172477248 +0000
+++ codecs/codec_slin_swap.c 2005-03-25 08:48:40.000000000 +0000
@@ -0,0 +1,282 @@
+/* codec_slin_swap.c - swap endianness of signed linear samples
+ *
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Copyright (c) 2001 Linux Support Services, Inc. All rights reserved.
+ *
+ * Mark Spencer <markster at linux-support.net
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/lock.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
+#include <asterisk/translate.h>
+#include <asterisk/channel.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <endian.h>
+
+#define BUFFER_SIZE 8096 /* size for the translation buffers */
+
+AST_MUTEX_DEFINE_STATIC(localuser_lock);
+static int localusecnt = 0;
+
+static char *tdesc = "Signed Linear Byteswapper";
+
+static inline short swap16(short s)
+{
+ return (s<<8) | (s>>8);
+}
+
+/*
+ * Private workspace for byteswapping signed linear signals.
+ */
+
+struct slin_swap_pvt
+{
+ int format;
+ struct ast_frame f;
+ short outbuf[BUFFER_SIZE];
+ int tail;
+};
+
+
+/*
+ * slin_swap_new
+ * Create a new instance of slin_swap_pvt.
+ *
+ * Results:
+ * Returns a pointer to the new instance.
+ *
+ * Side effects:
+ * None.
+ */
+
+static struct ast_translator_pvt *slin_swap_new (int format)
+{
+ struct slin_swap_pvt *tmp;
+ tmp = malloc (sizeof (struct slin_swap_pvt));
+ if (tmp)
+ {
+ memset(tmp, 0, sizeof(*tmp));
+ tmp->tail = 0;
+ tmp->format = format;
+ localusecnt++;
+ ast_update_use_count ();
+ }
+ return (struct ast_translator_pvt *) tmp;
+}
+
+static struct ast_translator_pvt *slin_swap_new_be (void)
+{
+ return slin_swap_new(AST_FORMAT_SLINEAR_BE);
+}
+
+static struct ast_translator_pvt *slin_swap_new_le (void)
+{
+ return slin_swap_new(AST_FORMAT_SLINEAR_LE);
+}
+
+/*
+ * slin_swap_FrameIn
+ * Fill an input buffer with byteswapped 16-bit signed linear PCM values.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * tmp->tail is number of signal values in the input buffer.
+ */
+
+static int
+slin_swap_framein (struct ast_translator_pvt *pvt, struct ast_frame *f)
+{
+ struct slin_swap_pvt *tmp = (struct slin_swap_pvt *) pvt;
+ int x;
+ short *s;
+
+ if (tmp->tail + f->datalen/2 >= sizeof(tmp->outbuf))
+ {
+ ast_log (LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ s = f->data;
+ for (x=0;x<f->datalen/2;x++)
+ tmp->outbuf[x+tmp->tail] = swap16(s[x]);
+ tmp->tail += f->datalen/2;
+ return 0;
+}
+
+/*
+ * slin_swap_FrameOut
+ * Output byteswapped signed linear samples from internal buffer
+ *
+ * Results:
+ * Converted signals are placed in tmp->f.data, tmp->f.datalen
+ * and tmp->f.samples are calculated.
+ *
+ * Side effects:
+ * None.
+ */
+
+static struct ast_frame *
+slin_swap_frameout (struct ast_translator_pvt *pvt)
+{
+ struct slin_swap_pvt *tmp = (struct slin_swap_pvt *) pvt;
+
+ if (!tmp->tail)
+ return NULL;
+
+ tmp->f.frametype = AST_FRAME_VOICE;
+ tmp->f.subclass = tmp->format;
+ tmp->f.datalen = tmp->tail *2;
+ tmp->f.samples = tmp->tail;
+ tmp->f.mallocd = 0;
+ tmp->f.offset = AST_FRIENDLY_OFFSET;
+ tmp->f.src = __PRETTY_FUNCTION__;
+ tmp->f.data = tmp->outbuf;
+ tmp->tail = 0;
+ return &tmp->f;
+}
+
+/*
+ * slin_swap_Sample
+ */
+
+static short slin_swap_ex[80]; /* Silence */
+
+static struct ast_frame *
+slin_swap_sample_le (void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR_LE;
+ f.datalen = sizeof (slin_swap_ex);
+ f.samples = sizeof(slin_swap_ex) / 2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_swap_ex;
+ return &f;
+}
+
+static struct ast_frame *
+slin_swap_sample_be (void)
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR_BE;
+ f.datalen = sizeof (slin_swap_ex);
+ f.samples = sizeof (slin_swap_ex) / 2;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_swap_ex;
+ return &f;
+}
+
+/*
+ * slin_swap_destroy
+ * Destroys a private workspace.
+ *
+ * Results:
+ * It's gone!
+ *
+ * Side effects:
+ * None.
+ */
+
+static void
+slin_swap_destroy (struct ast_translator_pvt *pvt)
+{
+ free (pvt);
+ localusecnt--;
+ ast_update_use_count ();
+}
+
+/*
+ * The complete translator for big-endian to little-endian
+ */
+
+static struct ast_translator slin_swap_be = {
+ .name = "slinbetole",
+ .srcfmt = AST_FORMAT_SLINEAR_BE,
+ .dstfmt = AST_FORMAT_SLINEAR_LE,
+ .newpvt = slin_swap_new_le,
+ .framein = slin_swap_framein,
+ .frameout = slin_swap_frameout,
+ .destroy = slin_swap_destroy,
+ .sample = slin_swap_sample_be
+};
+
+/*
+ * The complete translator for little-endian to big-endian.
+ */
+
+static struct ast_translator slin_swap_le = {
+ .name = "slinletobe",
+ .srcfmt = AST_FORMAT_SLINEAR_LE,
+ .dstfmt = AST_FORMAT_SLINEAR_BE,
+ .newpvt = slin_swap_new_be,
+ .framein = slin_swap_framein,
+ .frameout = slin_swap_frameout,
+ .destroy = slin_swap_destroy,
+ .sample = slin_swap_sample_le
+};
+
+int
+unload_module (void)
+{
+ int res;
+ ast_mutex_lock (&localuser_lock);
+ res = ast_unregister_translator (&slin_swap_be);
+ if (!res)
+ res = ast_unregister_translator (&slin_swap_le);
+ if (localusecnt)
+ res = -1;
+ ast_mutex_unlock (&localuser_lock);
+ return res;
+}
+
+int
+load_module (void)
+{
+ int res;
+ res = ast_register_translator (&slin_swap_le);
+ if (!res)
+ res = ast_register_translator (&slin_swap_be);
+ else
+ ast_unregister_translator (&slin_swap_le);
+ return res;
+}
+
+/*
+ * Return a description of this module.
+ */
+
+char *
+description (void)
+{
+ return tdesc;
+}
+
+int
+usecount (void)
+{
+ int res;
+ STANDARD_USECOUNT (res);
+ return res;
+}
+
+char *
+key ()
+{
+ return ASTERISK_GPL_KEY;
+}
--
dwmw2
More information about the asterisk-dev
mailing list