diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 80476a4e0b1b374753628d1c5ad3bb207f58dc5f..f6e09252f3a1ce8f642381f8539d48771c4bd2f4 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -664,9 +664,33 @@ struct ast_channel_tech {
 	/*! \brief Answer the channel */
 	int (* const answer)(struct ast_channel *chan);
 
-	/*! \brief Read a frame, in standard format (see frame.h) */
+	/*!
+	 * \brief Read a frame (or chain of frames from the same stream), in standard format (see frame.h)
+	 *
+	 * \param chan channel to read frames from
+	 *
+	 * \retval non-NULL on success
+	 * \retval NULL on failure
+	 *
+	 * \note Each media frame from this callback will have the stream_num of it changed to the default
+	 *       stream num based on the type of media returned. As a result a multistream capable channel
+	 *       should not implement this callback.
+	 */
 	struct ast_frame * (* const read)(struct ast_channel *chan);
 
+	/*!
+	 * \brief Read a frame (or chain of frames from the same stream), in standard format (see frame.h), with stream num
+	 *
+	 * \param chan channel to read frames from
+	 *
+	 * \retval non-NULL on success
+	 * \retval NULL on failure
+	 *
+	 * \note Each media frame from this callback should contain a stream_num value which is set to the
+	 *       stream that the media frame originated from.
+	 */
+	struct ast_frame * (* const read_stream)(struct ast_channel *chan);
+
 	/*! \brief Write a frame, in standard format (see frame.h) */
 	int (* const write)(struct ast_channel *chan, struct ast_frame *frame);
 
@@ -1926,13 +1950,36 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception);
 
 /*!
  * \brief Reads a frame
+ *
  * \param chan channel to read a frame from
+ *
  * \return Returns a frame, or NULL on error.  If it returns NULL, you
  * best just stop reading frames and assume the channel has been
  * disconnected.
+ *
+ * \note This function will filter frames received from the channel so
+ *       that only frames from the default stream for each media type
+ *       are returned. All other media frames from other streams will
+ *       be absorbed internally and a NULL frame returned instead.
  */
 struct ast_frame *ast_read(struct ast_channel *chan);
 
+/*!
+ * \brief Reads a frame, but does not filter to just the default streams
+ *
+ * \param chan channel to read a frame from
+ *
+ * \return Returns a frame, or NULL on error.  If it returns NULL, you
+ * best just stop reading frames and assume the channel has been
+ * disconnected.
+ *
+ * \note This function will not perform any filtering and will return
+ *       media frames from all streams on the channel. To determine which
+ *       stream a frame originated from the stream_num on it can be
+ *       examined.
+ */
+struct ast_frame *ast_read_stream(struct ast_channel *chan);
+
 /*!
  * \brief Reads a frame, returning AST_FRAME_NULL frame if audio.
  * \param chan channel to read a frame from
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 90f8aa086c2138ba5967af3bccfc6b2d60f8ccdd..c56539af466b98172b1c6d65c5b323c5c32e96c9 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -181,6 +181,8 @@ struct ast_frame {
 	long len;
 	/*! Sequence number */
 	int seqno;
+	/*! Stream number the frame originated from */
+	int stream_num;
 };
 
 /*!
diff --git a/main/channel.c b/main/channel.c
index 183f8936f1205b46da8f47dbdaa650707920517e..e3e9561fe9575fafda305e0ae3e6976bb19fa940 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -3782,11 +3782,12 @@ static inline int calc_monitor_jump(int samples, int sample_rate, int seek_rate)
 	return samples;
 }
 
-static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
+static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int dropnondefault)
 {
 	struct ast_frame *f = NULL;	/* the return value */
 	int prestate;
 	int cause = 0;
+	struct ast_stream *stream = NULL, *default_stream = NULL;
 
 	/* this function is very long so make sure there is only one return
 	 * point at the end (there are only two exceptions to this).
@@ -3943,6 +3944,13 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 			default:
 				break;
 			}
+		} else if (!(ast_channel_tech(chan)->properties & AST_CHAN_TP_MULTISTREAM) && (
+			f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
+			/* Since this channel driver does not support multistream determine the default stream this frame
+			 * originated from and update the frame to include it.
+			 */
+			stream = default_stream = ast_channel_get_default_stream(chan, ast_format_get_type(f->subclass.format));
+			f->stream_num = ast_stream_get_position(stream);
 		}
 	} else {
 		ast_channel_blocker_set(chan, pthread_self());
@@ -3955,15 +3963,43 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 			}
 			/* Clear the exception flag */
 			ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
