loader: Correct overly strict startup checks.
[asterisk/asterisk.git] / main / channel.c
index b30bb1d..7eb40d1 100644 (file)
 #include "asterisk/autochan.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/global_datastores.h"
-#include "asterisk/data.h"
 #include "asterisk/channel_internal.h"
 #include "asterisk/features.h"
 #include "asterisk/bridge.h"
 #include "asterisk/test.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/max_forwards.h"
+#include "asterisk/stream.h"
 
 /*** DOCUMENTATION
  ***/
 
-#ifdef HAVE_EPOLL
-#include <sys/epoll.h>
-#endif
-
 #if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
 #if defined(HAVE_PRI)
 #include "libpri.h"
 #endif /* defined(HAVE_PRI) */
 #endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */
 
-struct ast_epoll_data {
-       struct ast_channel *chan;
-       int which;
-};
-
 /* uncomment if you have problems with 'monitoring' synchronized files */
 #if 0
 #define MONITOR_CONSTANT_DELAY
@@ -806,6 +797,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        struct ast_timer *timer;
        struct timeval now;
        const struct ast_channel_tech *channel_tech;
+       struct ast_stream_topology *topology;
 
        /* If shutting down, don't allocate any new channels */
        if (ast_shutting_down()) {
@@ -823,7 +815,12 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        ast_channel_stage_snapshot(tmp);
 
        if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
-               /* format capabilities structure allocation failure */
+               /*
+                * Aborting the channel creation.  We do not need to complete staging
+                * the channel snapshot because the channel has not been finalized or
+                * linked into the channels container yet.  Nobody else knows about
+                * this channel nor will anybody ever know about it.
+                */
                return ast_channel_unref(tmp);
        }
        ast_format_cap_append(nativeformats, ast_format_none, 0);
@@ -843,12 +840,9 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        ast_channel_internal_alertpipe_clear(tmp);
        ast_channel_internal_fd_clear_all(tmp);
 
-#ifdef HAVE_EPOLL
-       ast_channel_epfd_set(tmp, epoll_create(25));
-#endif
-
        if (!(schedctx = ast_sched_context_create())) {
                ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
+               /* See earlier channel creation abort comment above. */
                return ast_channel_unref(tmp);
        }
        ast_channel_sched_set(tmp, schedctx);
@@ -863,6 +857,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
                ast_channel_caller(tmp)->id.name.valid = 1;
                ast_channel_caller(tmp)->id.name.str = ast_strdup(cid_name);
                if (!ast_channel_caller(tmp)->id.name.str) {
+                       /* See earlier channel creation abort comment above. */
                        return ast_channel_unref(tmp);
                }
        }
@@ -870,6 +865,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
                ast_channel_caller(tmp)->id.number.valid = 1;
                ast_channel_caller(tmp)->id.number.str = ast_strdup(cid_num);
                if (!ast_channel_caller(tmp)->id.number.str) {
+                       /* See earlier channel creation abort comment above. */
                        return ast_channel_unref(tmp);
                }
        }
@@ -883,9 +879,15 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        }
 
        if (needqueue && ast_channel_internal_alertpipe_init(tmp)) {
+               /* See earlier channel creation abort comment above. */
                return ast_channel_unref(tmp);
        }
 
+       if (!(topology = ast_stream_topology_alloc())) {
+               return ast_channel_unref(tmp);
+       }
+       ast_channel_internal_set_stream_topology(tmp, topology);
+
        /* Always watch the alertpipe */
        ast_channel_set_fd(tmp, AST_ALERT_FD, ast_channel_internal_alert_readfd(tmp));
        /* And timing pipe */
@@ -974,20 +976,14 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        if (assignedids && (does_id_conflict(assignedids->uniqueid) || does_id_conflict(assignedids->uniqueid2))) {
                ast_channel_internal_errno_set(AST_CHANNEL_ERROR_ID_EXISTS);
                ao2_unlock(channels);
-               /* This is a bit unorthodox, but we can't just call ast_channel_stage_snapshot_done()
-                * because that will result in attempting to publish the channel snapshot. That causes
-                * badness in some places, such as CDRs. So we need to manually clear the flag on the
-                * channel that says that a snapshot is being cleared.
-                */
-               ast_clear_flag(ast_channel_flags(tmp), AST_FLAG_SNAPSHOT_STAGE);
                ast_channel_unlock(tmp);
+               /* See earlier channel creation abort comment above. */
                return ast_channel_unref(tmp);
        }
 
+       /* Finalize and link into the channels container. */
        ast_channel_internal_finalize(tmp);
-
        ast_atomic_fetchadd_int(&chancount, +1);
-
        ao2_link_flags(channels, tmp, OBJ_NOLOCK);
 
        ao2_unlock(channels);
@@ -1001,6 +997,9 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
         * the world know of its existance
         */
        ast_channel_stage_snapshot_done(tmp);
+
+       ast_debug(1, "Channel %p '%s' allocated\n", tmp, ast_channel_name(tmp));
+
        return tmp;
 }
 
@@ -1048,9 +1047,6 @@ struct ast_channel *__ast_dummy_channel_alloc(const char *file, int line, const
        ast_channel_timingfd_set(tmp, -1);
        ast_channel_internal_alertpipe_clear(tmp);
        ast_channel_internal_fd_clear_all(tmp);
-#ifdef HAVE_EPOLL
-       ast_channel_epfd_set(tmp, -1);
-#endif
 
        ast_channel_hold_state_set(tmp, AST_CONTROL_UNHOLD);
 
@@ -1062,25 +1058,6 @@ struct ast_channel *__ast_dummy_channel_alloc(const char *file, int line, const
        return tmp;
 }
 
-void ast_channel_start_defer_frames(struct ast_channel *chan)
-{
-       ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES);
-}
-
-void ast_channel_stop_defer_frames(struct ast_channel *chan)
-{
-       ast_clear_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES);
-
-       /* Move the deferred frames onto the channel read queue, ahead of other queued frames */
-       ast_queue_frame_head(chan, AST_LIST_FIRST(ast_channel_deferred_readq(chan)));
-       /* ast_frfree will mosey down the list and free them all */
-       if (!AST_LIST_EMPTY(ast_channel_deferred_readq(chan))) {
-               ast_frfree(AST_LIST_FIRST(ast_channel_deferred_readq(chan)));
-       }
-       /* Reset the list to be empty */
-       AST_LIST_HEAD_INIT_NOLOCK(ast_channel_deferred_readq(chan));
-}
-
 static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after)
 {
        struct ast_frame *f;
@@ -1167,6 +1144,9 @@ static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, in
                                }
                                AST_LIST_REMOVE_CURRENT(frame_list);
                                ast_frfree(cur);
+
+                               /* Read from the alert pipe for each flushed frame. */
+                               ast_channel_internal_alert_read(chan);
                        }
                }
                AST_LIST_TRAVERSE_SAFE_END;
@@ -1183,9 +1163,13 @@ static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, in
        }
 
        if (ast_channel_alert_writable(chan)) {
-               if (ast_channel_alert_write(chan)) {
-                       ast_log(LOG_WARNING, "Unable to write to alert pipe on %s (qlen = %u): %s!\n",
-                               ast_channel_name(chan), queued_frames, strerror(errno));
+               /* Write to the alert pipe for each added frame */
+               while (new_frames--) {
+                       if (ast_channel_alert_write(chan)) {
+                               ast_log(LOG_WARNING, "Unable to write to alert pipe on %s (qlen = %u): %s!\n",
+                                       ast_channel_name(chan), queued_frames, strerror(errno));
+                               break;
+                       }
                }
        } else if (ast_channel_timingfd(chan) > -1) {
                ast_timer_enable_continuous(ast_channel_timer(chan));
@@ -1306,8 +1290,10 @@ int ast_channel_defer_dtmf(struct ast_channel *chan)
        int pre = 0;
 
        if (chan) {
+               ast_channel_lock(chan);
                pre = ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF);
                ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF);
+               ast_channel_unlock(chan);
        }
        return pre;
 }
@@ -1315,8 +1301,9 @@ int ast_channel_defer_dtmf(struct ast_channel *chan)
 /*! \brief Unset defer DTMF flag on channel */
 void ast_channel_undefer_dtmf(struct ast_channel *chan)
 {
-       if (chan)
-               ast_clear_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF);
+       if (chan) {
+               ast_channel_clear_flag(chan, AST_FLAG_DEFER_DTMF);
+       }
 }
 
 struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg,
@@ -1531,6 +1518,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame)
        case AST_FRAME_IAX:
        case AST_FRAME_CNG:
        case AST_FRAME_MODEM:
+       case AST_FRAME_RTCP:
                return 0;
        }
        return 0;
