From 3c345ec56d4e8c09ffafa07af91eaf9ba56b2fb7 Mon Sep 17 00:00:00 2001
From: Kevin Harwell <kharwell@digium.com>
Date: Tue, 31 Mar 2020 12:52:44 -0500
Subject: [PATCH] channel: write to a stream on multi-frame writes

If a frame handling routine returns a list of frames (vs. a single frame)
those frames are never passed to a tech's write_stream handler even if one is
available. For instance, if a codec translation occurred and that codec
returned multiple frames then those particular frames were always only sent
to the tech's "write" handler. If that tech (pjsip for example) was stream
capable then those frames were essentially ignored. Thus resulting in bad
audio.

This patch makes it so the "write_stream" handler is appropriately called
for all cases, and for all frames if available.

ASTERISK-28795 #close

Change-Id: I868faea0b73a07ed5a32c2b05bb9cf4b586f739d
---
 main/channel.c | 39 ++++++++++++++++-----------------------
 1 file changed, 16 insertions(+), 23 deletions(-)

diff --git a/main/channel.c b/main/channel.c
index 694f2396c5..821d8fd4fb 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -5045,6 +5045,18 @@ static void apply_plc(struct ast_channel *chan, struct ast_frame *frame)
 	adjust_frame_for_plc(chan, frame, datastore);
 }
 
+static int tech_write(struct ast_channel *chan, struct ast_stream *stream,
+			struct ast_stream *default_stream, struct ast_frame *frame)
+{
+	if (ast_channel_tech(chan)->write_stream) {
+		return stream ? ast_channel_tech(chan)->write_stream(
+			chan, ast_stream_get_position(stream), frame) : 0;
+	}
+
+	return ((stream == default_stream) && ast_channel_tech(chan)->write) ?
+		ast_channel_tech(chan)->write(chan, frame) : 0;
+}
+
 int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 {
 	return ast_write_stream(chan, -1, fr);
@@ -5198,17 +5210,7 @@ int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame
 		break;
 	case AST_FRAME_MODEM:
 		CHECK_BLOCKING(chan);
-		if (ast_channel_tech(chan)->write_stream) {
-			if (stream) {
-				res = ast_channel_tech(chan)->write_stream(chan, ast_stream_get_position(stream), fr);
-			} else {
-				res = 0;
-			}
-		} else if ((stream == default_stream) && ast_channel_tech(chan)->write) {
-			res = ast_channel_tech(chan)->write(chan, fr);
-		} else {
-			res = 0;
-		}
+		res = tech_write(chan, stream, default_stream, fr);
 		ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING);
 		break;
 	case AST_FRAME_VOICE:
@@ -5375,7 +5377,8 @@ int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame
 				next = AST_LIST_NEXT(cur, frame_list);
 				AST_LIST_NEXT(cur, frame_list) = NULL;
 				if (!skip) {
-					if ((res = ast_channel_tech(chan)->write(chan, cur)) < 0) {
+					res = tech_write(chan, stream, default_stream, cur);
+					if (res < 0) {
 						ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV);
 						skip = 1;
 					} else if (next) {
@@ -5392,17 +5395,7 @@ int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame
 			/* reset f so the code below doesn't attempt to free it */
 			f = NULL;
 		} else {
-			if (ast_channel_tech(chan)->write_stream) {
-				if (stream) {
-					res = ast_channel_tech(chan)->write_stream(chan, ast_stream_get_position(stream), f);
-				} else {
-					res = 0;
-				}
-			} else if ((stream == default_stream) && ast_channel_tech(chan)->write) {
-				res = ast_channel_tech(chan)->write(chan, f);
-			} else {
-				res = 0;
-			}
+			res = tech_write(chan, stream, default_stream, f);
 		}
 		ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING);
 		break;
-- 
GitLab