From b96ae79baacf4012d524638c34f5eea97be426e5 Mon Sep 17 00:00:00 2001
From: "Kevin P. Fleming" <kpfleming@digium.com>
Date: Sun, 3 Apr 2005 22:57:18 +0000
Subject: [PATCH] handle AST_FORMAT_SLINEAR endianness properly on big-endian
 systems (bug #3865)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5373 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 channels/chan_iax2.c     |  7 +++++--
 channels/chan_phone.c    | 21 ++++++++++++++++-----
 channels/iax2-parser.c   |  9 ++++++++-
 frame.c                  | 17 +++++++++++++++--
 include/asterisk/frame.h | 23 ++++++++++++++++++++++-
 rtp.c                    | 14 ++++++++++++++
 6 files changed, 80 insertions(+), 11 deletions(-)

diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 6135ca4a14..6ea03392df 100755
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -7337,9 +7337,12 @@ retryowner2:
 	f.src = "IAX2";
 	f.mallocd = 0;
 	f.offset = 0;
-	if (f.datalen && (f.frametype == AST_FRAME_VOICE)) 
+	if (f.datalen && (f.frametype == AST_FRAME_VOICE)) {
 		f.samples = get_samples(&f);
-	else
+		/* We need to byteswap incoming slinear samples from network byte order */
+		if (f.subclass == AST_FORMAT_SLINEAR)
+			ast_frame_byteswap_be(&f);
+	} else
 		f.samples = 0;
 	iax_frame_wrap(&fr, &f);
 
diff --git a/channels/chan_phone.c b/channels/chan_phone.c
index 5cee02e6f4..8e5bdb5234 100755
--- a/channels/chan_phone.c
+++ b/channels/chan_phone.c
@@ -544,10 +544,13 @@ static struct ast_frame  *phone_read(struct ast_channel *ast)
 			  : AST_FRAME_VIDEO;
 	p->fr.subclass = p->lastinput;
 	p->fr.offset = AST_FRIENDLY_OFFSET;
+	/* Byteswap from little-endian to native-endian */
+	if (p->fr.subclass == AST_FORMAT_SLINEAR)
+		ast_frame_byteswap_le(&p->fr);
 	return &p->fr;
 }
 
-static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int frlen)
+static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int frlen, int swap)
 {
 	int res;
 	/* Store as much of the buffer as we can, then write fixed frames */
@@ -555,7 +558,10 @@ static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int fr
 	/* Make sure we have enough buffer space to store the frame */
 	if (space < len)
 		len = space;
-	memcpy(p->obuf + p->obuflen, buf, len);
+	if (swap)
+		ast_memcpy_byteswap(p->obuf+p->obuflen, buf, len/2);
+	else
+		memcpy(p->obuf + p->obuflen, buf, len);
 	p->obuflen += len;
 	while(p->obuflen > frlen) {
 		res = write(p->fd, p->obuf, frlen);
@@ -581,7 +587,7 @@ static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int fr
 static int phone_send_text(struct ast_channel *ast, const char *text)
 {
     int length = strlen(text);
-    return phone_write_buf(ast->tech_pvt, text, length, length) == 
+    return phone_write_buf(ast->tech_pvt, text, length, length, 0) == 
            length ? 0 : -1;
 }
 
@@ -729,12 +735,17 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
 				memset(tmpbuf + 4, 0, sizeof(tmpbuf) - 4);
 				memcpy(tmpbuf, frame->data, 4);
 				expected = 24;
-				res = phone_write_buf(p, tmpbuf, expected, maxfr);
+				res = phone_write_buf(p, tmpbuf, expected, maxfr, 0);
 			}
 			res = 4;
 			expected=4;
 		} else {
-			res = phone_write_buf(p, pos, expected, maxfr);
+			int swap = 0;
+#if __BYTE_ORDER == __BIG_ENDIAN
+			if (frame->subclass == AST_FORMAT_SLINEAR)
+				swap = 1; /* Swap big-endian samples to little-endian as we copy */
+#endif
+			res = phone_write_buf(p, pos, expected, maxfr, swap);
 		}
 		if (res != expected) {
 			if ((errno != EAGAIN) && (errno != EINTR)) {
diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c
index 3472b5008d..f83e7413fa 100755
--- a/channels/iax2-parser.c
+++ b/channels/iax2-parser.c
@@ -855,8 +855,15 @@ void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f)
 	fr->af.delivery.tv_sec = 0;
 	fr->af.delivery.tv_usec = 0;
 	fr->af.data = fr->afdata;
-	if (fr->af.datalen) 
+	if (fr->af.datalen) {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		/* We need to byte-swap slinear samples from network byte order */
+		if (fr->af.subclass == AST_FORMAT_SLINEAR) {
+			ast_memcpy_byteswap(fr->af.data, f->data, fr->af.samples);
+		} else
+#endif
 		memcpy(fr->af.data, f->data, fr->af.datalen);
+	}
 }
 
 struct iax_frame *iax_frame_new(int direction, int datalen)