@@ -2224,9 +2212,6 @@ void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
 static void ast_channel_destructor(void *obj)
 {
        struct ast_channel *chan = obj;
-#ifdef HAVE_EPOLL
-       int i;
-#endif
        struct ast_var_t *vardata;
        struct ast_frame *f;
        struct varshead *headp;
@@ -2234,6 +2219,8 @@ static void ast_channel_destructor(void *obj)
        char device_name[AST_CHANNEL_NAME];
        ast_callid callid;
 
+       ast_debug(1, "Channel %p '%s' destroying\n", chan, ast_channel_name(chan));
+
        /* Stop monitoring */
        if (ast_channel_monitor(chan)) {
                ast_channel_monitor(chan)->stop(chan, 0);
@@ -2324,14 +2311,6 @@ static void ast_channel_destructor(void *obj)
                ast_timer_close(ast_channel_timer(chan));
                ast_channel_timer_set(chan, NULL);
        }
-#ifdef HAVE_EPOLL
-       for (i = 0; i < AST_MAX_FDS; i++) {
-               if (ast_channel_internal_epfd_data(chan, i)) {
-                       ast_free(ast_channel_internal_epfd_data(chan, i));
-               }
-       }
-       close(ast_channel_epfd(chan));
-#endif
        while ((f = AST_LIST_REMOVE_HEAD(ast_channel_readq(chan), frame_list)))
                ast_frfree(f);
 
@@ -2482,82 +2461,10 @@ struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const
 /*! Set the file descriptor on the channel */
 void ast_channel_set_fd(struct ast_channel *chan, int which, int fd)
 {
-#ifdef HAVE_EPOLL
-       struct epoll_event ev;
-       struct ast_epoll_data *aed = NULL;
-
-       if (ast_channel_fd_isset(chan, which)) {
-               epoll_ctl(ast_channel_epfd(chan), EPOLL_CTL_DEL, ast_channel_fd(chan, which), &ev);
-               aed = ast_channel_internal_epfd_data(chan, which);
-       }
-
-       /* If this new fd is valid, add it to the epoll */
-       if (fd > -1) {
-               if (!aed && (!(aed = ast_calloc(1, sizeof(*aed)))))
-                       return;
-
-               ast_channel_internal_epfd_data_set(chan, which, aed);
-               aed->chan = chan;
-               aed->which = which;
-
-               ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
-               ev.data.ptr = aed;
-               epoll_ctl(ast_channel_epfd(chan), EPOLL_CTL_ADD, fd, &ev);
-       } else if (aed) {
-               /* We don't have to keep around this epoll data structure now */
-               ast_free(aed);
-               ast_channel_epfd_data_set(chan, which, NULL);
-       }
-#endif
        ast_channel_internal_fd_set(chan, which, fd);
        return;
 }
 
-/*! Add a channel to an optimized waitfor */
-void ast_poll_channel_add(struct ast_channel *chan0, struct ast_channel *chan1)
-{
-#ifdef HAVE_EPOLL
-       struct epoll_event ev;
-       int i = 0;
-
-       if (ast_channel_epfd(chan0) == -1)
-               return;
-
-       /* Iterate through the file descriptors on chan1, adding them to chan0 */
-       for (i = 0; i < AST_MAX_FDS; i++) {
-               if (!ast_channel_fd_isset(chan1, i)) {
-                       continue;
-               }
-               ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
-               ev.data.ptr = ast_channel_internal_epfd_data(chan1, i);
-               epoll_ctl(ast_channel_epfd(chan0), EPOLL_CTL_ADD, ast_channel_fd(chan1, i), &ev);
-       }
-
-#endif
-       return;
-}
-
-/*! Delete a channel from an optimized waitfor */
-void ast_poll_channel_del(struct ast_channel *chan0, struct ast_channel *chan1)
-{
-#ifdef HAVE_EPOLL
-       struct epoll_event ev;
-       int i = 0;
-
-       if (ast_channel_epfd(chan0) == -1)
-               return;
-
-       for (i = 0; i < AST_MAX_FDS; i++) {
-               if (!ast_channel_fd_isset(chan1, i)) {
-                       continue;
-               }
-               epoll_ctl(ast_channel_epfd(chan0), EPOLL_CTL_DEL, ast_channel_fd(chan1, i), &ev);
-       }
-
-#endif
-       return;
-}
-
 void ast_channel_clear_softhangup(struct ast_channel *chan, int flag)
 {
        ast_channel_lock(chan);
@@ -2676,6 +2583,9 @@ void ast_hangup(struct ast_channel *chan)
                return;
        }
 
+       ast_debug(1, "Channel %p '%s' hanging up.  Refs: %d\n", chan, ast_channel_name(chan),
+               ao2_ref(chan, 0));
+
        ast_autoservice_stop(chan);
 
        ast_channel_lock(chan);
@@ -2735,7 +2645,6 @@ void ast_hangup(struct ast_channel *chan)
                ast_assert(ast_test_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING) == 0);
        }
 
-       ast_debug(1, "Hanging up channel '%s'\n", ast_channel_name(chan));
        if (ast_channel_tech(chan)->hangup) {
                ast_channel_tech(chan)->hangup(chan);
        }
@@ -2887,6 +2796,7 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
                                case AST_FRAME_IMAGE:
                                case AST_FRAME_HTML:
                                case AST_FRAME_MODEM:
+                               case AST_FRAME_RTCP:
                                        done = 1;
                                        break;
                                case AST_FRAME_CONTROL:
@@ -3061,20 +2971,15 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
 }
 
 /*! \brief Wait for x amount of time on a file descriptor to have input.  */
-#ifdef HAVE_EPOLL
-static struct ast_channel *ast_waitfor_nandfds_classic(struct ast_channel **c, int n, int *fds, int nfds,
-                                       int *exception, int *outfd, int *ms)
-#else
 struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
                                        int *exception, int *outfd, int *ms)
-#endif
 {
        struct timeval start = { 0 , 0 };
        struct pollfd *pfds = NULL;
        int res;
        long rms;
        int x, y, max;
-       int sz;
+       int sz = nfds;
        struct timeval now = { 0, 0 };
        struct timeval whentohangup = { 0, 0 }, diff;
        struct ast_channel *winner = NULL;
@@ -3090,14 +2995,6 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
                *exception = 0;
        }
 
-       if ((sz = n * AST_MAX_FDS + nfds)) {
-               pfds = ast_alloca(sizeof(*pfds) * sz);
-               fdmap = ast_alloca(sizeof(*fdmap) * sz);
-       } else {
-               /* nothing to allocate and no FDs to check */
-               return NULL;
-       }
-
        for (x = 0; x < n; x++) {
                ast_channel_lock(c[x]);
                if (!ast_tvzero(*ast_channel_whentohangup(c[x]))) {
@@ -3114,8 +3011,17 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
                        if (ast_tvzero(whentohangup) || ast_tvcmp(diff, whentohangup) < 0)
                                whentohangup = diff;
                }
+               sz += ast_channel_fd_count(c[x]);
                ast_channel_unlock(c[x]);
        }
+
+       if (!sz) {
+               return NULL;
+       }
+
+       pfds = ast_alloca(sizeof(*pfds) * sz);
+       fdmap = ast_alloca(sizeof(*fdmap) * sz);
+
        /* Wait full interval */
        rms = *ms;
        /* INT_MAX, not LONG_MAX, because it matters on 64-bit */
