[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