diff --git a/frame.c b/frame.c
index 673e7b46d9..6ae728e9cd 100755
--- a/frame.c
+++ b/frame.c
@@ -83,7 +83,7 @@ void ast_smoother_set_flags(struct ast_smoother *s, int flags)
 	s->flags = flags;
 }
 
-int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
+int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap)
 {
 	if (f->frametype != AST_FRAME_VOICE) {
 		ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
@@ -129,7 +129,10 @@ int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
 			return 0;
 		}
 	}
-	memcpy(s->data + s->len, f->data, f->datalen);
+	if (swap)
+		ast_swapcopy_samples(s->data+s->len, f->data, f->samples);
+	else
+		memcpy(s->data + s->len, f->data, f->datalen);
 	/* If either side is empty, reset the delivery time */
 	if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) ||
 			(!s->delivery.tv_sec && !s->delivery.tv_usec))
@@ -399,6 +402,16 @@ int ast_fr_fdhangup(int fd)
 	return ast_fr_fdwrite(fd, &hangup);
 }
 
+void ast_swapcopy_samples(void *dst, void *src, int samples)
+{
+	int i;
+	unsigned short *dst_s = dst;
+	unsigned short *src_s = src;
+
+	for (i=0; i<samples; i++)
+		dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8);
+}
+
 static struct ast_format_list AST_FORMAT_LIST[] = {
 	{ 1, AST_FORMAT_G723_1 , "g723" , "G.723.1"},
 	{ 1, AST_FORMAT_GSM, "gsm" , "GSM"},
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 2edf248a3b..88eb6d5c7d 100755
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -301,6 +301,19 @@ int ast_fr_fdwrite(int fd, struct ast_frame *frame);
  */
 int ast_fr_fdhangup(int fd);
 
+void ast_swapcopy_samples(void *dst, void *src, int samples);
+
+/* Helpers for byteswapping native samples to/from 
+   little-endian and big-endian. */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ast_frame_byteswap_le(fr) do { ; } while(0)
+#define ast_frame_byteswap_be(fr) do { struct ast_frame *__f = (fr); ast_swapcopy_samples(__f->data, __f->data, __f->samples); } while(0)
+#else
+#define ast_frame_byteswap_le(fr) do { struct ast_frame *__f = (fr); ast_swapcopy_samples(__f->data, __f->data, __f->samples); } while(0)
+#define ast_frame_byteswap_be(fr) do { ; } while(0)
+#endif
+
+
 /*! Get the name of a format */
 /*!
  * \param format id of format
@@ -347,8 +360,16 @@ extern void ast_smoother_set_flags(struct ast_smoother *smoother, int flags);
 extern int ast_smoother_get_flags(struct ast_smoother *smoother);
 extern void ast_smoother_free(struct ast_smoother *s);
 extern void ast_smoother_reset(struct ast_smoother *s, int bytes);
-extern int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f);
+extern int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap);
 extern struct ast_frame *ast_smoother_read(struct ast_smoother *s);
+#define ast_smoother_feed(s,f) do { __ast_smoother_feed(s, f, 0); } while(0)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ast_smoother_feed_be(s,f) do { __ast_smoother_feed(s, f, 1); } while(0)
+#define ast_smoother_feed_le(s,f) do { __ast_smoother_feed(s, f, 0); } while(0)
+#else
+#define ast_smoother_feed_be(s,f) do { __ast_smoother_feed(s, f, 0); } while(0)
+#define ast_smoother_feed_le(s,f) do { __ast_smoother_feed(s, f, 1); } while(0)
+#endif
 
 extern void ast_frame_dump(char *name, struct ast_frame *f, char *prefix);
 
diff --git a/rtp.c b/rtp.c
index cfcfc1c3db..22840eb3c3 100755
--- a/rtp.c
+++ b/rtp.c
@@ -596,6 +596,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 			break;
 		case AST_FORMAT_SLINEAR:
 			rtp->f.samples = rtp->f.datalen / 2;
+			ast_frame_byteswap_be(&rtp->f);
 			break;
 		case AST_FORMAT_GSM:
 			rtp->f.samples = 160 * (rtp->f.datalen / 33);
@@ -1320,6 +1321,19 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
 
 
 	switch(subclass) {
+	case AST_FORMAT_SLINEAR:
+		if (!rtp->smoother) {
+			rtp->smoother = ast_smoother_new(320);
+		}
+		if (!rtp->smoother) {
+			ast_log(LOG_WARNING, "Unable to create smoother :(\n");
+			return -1;
+		}
+		ast_smoother_feed_be(rtp->smoother, _f);
+		
+		while((f = ast_smoother_read(rtp->smoother)))
+			ast_rtp_raw_write(rtp, f, codec);
+		break;
 	case AST_FORMAT_ULAW:
 	case AST_FORMAT_ALAW:
 		if (!rtp->smoother) {
-- 
GitLab