@@ -3135,12 +3041,14 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
         */
        max = 0;
        for (x = 0; x < n; x++) {
-               for (y = 0; y < AST_MAX_FDS; y++) {
+               ast_channel_lock(c[x]);
+               for (y = 0; y < ast_channel_fd_count(c[x]); y++) {
                        fdmap[max].fdno = y;  /* fd y is linked to this pfds */
                        fdmap[max].chan = x;  /* channel x is linked to this pfds */
                        max += ast_add_fd(&pfds[max], ast_channel_fd(c[x], y));
                }
                CHECK_BLOCKING(c[x]);
+               ast_channel_unlock(c[x]);
        }
        /* Add the individual fds */
        for (x = 0; x < nfds; x++) {
@@ -3167,7 +3075,9 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
                res = ast_poll(pfds, max, rms);
        }
        for (x = 0; x < n; x++) {
+               ast_channel_lock(c[x]);
                ast_clear_flag(ast_channel_flags(c[x]), AST_FLAG_BLOCKING);
+               ast_channel_unlock(c[x]);
        }
        if (res < 0) { /* Simulate a timeout if we were interrupted */
                if (errno != EINTR) {
@@ -3203,12 +3113,14 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
                }
                if (fdmap[x].chan >= 0) {       /* this is a channel */
                        winner = c[fdmap[x].chan];      /* override previous winners */
+                       ast_channel_lock(winner);
                        if (res & POLLPRI) {
                                ast_set_flag(ast_channel_flags(winner), AST_FLAG_EXCEPTION);
                        } else {
                                ast_clear_flag(ast_channel_flags(winner), AST_FLAG_EXCEPTION);
                        }
                        ast_channel_fdno_set(winner, fdmap[x].fdno);
+                       ast_channel_unlock(winner);
                } else {                        /* this is an fd */
                        if (outfd) {
                                *outfd = pfds[x].fd;
@@ -3228,205 +3140,6 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
        return winner;
 }
 
-#ifdef HAVE_EPOLL
-static struct ast_channel *ast_waitfor_nandfds_simple(struct ast_channel *chan, int *ms)
-{
-       struct timeval start = { 0 , 0 };
-       int res = 0;
-       struct epoll_event ev[1];
-       long diff, rms = *ms;
-       struct ast_channel *winner = NULL;
-       struct ast_epoll_data *aed = NULL;
-
-       ast_channel_lock(chan);
-       /* Figure out their timeout */
-       if (!ast_tvzero(*ast_channel_whentohangup(chan))) {
-               if ((diff = ast_tvdiff_ms(*ast_channel_whentohangup(chan), ast_tvnow())) < 0) {
-                       /* They should already be hungup! */
-                       ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_TIMEOUT);
-                       ast_channel_unlock(chan);
-                       return NULL;
-               }
-               /* If this value is smaller then the current one... make it priority */
-               if (rms > diff) {
-                       rms = diff;
-               }
-       }
-
-       ast_channel_unlock(chan);
-
-       /* Time to make this channel block... */
-       CHECK_BLOCKING(chan);
-
-       if (*ms > 0) {
-               start = ast_tvnow();
-       }
-
-       /* We don't have to add any file descriptors... they are already added, we just have to wait! */
-       res = epoll_wait(ast_channel_epfd(chan), ev, 1, rms);
-
-       /* Stop blocking */
-       ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING);
-
-       /* Simulate a timeout if we were interrupted */
-       if (res < 0) {
-               if (errno != EINTR) {
-                       *ms = -1;
-               }
-               return NULL;
-       }
-
-       /* If this channel has a timeout see if it expired */
-       if (!ast_tvzero(*ast_channel_whentohangup(chan))) {
-               if (ast_tvdiff_ms(ast_tvnow(), *ast_channel_whentohangup(chan)) >= 0) {
-                       ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_TIMEOUT);
-                       winner = chan;
-               }
-       }
-
-       /* No fd ready, reset timeout and be done for now */
-       if (!res) {
-               *ms = 0;
-               return winner;
-       }
-
-       /* See what events are pending */
-       aed = ev[0].data.ptr;
-       ast_channel_fdno_set(chan, aed->which);
-       if (ev[0].events & EPOLLPRI) {
-               ast_set_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
-       } else {
-               ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
-       }
-
-       if (*ms > 0) {
-               *ms -= ast_tvdiff_ms(ast_tvnow(), start);
-               if (*ms < 0) {
-                       *ms = 0;
-               }
-       }
-
-       return chan;
-}
-
-static struct ast_channel *ast_waitfor_nandfds_complex(struct ast_channel **c, int n, int *ms)
-{
-       struct timeval start = { 0 , 0 };
-       int res = 0, i;
-       struct epoll_event ev[25] = { { 0, } };
-       struct timeval now = { 0, 0 };
-       long whentohangup = 0, diff = 0, rms = *ms;
-       struct ast_channel *winner = NULL;
-
-       for (i = 0; i < n; i++) {
-               ast_channel_lock(c[i]);
-               if (!ast_tvzero(*ast_channel_whentohangup(c[i]))) {
-                       if (whentohangup == 0) {
-                               now = ast_tvnow();
-                       }
-                       if ((diff = ast_tvdiff_ms(*ast_channel_whentohangup(c[i]), now)) < 0) {
-                               ast_channel_softhangup_internal_flag_add(c[i], AST_SOFTHANGUP_TIMEOUT);
-                               ast_channel_unlock(c[i]);
-                               return c[i];
-                       }
-                       if (!whentohangup || whentohangup > diff) {
-                               whentohangup = diff;
-                       }
-               }
-               ast_channel_unlock(c[i]);
-               CHECK_BLOCKING(c[i]);
-       }
-
-       rms = *ms;
-       if (whentohangup) {
-               rms = whentohangup;
-               if (*ms >= 0 && *ms < rms) {
-                       rms = *ms;
-               }
-       }
-
-       if (*ms > 0) {
-               start = ast_tvnow();
-       }
-
-       res = epoll_wait(ast_channel_epfd(c[0]), ev, 25, rms);
-
-       for (i = 0; i < n; i++) {
-               ast_clear_flag(ast_channel_flags(c[i]), AST_FLAG_BLOCKING);
-       }
-
-       if (res < 0) {
-               if (errno != EINTR) {
-                       *ms = -1;
-               }
-               return NULL;
-       }
-
-       if (whentohangup) {
-               now = ast_tvnow();
-               for (i = 0; i < n; i++) {
-                       if (!ast_tvzero(*ast_channel_whentohangup(c[i])) && ast_tvdiff_ms(now, *ast_channel_whentohangup(c[i])) >= 0) {
-                               ast_channel_softhangup_internal_flag_add(c[i], AST_SOFTHANGUP_TIMEOUT);
-                               if (!winner) {
-                                       winner = c[i];
-                               }
-                       }
-               }
-       }
-
-       if (!res) {
-               *ms = 0;
-               return winner;
-       }
-
-       for (i = 0; i < res; i++) {
-               struct ast_epoll_data *aed = ev[i].data.ptr;
-
-               if (!ev[i].events || !aed) {
-                       continue;
-               }
-
-               winner = aed->chan;
-               if (ev[i].events & EPOLLPRI) {
-                       ast_set_flag(ast_channel_flags(winner), AST_FLAG_EXCEPTION);
-               } else {
-                       ast_clear_flag(ast_channel_flags(winner), AST_FLAG_EXCEPTION);
-               }
-               ast_channel_fdno_set(winner, aed->which);
-       }
-
-       if (*ms > 0) {
-               *ms -= ast_tvdiff_ms(ast_tvnow(), start);
-               if (*ms < 0) {
-                       *ms = 0;
-               }
-       }
-
-       return winner;
-}
-
-struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
-                                       int *exception, int *outfd, int *ms)
-{
-       /* Clear all provided values in one place. */
-       if (outfd) {
-               *outfd = -99999;
-       }
-       if (exception) {
-               *exception = 0;
-       }
-
-       /* If no epoll file descriptor is available resort to classic nandfds */
-       if (!n || nfds || ast_channel_epfd(c[0]) == -1) {
-               return ast_waitfor_nandfds_classic(c, n, fds, nfds, exception, outfd, ms);
-       } else if (!nfds && n == 1) {
-               return ast_waitfor_nandfds_simple(c[0], ms);
-       } else {
-               return ast_waitfor_nandfds_complex(c, n, ms);
-       }
-}
-#endif
-
 struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
 {
        return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms);
@@ -3447,7 +3160,7 @@ int ast_waitfor(struct ast_channel *c, int ms)
 
 int ast_waitfordigit(struct ast_channel *c, int ms)
 {
-       return ast_waitfordigit_full(c, ms, -1, -1);
+       return ast_waitfordigit_full(c, ms, NULL, -1, -1);
 }
 
 int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data)
@@ -3509,7 +3222,7 @@ int ast_settimeout_full(struct ast_channel *c, unsigned int rate, int (*func)(co
        return res;
 }
 
-int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, int cmdfd)
+int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, const char *breakon, int audiofd, int cmdfd)
 {
        struct timeval start = ast_tvnow();
        int ms;
@@ -3519,7 +3232,7 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
                return -1;
 
        /* Only look for the end of DTMF, don't bother with the beginning and don't emulate things */
-       ast_set_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+       ast_channel_set_flag(c, AST_FLAG_END_DTMF_ONLY);
 
        /* Wait for a digit, no more than timeout_ms milliseconds total.
         * Or, wait indefinitely if timeout_ms is <0.
@@ -3538,32 +3251,39 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
                        if (errno == 0 || errno == EINTR)
                                continue;
                        ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
-                       ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+                       ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
                        return -1;
                } else if (outfd > -1) {
                        /* The FD we were watching has something waiting */
                        ast_log(LOG_WARNING, "The FD we were waiting for has something waiting. Waitfordigit returning numeric 1\n");
-                       ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+                       ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
                        return 1;
                } else if (rchan) {
                        int res;
                        struct ast_frame *f = ast_read(c);
-                       if (!f)
+
+                       if (!f) {
+                               ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
                                return -1;
+                       }
 
                        switch (f->frametype) {
                        case AST_FRAME_DTMF_BEGIN:
                                break;
                        case AST_FRAME_DTMF_END:
                                res = f->subclass.integer;
-                               ast_frfree(f);
-                               ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
-                               return res;
+                               if (!breakon || strchr(breakon, res)) {
+                                       ast_frfree(f);
+                                       ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+                                       return res;
+                               }
+                               break;
                        case AST_FRAME_CONTROL:
                                switch (f->subclass.integer) {
                                case AST_CONTROL_HANGUP:
                                        ast_frfree(f);
-                                       ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+                                       ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
                                        return -1;
                                case AST_CONTROL_STREAM_STOP:
                                case AST_CONTROL_STREAM_SUSPEND:
@@ -3574,7 +3294,7 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
                                         * that perform stream control will handle this. */
                                        res = f->subclass.integer;
                                        ast_frfree(f);
-                                       ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+                                       ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
                                        return res;
                                case AST_CONTROL_PVT_CAUSE_CODE:
                                case AST_CONTROL_RINGING:
@@ -3609,7 +3329,7 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in
                }
        }
 
-       ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
+       ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
 
        return 0; /* Time is up */
 }