-		} else if (ast_channel_tech(chan) && ast_channel_tech(chan)->read)
+		} else if (ast_channel_tech(chan) && ast_channel_tech(chan)->read_stream) {
+			f = ast_channel_tech(chan)->read_stream(chan);
+
+			/* This channel driver supports multistream so the stream_num on the frame is valid, the only
+			 * thing different is that we need to find the default stream so we know whether to invoke the
+			 * default stream logic or not (such as transcoding).
+			 */
+			if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
+				stream = ast_stream_topology_get_stream(ast_channel_get_stream_topology(chan), f->stream_num);
+				default_stream = ast_channel_get_default_stream(chan, ast_format_get_type(f->subclass.format));
+			}
+		} else if (ast_channel_tech(chan) && ast_channel_tech(chan)->read) {
 			f = ast_channel_tech(chan)->read(chan);
+
+			/* Since this channel driver does not support multistream determine the default stream this frame
+			 * originated from and update the frame to include it.
+			 */
+			if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
+				stream = default_stream = ast_channel_get_default_stream(chan, ast_format_get_type(f->subclass.format));
+				f->stream_num = ast_stream_get_position(stream);
+			}
+		}
 		else
 			ast_log(LOG_WARNING, "No read routine on channel %s\n", ast_channel_name(chan));
 	}
 
-	/* Perform the framehook read event here. After the frame enters the framehook list
-	 * there is no telling what will happen, <insert mad scientist laugh here>!!! */
-	f = ast_framehook_list_read_event(ast_channel_framehooks(chan), f);
+	if (dropnondefault && stream != default_stream) {
+		/* If the frame originates from a non-default stream and the caller can not handle other streams
+		 * absord the frame and replace it with a null one instead.
+		 */
+		ast_frfree(f);
+		f = &ast_null_frame;
+	} else if (stream == default_stream) {
+		/* Perform the framehook read event here. After the frame enters the framehook list
+		 * there is no telling what will happen, <insert mad scientist laugh here>!!! */
+		f = ast_framehook_list_read_event(ast_channel_framehooks(chan), f);
+	}
 
 	/*
 	 * Reset the recorded file descriptor that triggered this read so that we can
@@ -4162,6 +4198,11 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 			}
 			break;
 		case AST_FRAME_VOICE:
+			/* If media was received from a non-default stream don't perform any actions, let it just go through */
+			if (stream != default_stream) {
+				break;
+			}
+
 			/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
 			 * is reached , because we want to make sure we pass at least one
 			 * voice frame through before starting the next digit, to ensure a gap
@@ -4396,12 +4437,17 @@ done:
 
 struct ast_frame *ast_read(struct ast_channel *chan)
 {
-	return __ast_read(chan, 0);
+	return __ast_read(chan, 0, 1);
+}
+
+struct ast_frame *ast_read_stream(struct ast_channel *chan)
+{
+	return __ast_read(chan, 0, 0);
 }
 
 struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
 {
-	return __ast_read(chan, 1);
+	return __ast_read(chan, 1, 1);
 }
 
 int ast_indicate(struct ast_channel *chan, int condition)
diff --git a/main/frame.c b/main/frame.c
index c284a8e1cd9d122e3669939cf877634779fdb6e0..c24cc8f78695b35315f660f7b9ee6623fccebf27 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -222,6 +222,7 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
 			out->len = fr->len;
 			out->seqno = fr->seqno;
 		}
+		out->stream_num = fr->stream_num;
 	} else {
 		out = fr;
 	}
@@ -370,6 +371,7 @@ struct ast_frame *ast_frdup(const struct ast_frame *f)
 	out->ts = f->ts;
 	out->len = f->len;
 	out->seqno = f->seqno;
+	out->stream_num = f->stream_num;
 	return out;
 }