@@ -3776,11 +3496,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).
@@ -3902,32 +3623,6 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        if (!AST_LIST_EMPTY(ast_channel_readq(chan))) {
                int skip_dtmf = should_skip_dtmf(chan);
 
-               if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES)) {
-                       AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {
-                               if (ast_is_deferrable_frame(f)) {
-                                       if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP) {
-                                               struct ast_frame *dup;
-
-                                               /* Hangup is a special case. We want to defer the frame, but we also do not
-                                                * want to remove it from the frame queue. So rather than just moving the frame
-                                                * over, we duplicate it and move the copy to the deferred readq.
-                                                *
-                                                * The reason for this? This way, whoever calls ast_read() will get a NULL return
-                                                * immediately and can tell the channel has hung up and do what it needs to. Also,
-                                                * when frame deferral finishes, then whoever calls ast_read() next will also get
-                                                * the hangup.
-                                                */
-                                               dup = ast_frdup(f);
-                                               AST_LIST_INSERT_TAIL(ast_channel_deferred_readq(chan), dup, frame_list);
-                                       } else {
-                                               AST_LIST_INSERT_TAIL(ast_channel_deferred_readq(chan), f, frame_list);
-                                               AST_LIST_REMOVE_CURRENT(frame_list);
-                                       }
-                               }
-                       }
-                       AST_LIST_TRAVERSE_SAFE_END;
-               }
-
                AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {
                        /* We have to be picky about which frame we pull off of the readq because
                         * there are cases where we want to leave DTMF frames on the queue until
@@ -3963,6 +3658,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                        default:
                                break;
                        }
+               } else if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO) {
+                       if (ast_channel_tech(chan) && ast_channel_tech(chan)->read_stream) {
+                               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 {
+                               /* 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());
@@ -3975,15 +3681,37 @@ 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 && (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 && (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 (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
@@ -4006,6 +3734,14 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                        AST_LIST_NEXT(f, frame_list) = NULL;
                }
 
+               if (dropnondefault && stream != default_stream) {
+                       /* If the frame originates from a non-default stream and the caller can not handle other streams
+                        * absorb the frame and replace it with a null one instead.
+                        */
+                       ast_frfree(f);
+                       f = &ast_null_frame;
+               }
+
                switch (f->frametype) {
                case AST_FRAME_CONTROL:
                        if (f->subclass.integer == AST_CONTROL_ANSWER) {
@@ -4046,6 +3782,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                }
                                ast_frfree(f);
                                f = &ast_null_frame;
+                       } else if (f->subclass.integer == AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE && dropnondefault) {
+                               /* The caller of this function is incapable of handling streams so we don't accept the change request
+                                * and stick to the streams currently on the channel.
+                                */
+                               ast_channel_stream_topology_changed(chan, ast_channel_get_stream_topology(chan));
+                               ast_frfree(f);
+                               f = &ast_null_frame;
+                       } else if (f->subclass.integer == AST_CONTROL_STREAM_TOPOLOGY_CHANGED && dropnondefault) {
+                               /* The caller of this function is incapable of handling streams so we absord the notification that the
+                                * stream topology has changed.
+                                */
+                               ast_frfree(f);
+                               f = &ast_null_frame;
                        }
                        break;
                case AST_FRAME_DTMF_END:
@@ -4182,6 +3931,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
@@ -4306,7 +4060,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                }
 #else
                                int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
-                                       ast_format_get_sample_rate(f->subclass.codec),
+                                       ast_format_get_sample_rate(f->subclass.format),
                                        ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
                                if (jump - MONITOR_DELAY >= 0) {
                                        if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1) {
@@ -4350,12 +4104,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                         * at the end of the queue.
                         */
                        if (AST_LIST_NEXT(f, frame_list)) {
+                               struct ast_frame *cur, *multi_frame = AST_LIST_NEXT(f, frame_list);
+
+                               /* Mark these frames as being re-queued */
+                               for (cur = multi_frame; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+                                       ast_set_flag(cur, AST_FRFLAG_REQUEUED);
+                               }
+
                                if (!readq_tail) {
-                                       ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
+                                       ast_queue_frame_head(chan, multi_frame);
                                } else {
-                                       __ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
+                                       __ast_queue_frame(chan, multi_frame, 0, readq_tail);
                                }
-                               ast_frfree(AST_LIST_NEXT(f, frame_list));
+                               ast_frfree(multi_frame);
                                AST_LIST_NEXT(f, frame_list) = NULL;
                        }
 
@@ -4365,6 +4126,14 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                         */
                        ast_read_generator_actions(chan, f);
                        break;
+               case AST_FRAME_RTCP:
+                       /* Incoming RTCP feedback needs to get to the translator for
+                        * outgoing media, which means we treat it as an ast_write */
+                       if (ast_channel_writetrans(chan)) {
+                               ast_translate(ast_channel_writetrans(chan), f, 0);
+                       }
+                       ast_frfree(f);
+                       f = &ast_null_frame;
                default:
                        /* Just pass it on! */
                        break;
@@ -4401,12 +4170,22 @@ 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);
+}
+
+struct ast_frame *ast_read_stream_noaudio(struct ast_channel *chan)
+{
+       return __ast_read(chan, 1, 0);
 }
 
 int ast_indicate(struct ast_channel *chan, int condition)
@@ -4447,6 +4226,9 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
        case AST_CONTROL_UPDATE_RTP_PEER:
        case AST_CONTROL_PVT_CAUSE_CODE:
        case AST_CONTROL_MASQUERADE_NOTIFY:
+       case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+       case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
+       case AST_CONTROL_STREAM_TOPOLOGY_SOURCE_CHANGED:
        case AST_CONTROL_STREAM_STOP:
        case AST_CONTROL_STREAM_SUSPEND:
        case AST_CONTROL_STREAM_REVERSE:
@@ -4745,6 +4527,9 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons
        case AST_CONTROL_MCID:
        case AST_CONTROL_MASQUERADE_NOTIFY:
        case AST_CONTROL_UPDATE_RTP_PEER:
+       case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+       case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
+       case AST_CONTROL_STREAM_TOPOLOGY_SOURCE_CHANGED:
        case AST_CONTROL_STREAM_STOP:
        case AST_CONTROL_STREAM_SUSPEND:
        case AST_CONTROL_STREAM_REVERSE:
@@ -4897,16 +4682,18 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
        if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_TEXT))) {
                struct ast_frame f;
 
+               memset(&f, 0, sizeof(f));
                f.frametype = AST_FRAME_TEXT;
                f.src = "DIALPLAN";
                f.mallocd = AST_MALLOCD_DATA;
                f.datalen = strlen(text);
                f.data.ptr = ast_strdup(text);
-               f.offset = 0;
-               f.seqno = 0;
-
                f.subclass.format = ast_format_t140;
-               res = ast_channel_tech(chan)->write_text(chan, &f);
+
+               if (f.data.ptr) {
+                       res = ast_channel_tech(chan)->write_text(chan, &f);
+                       ast_frfree(&f);
+               }
        } else if (ast_channel_tech(chan)->send_text) {
                res = ast_channel_tech(chan)->send_text(chan, text);
        }
@@ -5128,6 +4915,12 @@ static void apply_plc(struct ast_channel *chan, struct ast_frame *frame)
 
 int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 {
+       return ast_write_stream(chan, -1, fr);
+}
+
+int ast_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame *fr)
+{
+       struct ast_stream *stream = NULL, *default_stream = NULL;
        int res = -1;
        struct ast_frame *f = NULL;
        int count = 0;
@@ -5140,15 +4933,41 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                        ast_debug(1, "Deadlock avoided for write to channel '%s'\n", ast_channel_name(chan));
                        return 0;
                }
-               usleep(1);
+               usleep(1);
+       }
+
+       /* Stop if we're a zombie or need a soft hangup */
+       if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+               goto done;
+       }
+
+       if (stream_num >= 0) {
+               /* If we were told to write to an explicit stream then allow this frame through, no matter
+                * if the type is expected or not (a framehook could change)
+                */
+               if (stream_num >= ast_stream_topology_get_count(ast_channel_get_stream_topology(chan))) {
+                       goto done;
+               }
+               stream = ast_stream_topology_get_stream(ast_channel_get_stream_topology(chan), stream_num);
+               default_stream = ast_channel_get_default_stream(chan, ast_stream_get_type(stream));
+       } else if (fr->frametype == AST_FRAME_VOICE || fr->frametype == AST_FRAME_VIDEO || fr->frametype == AST_FRAME_MODEM) {
+               /* If we haven't been told of a stream then we need to figure out which once we need */
+               enum ast_media_type type = AST_MEDIA_TYPE_UNKNOWN;
+
+               /* Some frame types have a fixed media type */
+               if (fr->frametype == AST_FRAME_VOICE || fr->frametype == AST_FRAME_VIDEO) {
+                       type = ast_format_get_type(fr->subclass.format);
+               } else if (fr->frametype == AST_FRAME_MODEM) {
+                       type = AST_MEDIA_TYPE_IMAGE;
+               }
+
+               /* No stream was specified, so use the default one */
+               stream = default_stream = ast_channel_get_default_stream(chan, type);
        }
-       /* Stop if we're a zombie or need a soft hangup */
-       if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
-               goto done;
 
        /* Perform the framehook write event here. After the frame enters the framehook list
         * there is no telling what will happen, how awesome is that!!! */
-       if (!(fr = ast_framehook_list_write_event(ast_channel_framehooks(chan), fr))) {
+       if ((stream == default_stream) && !(fr = ast_framehook_list_write_event(ast_channel_framehooks(chan), fr))) {
                res = 0;
                goto done;
        }
@@ -5234,41 +5053,51 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                break;
        case AST_FRAME_VIDEO:
                /* XXX Handle translation of video codecs one day XXX */
-               res = (ast_channel_tech(chan)->write_video == NULL) ? 0 :
-                       ast_channel_tech(chan)->write_video(chan, fr);
+               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_video) {
+                       res = ast_channel_tech(chan)->write_video(chan, fr);
+               } else {
+                       res = 0;
+               }
                break;
        case AST_FRAME_MODEM:
-               res = (ast_channel_tech(chan)->write == NULL) ? 0 :
-                       ast_channel_tech(chan)->write(chan, fr);
+               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;
+               }
                break;
        case AST_FRAME_VOICE:
-               if (ast_channel_tech(chan)->write == NULL)
-                       break;  /*! \todo XXX should return 0 maybe ? */
-
                if (ast_opt_generic_plc && ast_format_cmp(fr->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) {
                        apply_plc(chan, fr);
                }
 
+               f = fr;
+
                /*
                 * Send frame to audiohooks if present, if frametype is linear (else, later as per
                 * previous behavior)
                 */
-               if (ast_channel_audiohooks(chan)) {
+               if ((stream == default_stream) && ast_channel_audiohooks(chan)) {
                        if (ast_format_cache_is_slinear(fr->subclass.format)) {
-                               struct ast_frame *old_frame;
                                hooked = 1;
-                               old_frame = fr;
-                               fr = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_WRITE, fr);
-                               if (old_frame != fr) {
-                                       ast_frfree(old_frame);
-                               }
+                               f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_WRITE, fr);
                        }
                }
 
                /* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
-               if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) == AST_FORMAT_CMP_EQUAL) {
-                       f = fr;
-               } else {
+               if ((stream == default_stream) && ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) != AST_FORMAT_CMP_EQUAL) {
                        if (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL) {
                                struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
 
@@ -5294,7 +5123,18 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                                        break;
                                }
                        }
-                       f = ast_channel_writetrans(chan) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
+
+                       if (ast_channel_writetrans(chan)) {
+                               struct ast_frame *trans_frame = ast_translate(ast_channel_writetrans(chan), f, 0);
+                               if (trans_frame != f && f != fr) {
+                                       /*
+                                        * If translate gives us a new frame and so did the audio
+                                        * hook then we need to free the one from the audio hook.
+                                        */
+                                       ast_frfree(f);
+                               }
+                               f = trans_frame;
+                       }
                }
 
                if (!f) {
@@ -5302,7 +5142,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                        break;
                }
 
-               if (ast_channel_audiohooks(chan) && !hooked) {
+               if ((stream == default_stream) && ast_channel_audiohooks(chan) && !hooked) {
                        struct ast_frame *prev = NULL, *new_frame, *cur, *dup;
                        int freeoldlist = 0;
 
@@ -5351,7 +5191,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                /* the translator on chan->writetrans may have returned multiple frames
                   from the single frame we passed in; if so, feed each one of them to the
                   monitor */
-               if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->write_stream) {
+               if ((stream == default_stream) && ast_channel_monitor(chan) && ast_channel_monitor(chan)->write_stream) {
                        struct ast_frame *cur;
 
                        for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
@@ -5371,7 +5211,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                                }
 #else
                                int jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)),
-                                                            ast_format_get_sample_rate(f->subclass.codec),
+                                                            ast_format_get_sample_rate(f->subclass.format),
                                                             ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
                                if (jump - MONITOR_DELAY >= 0) {
                                        if (ast_seekstream(ast_channel_monitor(chan)->write_stream, jump - cur->samples, SEEK_FORCECUR) == -1) {
@@ -5418,7 +5258,17 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                        /* reset f so the code below doesn't attempt to free it */
                        f = NULL;
                } else {
-                       res = ast_channel_tech(chan)->write(chan, f);
+                       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;
+                       }
                }
                break;
        case AST_FRAME_NULL:
@@ -5490,6 +5340,42 @@ int ast_set_read_format_path(struct ast_channel *chan, struct ast_format *raw_fo
        return 0;
 }
 
+int ast_set_write_format_path(struct ast_channel *chan, struct ast_format *core_format, struct ast_format *raw_format)
+{
+       struct ast_trans_pvt *trans_old;
+       struct ast_trans_pvt *trans_new;
+
+       if (ast_format_cmp(ast_channel_rawwriteformat(chan), raw_format) == AST_FORMAT_CMP_EQUAL
+               && ast_format_cmp(ast_channel_writeformat(chan), core_format) == AST_FORMAT_CMP_EQUAL) {
+               /* Nothing to setup */
+               return 0;
+       }
+
+       ast_debug(1, "Channel %s setting write format path: %s -> %s\n",
+               ast_channel_name(chan),
+               ast_format_get_name(core_format),
+               ast_format_get_name(raw_format));
+
+       /* Setup new translation path. */
+       if (ast_format_cmp(raw_format, core_format) != AST_FORMAT_CMP_EQUAL) {
+               trans_new = ast_translator_build_path(raw_format, core_format);
+               if (!trans_new) {
+                       return -1;
+               }
+       } else {
+               /* No translation needed. */
+               trans_new = NULL;
+       }
+       trans_old = ast_channel_writetrans(chan);
+       if (trans_old) {
+               ast_translator_free_path(trans_old);
+       }
+       ast_channel_writetrans_set(chan, trans_new);
+       ast_channel_set_rawwriteformat(chan, raw_format);
+       ast_channel_set_writeformat(chan, core_format);
+       return 0;
+}
+
 struct set_format_access {
        const char *direction;
        struct ast_trans_pvt *(*get_trans)(const struct ast_channel *chan);
@@ -5879,9 +5765,9 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
        } else if (caller) { /* no outgoing helper so use caller if available */
                call_forward_inherit(new_chan, caller, orig);
        }
-       ast_set_flag(ast_channel_flags(new_chan), AST_FLAG_ORIGINATED);
 
        ast_channel_lock_both(orig, new_chan);
+       ast_channel_set_flag(new_chan, AST_FLAG_ORIGINATED);
        pbx_builtin_setvar_helper(new_chan, "FORWARDERNAME", forwarder);
        ast_party_connected_line_copy(ast_channel_connected(new_chan), ast_channel_connected(orig));
        ast_party_redirecting_copy(ast_channel_redirecting(new_chan), ast_channel_redirecting(orig));
@@ -6146,10 +6032,11 @@ static int set_security_requirements(const struct ast_channel *requestor, struct
        return 0;
 }
 
-struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+static struct ast_channel *request_channel(const char *type, struct ast_format_cap *request_cap, struct ast_stream_topology *topology,
+       const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
 {
        struct chanlist *chan;
-       struct ast_channel *c;
+       struct ast_channel *c = NULL;
        int res;
        int foo;
 
@@ -6163,13 +6050,44 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
        }
 
        AST_RWLIST_TRAVERSE(&backends, chan, list) {
+               if (strcasecmp(type, chan->tech->type)) {
+                       continue;
+               }
+
+               break;
+       }
+
+       AST_RWLIST_UNLOCK(&backends);
+
+       if (!chan) {
+               ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+               *cause = AST_CAUSE_NOSUCHDRIVER;
+               return NULL;
+       }
+
+       /* Allow either format capabilities or stream topology to be provided and adapt */
+       if (chan->tech->requester_with_stream_topology) {
+               struct ast_stream_topology *tmp_converted_topology = NULL;
+
+               if (!topology && request_cap) {
+                       /* Turn the requested capabilities into a stream topology */
+                       topology = tmp_converted_topology = ast_stream_topology_create_from_format_cap(request_cap);
+               }
+
+               c = chan->tech->requester_with_stream_topology(type, topology, assignedids, requestor, addr, cause);
+
+               ast_stream_topology_free(tmp_converted_topology);
+       } else if (chan->tech->requester) {
+               struct ast_format_cap *tmp_converted_cap = NULL;
                struct ast_format_cap *tmp_cap;
                RAII_VAR(struct ast_format *, tmp_fmt, NULL, ao2_cleanup);
                RAII_VAR(struct ast_format *, best_audio_fmt, NULL, ao2_cleanup);
                struct ast_format_cap *joint_cap;
 
-               if (strcasecmp(type, chan->tech->type))
-                       continue;
+               if (!request_cap && topology) {
+                       /* Turn the request stream topology into capabilities */
+                       request_cap = tmp_converted_cap = ast_format_cap_from_stream_topology(topology);
+               }
 
                /* find the best audio format to use */
                tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
@@ -6188,13 +6106,10 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
                                        ast_format_cap_get_names(chan->tech->capabilities, &tech_codecs),
                                        ast_format_cap_get_names(request_cap, &request_codecs));
                                *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
-                               AST_RWLIST_UNLOCK(&backends);
+                               ao2_cleanup(tmp_converted_cap);
                                return NULL;
                        }
                }
-               AST_RWLIST_UNLOCK(&backends);
-               if (!chan->tech->requester)
-                       return NULL;
 
                /* XXX Only the audio format calculated as being the best for translation
                 * purposes is used for the request. This is because we don't have the ability
@@ -6203,50 +6118,56 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
                 */
                joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
                if (!joint_cap) {
+                       ao2_cleanup(tmp_converted_cap);
                        return NULL;
                }
                ast_format_cap_append_from_cap(joint_cap, request_cap, AST_MEDIA_TYPE_UNKNOWN);
                ast_format_cap_remove_by_type(joint_cap, AST_MEDIA_TYPE_AUDIO);
                ast_format_cap_append(joint_cap, best_audio_fmt, 0);
+               ao2_cleanup(tmp_converted_cap);
 
-               if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) {
-                       ao2_ref(joint_cap, -1);
-                       return NULL;
-               }
+               c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause);
+               ao2_ref(joint_cap, -1);
+       }
 
-               if (requestor) {
-                       ast_callid callid;
+       if (!c) {
+               return NULL;
+       }
 
-                       ast_channel_lock_both(c, (struct ast_channel *) requestor);
+       if (requestor) {
+               ast_callid callid;
 
-                       /* Set the newly created channel's callid to the same as the requestor. */
-                       callid = ast_channel_callid(requestor);
-                       if (callid) {
-                               ast_channel_callid_set(c, callid);
-                       }
+               ast_channel_lock_both(c, (struct ast_channel *) requestor);
 
-                       ast_channel_unlock(c);
-                       ast_channel_unlock((struct ast_channel *) requestor);
+               /* Set the newly created channel's callid to the same as the requestor. */
+               callid = ast_channel_callid(requestor);
+               if (callid) {
+                       ast_channel_callid_set(c, callid);
                }
 
-               ao2_ref(joint_cap, -1);
-
-               if (set_security_requirements(requestor, c)) {
-                       ast_log(LOG_WARNING, "Setting security requirements failed\n");
-                       ast_hangup(c);
-                       *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
-                       return NULL;
-               }
+               ast_channel_unlock(c);
+               ast_channel_unlock((struct ast_channel *) requestor);
+       }
 
-               /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
-               return c;
+       if (set_security_requirements(requestor, c)) {
+               ast_log(LOG_WARNING, "Setting security requirements failed\n");
+               ast_hangup(c);
+               *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+               return NULL;
        }
 
-       ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
-       *cause = AST_CAUSE_NOSUCHDRIVER;
-       AST_RWLIST_UNLOCK(&backends);
+       /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
+       return c;
+}
 
-       return NULL;
+struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+{
+       return request_channel(type, request_cap, NULL, assignedids, requestor, addr, cause);
+}
+
+struct ast_channel *ast_request_with_stream_topology(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+{
+       return request_channel(type, NULL, topology, assignedids, requestor, addr, cause);
 }
 
 /*!
@@ -6447,11 +6368,11 @@ int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, in
                                silgen = ast_channel_start_silence_generator(c);
                        usleep(1000);
                        if (!d)
-                               d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
+                               d = ast_waitfordigit_full(c, to, NULL, audiofd, ctrlfd);
                } else {
                        if (!silgen && ast_opt_transmit_silence)
                                silgen = ast_channel_start_silence_generator(c);
-                       d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
+                       d = ast_waitfordigit_full(c, to, NULL, audiofd, ctrlfd);
                }
                if (d < 0) {
                        ast_channel_stop_silence_generator(c, silgen);
@@ -6720,6 +6641,7 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        int origstate;
        unsigned int orig_disablestatecache;
        unsigned int clone_disablestatecache;
+       int generator_fd;
        int visible_indication;
        int clone_hold_state;
        int moh_is_playing;
@@ -6910,8 +6832,13 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        /* Keep the same parkinglot. */
        ast_channel_parkinglot_set(original, ast_channel_parkinglot(clonechan));
 
-       /* Copy the FD's other than the generator fd */
-       for (x = 0; x < AST_MAX_FDS; x++) {
+       /* Clear all existing file descriptors but retain the generator */
+       generator_fd = ast_channel_fd(original, AST_GENERATOR_FD);
+       ast_channel_internal_fd_clear_all(original);
+       ast_channel_set_fd(original, AST_GENERATOR_FD, generator_fd);
+
+       /* Copy all file descriptors present on clonechan to original, skipping generator */
+       for (x = 0; x < ast_channel_fd_count(clonechan); x++) {
                if (x != AST_GENERATOR_FD)
                        ast_channel_set_fd(original, x, ast_channel_fd(clonechan, x));
        }
@@ -7057,6 +6984,8 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
                        ast_channel_tech(clonechan)->type, ast_channel_name(clonechan));
        }
 
+       ast_channel_internal_swap_stream_topology(original, clonechan);
+
        /*
         * Now, at this point, the "clone" channel is totally F'd up.
         * We mark it as a zombie so nothing tries to touch it.
@@ -7657,122 +7586,6 @@ int ast_plc_reload(void)
 
 /*!
  * \internal
- * \brief Implements the channels provider.
- */
-static int data_channels_provider_handler(const struct ast_data_search *search,
-       struct ast_data *root)
-{
-       struct ast_channel *c;
-       struct ast_channel_iterator *iter = NULL;
-       struct ast_data *data_channel;
-
-       for (iter = ast_channel_iterator_all_new();
-               iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
-               ast_channel_lock(c);
-
-               data_channel = ast_data_add_node(root, "channel");
-               if (!data_channel) {
-                       ast_channel_unlock(c);
-                       continue;
-               }
-
-               if (ast_channel_data_add_structure(data_channel, c, 1) < 0) {
-                       ast_log(LOG_ERROR, "Unable to add channel structure for channel: %s\n", ast_channel_name(c));
-               }
-
-               ast_channel_unlock(c);
-
-               if (!ast_data_search_match(search, data_channel)) {
-                       ast_data_remove_node(root, data_channel);
-               }
-       }
-       if (iter) {
-               ast_channel_iterator_destroy(iter);
-       }
-
-       return 0;
-}
-
-/*!
- * \internal
- * \brief Implements the channeltypes provider.
- */
-static int data_channeltypes_provider_handler(const struct ast_data_search *search,
-       struct ast_data *data_root)
-{
-       struct chanlist *cl;
-       struct ast_data *data_type;
-
-       AST_RWLIST_RDLOCK(&backends);
-       AST_RWLIST_TRAVERSE(&backends, cl, list) {
-               data_type = ast_data_add_node(data_root, "type");
-               if (!data_type) {
-                       continue;
-               }
-               ast_data_add_str(data_type, "name", cl->tech->type);
-               ast_data_add_str(data_type, "description", cl->tech->description);
-               ast_data_add_bool(data_type, "devicestate", cl->tech->devicestate ? 1 : 0);
-               ast_data_add_bool(data_type, "presencestate", cl->tech->presencestate ? 1 : 0);
-               ast_data_add_bool(data_type, "indications", cl->tech->indicate ? 1 : 0);
-               ast_data_add_bool(data_type, "transfer", cl->tech->transfer ? 1 : 0);
-               ast_data_add_bool(data_type, "send_digit_begin", cl->tech->send_digit_begin ? 1 : 0);
-               ast_data_add_bool(data_type, "send_digit_end", cl->tech->send_digit_end ? 1 : 0);
-               ast_data_add_bool(data_type, "call", cl->tech->call ? 1 : 0);
-               ast_data_add_bool(data_type, "hangup", cl->tech->hangup ? 1 : 0);
-               ast_data_add_bool(data_type, "answer", cl->tech->answer ? 1 : 0);
-               ast_data_add_bool(data_type, "read", cl->tech->read ? 1 : 0);
-               ast_data_add_bool(data_type, "write", cl->tech->write ? 1 : 0);
-               ast_data_add_bool(data_type, "send_text", cl->tech->send_text ? 1 : 0);
-               ast_data_add_bool(data_type, "send_image", cl->tech->send_image ? 1 : 0);
-               ast_data_add_bool(data_type, "send_html", cl->tech->send_html ? 1 : 0);
-               ast_data_add_bool(data_type, "exception", cl->tech->exception ? 1 : 0);
-               ast_data_add_bool(data_type, "early_bridge", cl->tech->early_bridge ? 1 : 0);
-               ast_data_add_bool(data_type, "fixup", cl->tech->fixup ? 1 : 0);
-               ast_data_add_bool(data_type, "setoption", cl->tech->setoption ? 1 : 0);
-               ast_data_add_bool(data_type, "queryoption", cl->tech->queryoption ? 1 : 0);
-               ast_data_add_bool(data_type, "write_video", cl->tech->write_video ? 1 : 0);
-               ast_data_add_bool(data_type, "write_text", cl->tech->write_text ? 1 : 0);
-               ast_data_add_bool(data_type, "func_channel_read", cl->tech->func_channel_read ? 1 : 0);
-               ast_data_add_bool(data_type, "func_channel_write", cl->tech->func_channel_write ? 1 : 0);
-               ast_data_add_bool(data_type, "get_pvt_uniqueid", cl->tech->get_pvt_uniqueid ? 1 : 0);
-               ast_data_add_bool(data_type, "cc_callback", cl->tech->cc_callback ? 1 : 0);
-
-               ast_data_add_codecs(data_type, "capabilities", cl->tech->capabilities);
-
-               if (!ast_data_search_match(search, data_type)) {
-                       ast_data_remove_node(data_root, data_type);
-               }
-       }
-       AST_RWLIST_UNLOCK(&backends);
-
-       return 0;
-}
-
-/*!
- * \internal
- * \brief /asterisk/core/channels provider.
- */
-static const struct ast_data_handler channels_provider = {
-       .version = AST_DATA_HANDLER_VERSION,
-       .get = data_channels_provider_handler
-};
-
-/*!
- * \internal
- * \brief /asterisk/core/channeltypes provider.
- */
-static const struct ast_data_handler channeltypes_provider = {
-       .version = AST_DATA_HANDLER_VERSION,
-       .get = data_channeltypes_provider_handler
-};
-
-static const struct ast_data_entry channel_providers[] = {
-       AST_DATA_ENTRY("/asterisk/core/channels", &channels_provider),
-       AST_DATA_ENTRY("/asterisk/core/channeltypes", &channeltypes_provider),
-};
-
-/*!
- * \internal
  * \brief Print channel object key (name).
  * \since 12.0.0
  *
@@ -7801,35 +7614,48 @@ struct manager_channel_variable {
        char name[];
 };
 
-static AST_RWLIST_HEAD_STATIC(channelvars, manager_channel_variable);
+AST_RWLIST_HEAD(external_vars, manager_channel_variable);
+
+static struct external_vars ami_vars;
+static struct external_vars ari_vars;
 
-static void free_channelvars(void)
+static void free_external_channelvars(struct external_vars *channelvars)
 {
        struct manager_channel_variable *var;
-       AST_RWLIST_WRLOCK(&channelvars);
-       while ((var = AST_RWLIST_REMOVE_HEAD(&channelvars, entry))) {
+       AST_RWLIST_WRLOCK(channelvars);
+       while ((var = AST_RWLIST_REMOVE_HEAD(channelvars, entry))) {
                ast_free(var);
        }
-       AST_RWLIST_UNLOCK(&channelvars);
+       AST_RWLIST_UNLOCK(channelvars);
 }
 
-int ast_channel_has_manager_vars(void)
+static int channel_has_external_vars(struct external_vars *channelvars)
 {
        int vars_present;
 
-       AST_RWLIST_RDLOCK(&channelvars);
-       vars_present = !AST_LIST_EMPTY(&channelvars);
-       AST_RWLIST_UNLOCK(&channelvars);
+       AST_RWLIST_RDLOCK(channelvars);
+       vars_present = !AST_LIST_EMPTY(channelvars);
+       AST_RWLIST_UNLOCK(channelvars);
 
        return vars_present;
 }
 
-void ast_channel_set_manager_vars(size_t varc, char **vars)
+int ast_channel_has_manager_vars(void)
+{
+       return channel_has_external_vars(&ami_vars);
+}
+
+int ast_channel_has_ari_vars(void)
+{
+       return channel_has_external_vars(&ari_vars);
+}
+
+static void channel_set_external_vars(struct external_vars *channelvars, size_t varc, char **vars)
 {
        size_t i;
 
-       free_channelvars();
-       AST_RWLIST_WRLOCK(&channelvars);
+       free_external_channelvars(channelvars);
+       AST_RWLIST_WRLOCK(channelvars);
        for (i = 0; i < varc; ++i) {
                const char *var = vars[i];
                struct manager_channel_variable *mcv;
@@ -7840,9 +7666,20 @@ void ast_channel_set_manager_vars(size_t varc, char **vars)
                if (strchr(var, '(')) {
                        mcv->isfunc = 1;
                }
-               AST_RWLIST_INSERT_TAIL(&channelvars, mcv, entry);
+               AST_RWLIST_INSERT_TAIL(channelvars, mcv, entry);
        }
-       AST_RWLIST_UNLOCK(&channelvars);
+       AST_RWLIST_UNLOCK(channelvars);
+
+}
+
+void ast_channel_set_manager_vars(size_t varc, char **vars)
+{
+       channel_set_external_vars(&ami_vars, varc, vars);
+}
+
+void ast_channel_set_ari_vars(size_t varc, char **vars)
+{
+       channel_set_external_vars(&ari_vars, varc, vars);
 }
 
 /*!
@@ -7884,14 +7721,15 @@ struct varshead *ast_channel_get_vars(struct ast_channel *chan)
        return ret;
 }
 
-struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan)
+static struct varshead *channel_get_external_vars(struct external_vars *channelvars,
+       struct ast_channel *chan)
 {
        RAII_VAR(struct varshead *, ret, NULL, ao2_cleanup);
        RAII_VAR(struct ast_str *, tmp, NULL, ast_free);
        struct manager_channel_variable *mcv;
-       SCOPED_LOCK(lock, &channelvars, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+       SCOPED_LOCK(lock, channelvars, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
 
-       if (AST_LIST_EMPTY(&channelvars)) {
+       if (AST_LIST_EMPTY(channelvars)) {
                return NULL;
        }
 
@@ -7902,7 +7740,7 @@ struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan)
                return NULL;
        }
 
-       AST_LIST_TRAVERSE(&channelvars, mcv, entry) {
+       AST_LIST_TRAVERSE(channelvars, mcv, entry) {
                const char *val = NULL;
                struct ast_var_t *var;
 
@@ -7927,13 +7765,24 @@ struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan)
 
        ao2_ref(ret, +1);
        return ret;
+
+}
+
+struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan)
+{
+       return channel_get_external_vars(&ami_vars, chan);
+}
+
+struct varshead *ast_channel_get_ari_vars(struct ast_channel *chan)
+{
+       return channel_get_external_vars(&ari_vars, chan);
 }
 
 static void channels_shutdown(void)
 {
-       free_channelvars();
+       free_external_channelvars(&ami_vars);
+       free_external_channelvars(&ari_vars);
 
-       ast_data_unregister(NULL);
        ast_cli_unregister_multiple(cli_channel, ARRAY_LEN(cli_channel));
        if (channels) {
                ao2_container_unregister("channels");
@@ -7958,12 +7807,13 @@ int ast_channels_init(void)
 
        ast_cli_register_multiple(cli_channel, ARRAY_LEN(cli_channel));
 
-       ast_data_register_multiple_core(channel_providers, ARRAY_LEN(channel_providers));
-
        ast_plc_reload();
 
        ast_register_cleanup(channels_shutdown);
 
+       AST_RWLIST_HEAD_INIT(&ami_vars);
+       AST_RWLIST_HEAD_INIT(&ari_vars);
+
        return 0;
 }
 
@@ -9195,7 +9045,7 @@ static int redirecting_reason_build_data(unsigned char *data, size_t datalen,
 
        if (reason->str) {
                length = strlen(reason->str);
-               if (datalen < pos + sizeof(data[0] * 2) + length) {
+               if (datalen < pos + (sizeof(data[0]) * 2) + length) {
                        ast_log(LOG_WARNING, "No space left for %s string\n", label);
                        return -1;
                }
@@ -10250,6 +10100,36 @@ void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct
        ast_queue_control_data(chan, AST_CONTROL_REDIRECTING, data, datalen);
 }
 
+/*!
+ * Storage to determine if the current thread is running an intercept dialplan routine.
+ */
+AST_THREADSTORAGE_RAW(in_intercept_routine);
+
+/*!
+ * \internal
+ * \brief Set the current intercept dialplan routine status mode.
+ * \since 13.14.0
+ *
+ * \param in_intercept_mode New intercept mode.  (Non-zero if in intercept mode)
+ *
+ * \return Nothing
+ */
+static void channel_set_intercept_mode(int in_intercept_mode)
+{
+       int status;
+
+       status = ast_threadstorage_set_ptr(&in_intercept_routine,
+               in_intercept_mode ? (void *) 1 : (void *) 0);
+       if (status) {
+               ast_log(LOG_ERROR, "Failed to set dialplan intercept mode\n");
+       }
+}
+
+int ast_channel_get_intercept_mode(void)
+{
+       return ast_threadstorage_get_ptr(&in_intercept_routine) ? 1 : 0;
+}
+
 int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const void *connected_info, int is_caller, int is_frame)
 {
        static int deprecation_warning = 0;
@@ -10283,15 +10163,11 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc
 
                ast_party_connected_line_copy(ast_channel_connected(macro_chan), connected);
        }
-       ast_channel_start_defer_frames(macro_chan);
        ast_channel_unlock(macro_chan);
 
+       channel_set_intercept_mode(1);
        retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args);
-
-       ast_channel_lock(macro_chan);
-       ast_channel_stop_defer_frames(macro_chan);
-       ast_channel_unlock(macro_chan);
-
+       channel_set_intercept_mode(0);
        if (!retval) {
                struct ast_party_connected_line saved_connected;
 
@@ -10339,15 +10215,11 @@ int ast_channel_redirecting_macro(struct ast_channel *autoservice_chan, struct a
 
                ast_party_redirecting_copy(ast_channel_redirecting(macro_chan), redirecting);
        }
-       ast_channel_start_defer_frames(macro_chan);
        ast_channel_unlock(macro_chan);
 
+       channel_set_intercept_mode(1);
        retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args);
-
-       ast_channel_lock(macro_chan);
-       ast_channel_stop_defer_frames(macro_chan);
-       ast_channel_unlock(macro_chan);
-
+       channel_set_intercept_mode(0);
        if (!retval) {
                struct ast_party_redirecting saved_redirecting;
 
@@ -10388,15 +10260,11 @@ int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct
 
                ast_party_connected_line_copy(ast_channel_connected(sub_chan), connected);
        }
-       ast_channel_start_defer_frames(sub_chan);
        ast_channel_unlock(sub_chan);
 
+       channel_set_intercept_mode(1);
        retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0);
-
-       ast_channel_lock(sub_chan);
-       ast_channel_stop_defer_frames(sub_chan);
-       ast_channel_unlock(sub_chan);
-
+       channel_set_intercept_mode(0);
        if (!retval) {
                struct ast_party_connected_line saved_connected;
 
@@ -10437,15 +10305,11 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast
 
                ast_party_redirecting_copy(ast_channel_redirecting(sub_chan), redirecting);
        }
-       ast_channel_start_defer_frames(sub_chan);
        ast_channel_unlock(sub_chan);
 
+       channel_set_intercept_mode(1);
        retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0);
-
-       ast_channel_lock(sub_chan);
-       ast_channel_stop_defer_frames(sub_chan);
-       ast_channel_unlock(sub_chan);
-
+       channel_set_intercept_mode(0);
        if (!retval) {
                struct ast_party_redirecting saved_redirecting;
 
@@ -10634,6 +10498,7 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
                char *context;
                char *name;
                int amaflags;
+               int priority;
                struct ast_format *readformat;
                struct ast_format *writeformat;
        } my_vars = { 0, };
@@ -10644,6 +10509,16 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
        my_vars.context = ast_strdupa(ast_channel_context(yankee));
        my_vars.name = ast_strdupa(ast_channel_name(yankee));
        my_vars.amaflags = ast_channel_amaflags(yankee);
+       my_vars.priority = ast_channel_priority(yankee);
+       /* The priority as returned by ast_channel_yank is where the channel
+        * should go if the dialplan is executed on it. If the channel is
+        * already executing dialplan then the priority currently set is
+        * where it is currently. We increment it so it becomes where it should
+        * execute.
+        */
+       if (ast_test_flag(ast_channel_flags(yankee), AST_FLAG_IN_AUTOLOOP)) {
+               my_vars.priority++;
+       }
        my_vars.writeformat = ao2_bump(ast_channel_writeformat(yankee));
        my_vars.readformat = ao2_bump(ast_channel_readformat(yankee));
        ast_channel_unlock(yankee);
@@ -10663,6 +10538,7 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
        ast_channel_set_writeformat(yanked_chan, my_vars.writeformat);
        ao2_cleanup(my_vars.readformat);
        ao2_cleanup(my_vars.writeformat);
+       ast_channel_priority_set(yanked_chan, my_vars.priority);
 
        ast_channel_unlock(yanked_chan);
 
@@ -10791,7 +10667,7 @@ static const struct ast_datastore_info *suppress_get_datastore_information(enum
 
 int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum ast_frame_type frametype)
 {
-       RAII_VAR(struct suppress_data *, suppress, NULL, ao2_cleanup);
+       struct suppress_data *suppress;
        const struct ast_datastore_info *datastore_info = NULL;
        struct ast_datastore *datastore = NULL;
        struct ast_framehook_interface interface = {
@@ -10827,6 +10703,7 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
        if (framehook_id < 0) {
                /* Hook attach failed.  Get rid of the evidence. */
                ast_log(LOG_WARNING, "Failed to attach framehook while attempting to suppress a stream.\n");
+               ao2_ref(suppress, -1);
                return -1;
        }
 
@@ -10838,11 +10715,11 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
        if (!(datastore = ast_datastore_alloc(datastore_info, NULL))) {
                ast_log(LOG_WARNING, "Failed to allocate datastore while attempting to suppress a stream.\n");
                ast_framehook_detach(chan, framehook_id);
+               ao2_ref(suppress, -1);
                return -1;
        }
 
-       /* and another ref for the datastore */
-       ao2_ref(suppress, +1);
+       /* the ref provided by the allocation is taken by the datastore */
        datastore->data = suppress;
 
        ast_channel_datastore_add(chan, datastore);
@@ -10976,3 +10853,57 @@ enum ast_channel_error ast_channel_errno(void)
 {
        return ast_channel_internal_errno();
 }
+
+int ast_channel_request_stream_topology_change(struct ast_channel *chan,
+               struct ast_stream_topology *topology, void *change_source)
+{
+       int res;
+
+       ast_assert(chan != NULL);
+       ast_assert(topology != NULL);
+
+       ast_channel_lock(chan);
+       if (!ast_channel_is_multistream(chan) || !ast_channel_tech(chan)->indicate) {
+               ast_channel_unlock(chan);
+               return -1;
+       }
+
+       if (ast_stream_topology_equal(ast_channel_get_stream_topology(chan), topology)) {
+               ast_debug(3, "Topology of %s already matches what is requested so ignoring topology change request\n",
+                               ast_channel_name(chan));
+               ast_channel_unlock(chan);
+               return 0;
+       }
+
+       ast_channel_internal_set_stream_topology_change_source(chan, change_source);
+
+       res = ast_channel_tech(chan)->indicate(chan, AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE, topology, sizeof(topology));
+       ast_channel_unlock(chan);
+       return res;
+}
+
+int ast_channel_stream_topology_changed(struct ast_channel *chan, struct ast_stream_topology *topology)
+{
+       ast_assert(chan != NULL);
+       ast_assert(topology != NULL);
+
+       if (!ast_channel_is_multistream(chan) || !ast_channel_tech(chan)->indicate) {
+               return -1;
+       }
+
+       return ast_channel_tech(chan)->indicate(chan, AST_CONTROL_STREAM_TOPOLOGY_CHANGED, topology, sizeof(topology));
+}
+
+void ast_channel_set_flag(struct ast_channel *chan, unsigned int flag)
+{
+       ast_channel_lock(chan);
+       ast_set_flag(ast_channel_flags(chan), flag);
+       ast_channel_unlock(chan);
+}
+
+void ast_channel_clear_flag(struct ast_channel *chan, unsigned int flag)
+{
+       ast_channel_lock(chan);
+       ast_clear_flag(ast_channel_flags(chan), flag);
+       ast_channel_unlock(chan);
+}