git migration: Refactor the ASTERISK_FILE_VERSION macro
[asterisk/asterisk.git] / main / channel.c
index 27e07b8..4e418b6 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
 
 #include "asterisk/_private.h"
 
@@ -100,9 +100,6 @@ struct ast_epoll_data {
 #define MONITOR_DELAY  150 * 8         /*!< 150 ms of MONITORING DELAY */
 #endif
 
-/*! \brief Prevent new channel allocation if shutting down. */
-static int shutting_down;
-
 static int chancount;
 
 unsigned long global_fin, global_fout;
@@ -275,7 +272,7 @@ static const char *party_number_plan2str(int plan)
 /*! \brief Show channel types - CLI command */
 static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-#define FORMAT  "%-15.15s  %-40.40s %-12.12s %-12.12s %-12.12s\n"
+#define FORMAT  "%-15.15s  %-40.40s %-13.13s %-13.13s %-13.13s %-13.13s\n"
        struct chanlist *cl;
        int count_chan = 0;
 
@@ -294,13 +291,14 @@ static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd,
        if (a->argc != 3)
                return CLI_SHOWUSAGE;
 
-       ast_cli(a->fd, FORMAT, "Type", "Description",       "Devicestate", "Indications", "Transfer");
-       ast_cli(a->fd, FORMAT, "-----------", "-----------", "-----------", "-----------", "-----------");
+       ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Presencestate", "Indications", "Transfer");
+       ast_cli(a->fd, FORMAT, "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
 
        AST_RWLIST_RDLOCK(&backends);
        AST_RWLIST_TRAVERSE(&backends, cl, list) {
                ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
                        (cl->tech->devicestate) ? "yes" : "no",
+                       (cl->tech->presencestate) ? "yes" : "no",
                        (cl->tech->indicate) ? "yes" : "no",
                        (cl->tech->transfer) ? "yes" : "no");
                count_chan++;
@@ -342,7 +340,7 @@ static char *complete_channeltypes(struct ast_cli_args *a)
 static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        struct chanlist *cl = NULL;
-       char buf[512];
+       struct ast_str *codec_buf = ast_str_alloca(256);
 
        switch (cmd) {
        case CLI_INIT:
@@ -375,6 +373,7 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
        ast_cli(a->fd,
                "-- Info about channel driver: %s --\n"
                "  Device State: %s\n"
+               "Presence State: %s\n"
                "    Indication: %s\n"
                "     Transfer : %s\n"
                "  Capabilities: %s\n"
@@ -385,9 +384,10 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
                "  Text Support: %s\n",
                cl->tech->type,
                (cl->tech->devicestate) ? "yes" : "no",
+               (cl->tech->presencestate) ? "yes" : "no",
                (cl->tech->indicate) ? "yes" : "no",
                (cl->tech->transfer) ? "yes" : "no",
-               ast_getformatname_multiple(buf, sizeof(buf), cl->tech->capabilities),
+               ast_format_cap_get_names(cl->tech->capabilities, &codec_buf),
                (cl->tech->send_digit_begin) ? "yes" : "no",
                (cl->tech->send_digit_end) ? "yes" : "no",
                (cl->tech->send_html) ? "yes" : "no",
@@ -504,13 +504,9 @@ static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
        return 0;
 }
 
-void ast_begin_shutdown(int hangup)
+void ast_softhangup_all(void)
 {
-       shutting_down = 1;
-
-       if (hangup) {
-               ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
-       }
+       ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
 }
 
 /*! \brief returns number of active/allocated channels */
@@ -524,18 +520,6 @@ int ast_undestroyed_channels(void)
        return ast_atomic_fetchadd_int(&chancount, 0);
 }
 
-/*! \brief Cancel a shutdown in progress */
-void ast_cancel_shutdown(void)
-{
-       shutting_down = 0;
-}
-
-/*! \brief Returns non-zero if Asterisk is being shut down */
-int ast_shutting_down(void)
-{
-       return shutting_down;
-}
-
 /*! \brief Set when to hangup channel */
 void ast_channel_setwhentohangup_tv(struct ast_channel *chan, struct timeval offset)
 {
@@ -678,6 +662,11 @@ int ast_str2cause(const char *name)
 static struct stasis_message *create_channel_snapshot_message(struct ast_channel *channel)
 {
        RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+
+       if (!ast_channel_snapshot_type()) {
+               return NULL;
+       }
+
        ast_channel_lock(channel);
        snapshot = ast_channel_snapshot_create(channel);
        ast_channel_unlock(channel);
@@ -764,78 +753,6 @@ char *ast_transfercapability2str(int transfercapability)
        }
 }
 
-/*! \brief Pick the best audio codec */
-struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format *result)
-{
-       /* This just our opinion, expressed in code.  We are asked to choose
-          the best codec to use, given no information */
-       static const enum ast_format_id prefs[] =
-       {
-               /*! Okay, ulaw is used by all telephony equipment, so start with it */
-               AST_FORMAT_ULAW,
-               /*! Unless of course, you're a silly European, so then prefer ALAW */
-               AST_FORMAT_ALAW,
-               AST_FORMAT_G719,
-               AST_FORMAT_SIREN14,
-               AST_FORMAT_SIREN7,
-               AST_FORMAT_TESTLAW,
-               /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */
-               AST_FORMAT_G722,
-               /*! Okay, well, signed linear is easy to translate into other stuff */
-               AST_FORMAT_SLINEAR192,
-               AST_FORMAT_SLINEAR96,
-               AST_FORMAT_SLINEAR48,
-               AST_FORMAT_SLINEAR44,
-               AST_FORMAT_SLINEAR32,
-               AST_FORMAT_SLINEAR24,
-               AST_FORMAT_SLINEAR16,
-               AST_FORMAT_SLINEAR12,
-               AST_FORMAT_SLINEAR,
-               /*! G.726 is standard ADPCM, in RFC3551 packing order */
-               AST_FORMAT_G726,
-               /*! G.726 is standard ADPCM, in AAL2 packing order */
-               AST_FORMAT_G726_AAL2,
-               /*! ADPCM has great sound quality and is still pretty easy to translate */
-               AST_FORMAT_ADPCM,
-               /*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to
-                   translate and sounds pretty good */
-               AST_FORMAT_GSM,
-               /*! iLBC is not too bad */
-               AST_FORMAT_ILBC,
-               /*! Speex is free, but computationally more expensive than GSM */
-               AST_FORMAT_SPEEX32,
-               AST_FORMAT_SPEEX16,
-               AST_FORMAT_SPEEX,
-               /*! Opus */
-               AST_FORMAT_OPUS,
-               /*! SILK is pretty awesome. */
-               AST_FORMAT_SILK,
-               /*! CELT supports crazy high sample rates */
-               AST_FORMAT_CELT,
-               /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
-                   to use it */
-               AST_FORMAT_LPC10,
-               /*! G.729a is faster than 723 and slightly less expensive */
-               AST_FORMAT_G729A,
-               /*! Down to G.723.1 which is proprietary but at least designed for voice */
-               AST_FORMAT_G723_1,
-       };
-       char buf[512];
-       int x;
-
-       /* Find the first preferred codec in the format given */
-       for (x = 0; x < ARRAY_LEN(prefs); x++) {
-               if (ast_format_cap_best_byid(cap, prefs[x], result)) {
-                       return result;
-               }
-       }
-
-       ast_format_clear(result);
-       ast_log(LOG_WARNING, "Don't know any of %s formats\n", ast_getformatname_multiple(buf, sizeof(buf), cap));
-
-       return NULL;
-}
-
 /*! \brief Channel technology used to extract a channel from a running application. The
  * channel created with this technology will be immediately hung up - most external
  * applications won't ever want to see this.
@@ -855,10 +772,11 @@ static void ast_channel_destructor(void *obj);
 static void ast_dummy_channel_destructor(void *obj);
 
 /*! \brief Create a new channel structure */
-static struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 0)))
+static struct ast_channel * attribute_malloc __attribute__((format(printf, 15, 0)))
 __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char *cid_name,
                       const char *acctcode, const char *exten, const char *context, const struct ast_assigned_ids *assignedids,
-                      const struct ast_channel *requestor, enum ama_flags amaflag, const char *file, int line,
+                      const struct ast_channel *requestor, enum ama_flags amaflag, struct ast_endpoint *endpoint,
+                      const char *file, int line,
                       const char *function, const char *name_fmt, va_list ap)
 {
        struct ast_channel *tmp;
@@ -883,11 +801,18 @@ __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_CACHE_STRINGS))) {
+       if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
                /* format capabilities structure allocation failure */
                return ast_channel_unref(tmp);
        }
+       ast_format_cap_append(nativeformats, ast_format_none, 0);
        ast_channel_nativeformats_set(tmp, nativeformats);
+       ao2_ref(nativeformats, -1);
+
+       ast_channel_set_rawwriteformat(tmp, ast_format_none);
+       ast_channel_set_rawreadformat(tmp, ast_format_none);
+       ast_channel_set_writeformat(tmp, ast_format_none);
+       ast_channel_set_readformat(tmp, ast_format_none);
 
        /*
         * Init file descriptors to unopened state so
@@ -1028,6 +953,10 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
 
        ao2_link(channels, tmp);
 
+       if (endpoint) {
+               ast_endpoint_add_channel(endpoint, tmp);
+       }
+
        /*
         * And now, since the channel structure is built, and has its name, let
         * the world know of its existance
@@ -1040,6 +969,7 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci
                                        const char *cid_name, const char *acctcode,
                                        const char *exten, const char *context, const struct ast_assigned_ids *assignedids,
                                        const struct ast_channel *requestor, enum ama_flags amaflag,
+                                       struct ast_endpoint *endpoint,
                                        const char *file, int line, const char *function,
                                        const char *name_fmt, ...)
 {
@@ -1048,7 +978,7 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci
 
        va_start(ap, name_fmt);
        result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context,
-                                       assignedids, requestor, amaflag, file, line, function, name_fmt, ap);
+                                       assignedids, requestor, amaflag, endpoint, file, line, function, name_fmt, ap);
        va_end(ap);
 
        return result;
@@ -1266,11 +1196,10 @@ int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause)
 
 int ast_queue_hold(struct ast_channel *chan, const char *musicclass)
 {
-       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
        struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HOLD };
+       struct ast_json *blob = NULL;
        int res;
 
-       ast_channel_lock(chan);
        if (!ast_strlen_zero(musicclass)) {
                f.data.ptr = (void *) musicclass;
                f.datalen = strlen(musicclass) + 1;
@@ -1279,10 +1208,12 @@ int ast_queue_hold(struct ast_channel *chan, const char *musicclass)
                                     "musicclass", musicclass);
        }
 
-       ast_channel_publish_blob(chan, ast_channel_hold_type(), blob);
+       ast_channel_publish_cached_blob(chan, ast_channel_hold_type(), blob);
 
        res = ast_queue_frame(chan, &f);
-       ast_channel_unlock(chan);
+
+       ast_json_unref(blob);
+
        return res;
 }
 
@@ -1291,11 +1222,10 @@ int ast_queue_unhold(struct ast_channel *chan)
        struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_UNHOLD };
        int res;
 
-       ast_channel_lock(chan);
-       ast_channel_publish_blob(chan, ast_channel_unhold_type(), NULL);
+       ast_channel_publish_cached_blob(chan, ast_channel_unhold_type(), NULL);
 
        res = ast_queue_frame(chan, &f);
-       ast_channel_unlock(chan);
+
        return res;
 }
 
@@ -2246,7 +2176,7 @@ static void ast_channel_destructor(void *obj)
        struct varshead *headp;
        struct ast_datastore *datastore;
        char device_name[AST_CHANNEL_NAME];
-       struct ast_callid *callid;
+       ast_callid callid;
 
        /* Stop monitoring */
        if (ast_channel_monitor(chan)) {
@@ -2319,6 +2249,13 @@ static void ast_channel_destructor(void *obj)
        if (ast_channel_pbx(chan))
                ast_log_callid(LOG_WARNING, callid, "PBX may not have been terminated properly on '%s'\n", ast_channel_name(chan));
 
+       /* Free formats */
+       ast_channel_set_oldwriteformat(chan, NULL);
+       ast_channel_set_rawreadformat(chan, NULL);
+       ast_channel_set_rawwriteformat(chan, NULL);
+       ast_channel_set_readformat(chan, NULL);
+       ast_channel_set_writeformat(chan, NULL);
+
        ast_party_dialed_free(ast_channel_dialed(chan));
        ast_party_caller_free(ast_channel_caller(chan));
        ast_party_connected_line_free(ast_channel_connected(chan));
@@ -2375,10 +2312,7 @@ static void ast_channel_destructor(void *obj)
                ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE) ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), device_name);
        }
 
-       ast_channel_nativeformats_set(chan, ast_format_cap_destroy(ast_channel_nativeformats(chan)));
-       if (callid) {
-               ast_callid_unref(callid);
-       }
+       ast_channel_nativeformats_set(chan, NULL);
 
        ast_channel_named_callgroups_set(chan, NULL);
        ast_channel_named_pickupgroups_set(chan, NULL);
@@ -2596,7 +2530,7 @@ void ast_channel_clear_softhangup(struct ast_channel *chan, int flag)
 /*! \brief Softly hangup a channel, don't lock */
 int ast_softhangup_nolock(struct ast_channel *chan, int cause)
 {
-       ast_debug(1, "Soft-Hanging (%#04x) up channel '%s'\n", cause, ast_channel_name(chan));
+       ast_debug(1, "Soft-Hanging (%#04x) up channel '%s'\n", (unsigned)cause, ast_channel_name(chan));
        /* Inform channel driver that we need to be hung up, if it cares */
        ast_channel_softhangup_internal_flag_add(chan, cause);
        ast_queue_frame(chan, &ast_null_frame);
@@ -2625,21 +2559,14 @@ int ast_softhangup(struct ast_channel *chan, int cause)
 
 static void free_translation(struct ast_channel *clonechan)
 {
-       if (ast_channel_writetrans(clonechan))
+       if (ast_channel_writetrans(clonechan)) {
                ast_translator_free_path(ast_channel_writetrans(clonechan));
-       if (ast_channel_readtrans(clonechan))
+       }
+       if (ast_channel_readtrans(clonechan)) {
                ast_translator_free_path(ast_channel_readtrans(clonechan));
+       }
        ast_channel_writetrans_set(clonechan, NULL);
        ast_channel_readtrans_set(clonechan, NULL);
-       if (ast_format_cap_is_empty(ast_channel_nativeformats(clonechan))) {
-               ast_format_clear(ast_channel_rawwriteformat(clonechan));
-               ast_format_clear(ast_channel_rawreadformat(clonechan));
-       } else {
-               struct ast_format tmpfmt;
-               ast_best_codec(ast_channel_nativeformats(clonechan), &tmpfmt);
-               ast_format_copy(ast_channel_rawwriteformat(clonechan), &tmpfmt);
-               ast_format_copy(ast_channel_rawreadformat(clonechan), &tmpfmt);
-       }
 }
 
 void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force)
@@ -2899,14 +2826,14 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
                                }
                        }
 
-                       if (res == 0) {
-                               ast_channel_lock(chan);
-                               while ((cur = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
+                       ast_channel_lock(chan);
+                       while ((cur = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
+                               if (res == 0) {
                                        ast_queue_frame_head(chan, cur);
-                                       ast_frfree(cur);
                                }
-                               ast_channel_unlock(chan);
+                               ast_frfree(cur);
                        }
+                       ast_channel_unlock(chan);
                } while (0);
                break;
        default:
@@ -3000,10 +2927,11 @@ static int generator_force(const void *data)
                generate = ast_channel_generator(chan)->generate;
        ast_channel_unlock(chan);
 
-       if (!tmp || !generate)
+       if (!tmp || !generate) {
                return 0;
+       }
 
-       res = generate(chan, tmp, 0, ast_format_rate(ast_channel_writeformat(chan)) / 50);
+       res = generate(chan, tmp, 0, ast_format_get_sample_rate(ast_channel_writeformat(chan)) / 50);
 
        ast_channel_lock(chan);
        if (ast_channel_generator(chan) && generate == ast_channel_generator(chan)->generate) {
@@ -3639,7 +3567,7 @@ static void send_dtmf_begin_event(struct ast_channel *chan,
                return;
        }
 
-       ast_channel_publish_blob(chan, ast_channel_dtmf_begin_type(), blob);
+       ast_channel_publish_cached_blob(chan, ast_channel_dtmf_begin_type(), blob);
 }
 
 static void send_dtmf_end_event(struct ast_channel *chan,
@@ -3656,7 +3584,7 @@ static void send_dtmf_end_event(struct ast_channel *chan,
                return;
        }
 
-       ast_channel_publish_blob(chan, ast_channel_dtmf_end_type(), blob);
+       ast_channel_publish_cached_blob(chan, ast_channel_dtmf_end_type(), blob);
 }
 
 static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f)
@@ -3679,11 +3607,9 @@ static void ast_read_generator_actions(struct ast_channel *chan, struct ast_fram
         * We must generate frames in phase locked mode since
         * we have no internal timer available.
         */
-
-       if (ast_format_cmp(&f->subclass.format, ast_channel_writeformat(chan)) == AST_FORMAT_CMP_NOT_EQUAL) {
+       if (ast_format_cmp(f->subclass.format, ast_channel_writeformat(chan)) == AST_FORMAT_CMP_NOT_EQUAL) {
                float factor;
-
-               factor = ((float) ast_format_rate(ast_channel_writeformat(chan))) / ((float) ast_format_rate(&f->subclass.format));
+               factor = ((float) ast_format_get_sample_rate(ast_channel_writeformat(chan))) / ((float) ast_format_get_sample_rate(f->subclass.format));
                samples = (int) (((float) f->samples) * factor);
        } else {
                samples = f->samples;
@@ -3988,7 +3914,6 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                        ast_frfree(f);
                                        f = &ast_null_frame;
                                } else {
-                                       /* Answer the CDR */
                                        ast_setstate(chan, AST_STATE_UP);
                                }
                        } else if (f->subclass.integer == AST_CONTROL_READ_ACTION) {
@@ -4189,74 +4114,133 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                        ast_frfree(f);
                                        f = &ast_null_frame;
                                }
-                       } else if ((f->frametype == AST_FRAME_VOICE) && !ast_format_cap_iscompatible(ast_channel_nativeformats(chan), &f->subclass.format)) {
-                               /* This frame is not one of the current native formats -- drop it on the floor */
-                               char to[200];
-                               ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
-                                       ast_channel_name(chan), ast_getformatname(&f->subclass.format), ast_getformatname_multiple(to, sizeof(to), ast_channel_nativeformats(chan)));
-                               ast_frfree(f);
-                               f = &ast_null_frame;
-                       } else if ((f->frametype == AST_FRAME_VOICE)) {
-                               /* Send frame to audiohooks if present */
-                               if (ast_channel_audiohooks(chan)) {
-                                       struct ast_frame *old_frame = f;
-                                       f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
-                                       if (old_frame != f)
-                                               ast_frfree(old_frame);
+                               break;
+                       }
+                       if (f->frametype != AST_FRAME_VOICE) {
+                               break;
+                       }
+                       if (ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) != AST_FORMAT_CMP_EQUAL
+                               && ast_format_cmp(f->subclass.format, ast_channel_readformat(chan)) != AST_FORMAT_CMP_EQUAL) {
+                               struct ast_format *core_format;
+
+                               /*
+                                * Note: This frame may not be one of the current native
+                                * formats.  We may have gotten it out of the read queue from
+                                * a previous multi-frame translation, from a framehook
+                                * injected frame, or the device we're talking to isn't
+                                * respecting negotiated formats.  Regardless we will accept
+                                * all frames.
+                                *
+                                * Update the read translation path to handle the new format
+                                * that just came in.  If the core wants slinear we need to
+                                * setup a new translation path because the core is usually
+                                * doing something with the audio itself and may not handle
+                                * any other format.  e.g., Softmix bridge, holding bridge
+                                * announcer channel, recording, AMD...  Otherwise, we'll
+                                * setup to pass the frame as is to the core.  In this case
+                                * the core doesn't care.  The channel is likely in
+                                * autoservice, safesleep, or the channel is in a bridge.
+                                * Let the bridge technology deal with format compatibility
+                                * between the channels in the bridge.
+                                *
+                                * Beware of the transcode_via_slin and genericplc options as
+                                * they force any transcoding to go through slin on a bridge.
+                                * Unfortunately transcode_via_slin is enabled by default and
+                                * genericplc is enabled in the codecs.conf.sample file.
+                                *
+                                * XXX Only updating translation to slinear frames has some
+                                * corner cases if slinear is one of the native formats and
+                                * there are different sample rates involved.  We might wind
+                                * up with conflicting translation paths between channels
+                                * where the read translation path on this channel reduces
+                                * the sample rate followed by a write translation path on
+                                * the peer channel that increases the sample rate.
+                                */
+                               core_format = ast_channel_readformat(chan);
+                               if (!ast_format_cache_is_slinear(core_format)) {
+                                       core_format = f->subclass.format;
+                               }
+                               if (ast_set_read_format_path(chan, f->subclass.format, core_format)) {
+                                       /* Drop frame.  We couldn't make it compatible with the core. */
+                                       ast_frfree(f);
+                                       f = &ast_null_frame;
+                                       break;
+                               }
+                       }
+                       /* Send frame to audiohooks if present */
+                       if (ast_channel_audiohooks(chan)) {
+                               struct ast_frame *old_frame = f;
+
+                               f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
+                               if (old_frame != f) {
+                                       ast_frfree(old_frame);
                                }
-                               if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream ) {
-                                       /* XXX what does this do ? */
+                       }
+                       if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream) {
+                               /* XXX what does this do ? */
 #ifndef MONITOR_CONSTANT_DELAY
-                                       int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
-                                       if (jump >= 0) {
-                                               jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)), ast_format_rate(&f->subclass.format), ast_format_rate(&ast_channel_monitor(chan)->read_stream->fmt->format));
-                                               if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
-                                                       ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
-                                               }
-                                               ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
-                                       } else {
-                                               ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
+                               int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
+                               if (jump >= 0) {
+                                       jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
+                                               ast_format_get_sample_rate(f->subclass.format),
+                                               ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
+                                       if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
+                                               ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
                                        }
+                                       ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
+                               } else {
+                                       ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
+                               }
 #else
-                                       int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)), ast_format_rate(f->subclass.codec), ast_format_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)
-                                                       ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
-                                               ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
-                                       } else
-                                               ast_channel_insmpl(chan) += f->samples;
-#endif
-                                       if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
-                                               if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
-                                                       ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+                               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(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) {
+                                               ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
                                        }
+                                       ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
+                               } else {
+                                       ast_channel_insmpl(chan) += f->samples;
+                               }
+#endif
+                               if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
+                                       if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
+                                               ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
                                }
+                       }
 
-                               if (ast_channel_readtrans(chan) && (f = ast_translate(ast_channel_readtrans(chan), f, 1)) == NULL) {
+                       if (ast_channel_readtrans(chan)
+                               && ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) == AST_FORMAT_CMP_EQUAL) {
+                               f = ast_translate(ast_channel_readtrans(chan), f, 1);
+                               if (!f) {
                                        f = &ast_null_frame;
                                }
+                       }
 
-                               /* it is possible for the translation process on chan->readtrans to have
-                                  produced multiple frames from the single input frame we passed it; if
-                                  this happens, queue the additional frames *before* the frames we may
-                                  have queued earlier. if the readq was empty, put them at the head of
-                                  the queue, and if it was not, put them just after the frame that was
-                                  at the end of the queue.
-                               */
-                               if (AST_LIST_NEXT(f, frame_list)) {
-                                       if (!readq_tail) {
-                                               ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
-                                       } else {
-                                               __ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
-                                       }
-                                       ast_frfree(AST_LIST_NEXT(f, frame_list));
-                                       AST_LIST_NEXT(f, frame_list) = NULL;
+                       /*
+                        * It is possible for the translation process on the channel to have
+                        * produced multiple frames from the single input frame we passed it; if
+                        * this happens, queue the additional frames *before* the frames we may
+                        * have queued earlier. if the readq was empty, put them at the head of
+                        * the queue, and if it was not, put them just after the frame that was
+                        * at the end of the queue.
+                        */
+                       if (AST_LIST_NEXT(f, frame_list)) {
+                               if (!readq_tail) {
+                                       ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
+                               } else {
+                                       __ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
                                }
-
-                               /* Run generator sitting on the line if timing device not available
-                               * and synchronous generation of outgoing frames is necessary       */
-                               ast_read_generator_actions(chan, f);
+                               ast_frfree(AST_LIST_NEXT(f, frame_list));
+                               AST_LIST_NEXT(f, frame_list) = NULL;
                        }
+
+                       /*
+                        * Run generator sitting on the line if timing device not available
+                        * and synchronous generation of outgoing frames is necessary
+                        */
+                       ast_read_generator_actions(chan, f);
                        break;
                default:
                        /* Just pass it on! */
@@ -4339,6 +4323,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
        case AST_CONTROL_MCID:
        case AST_CONTROL_UPDATE_RTP_PEER:
        case AST_CONTROL_PVT_CAUSE_CODE:
+       case AST_CONTROL_MASQUERADE_NOTIFY:
        case AST_CONTROL_STREAM_STOP:
        case AST_CONTROL_STREAM_SUSPEND:
        case AST_CONTROL_STREAM_REVERSE:
@@ -4505,7 +4490,9 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        ast_channel_lock(chan);
 
        /* Don't bother if the channel is about to go away, anyway. */
-       if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+       if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
+                       || ast_check_hangup(chan))
+               && condition != AST_CONTROL_MASQUERADE_NOTIFY) {
                res = -1;
                goto indicate_cleanup;
        }
@@ -4653,6 +4640,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        case AST_CONTROL_AOC:
        case AST_CONTROL_END_OF_Q:
        case AST_CONTROL_MCID:
+       case AST_CONTROL_MASQUERADE_NOTIFY:
        case AST_CONTROL_UPDATE_RTP_PEER:
        case AST_CONTROL_STREAM_STOP:
        case AST_CONTROL_STREAM_SUSPEND:
@@ -4751,7 +4739,7 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
        }
 
        CHECK_BLOCKING(chan);
-       if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_FORMAT_TYPE_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;
 
                f.frametype = AST_FRAME_TEXT;
@@ -4762,7 +4750,7 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
                f.offset = 0;
                f.seqno = 0;
 
-               ast_format_set(&f.subclass.format, AST_FORMAT_T140, 0);
+               f.subclass.format = ast_format_t140;
                res = ast_channel_tech(chan)->write_text(chan, &f);
        } else if (ast_channel_tech(chan)->send_text) {
                res = ast_channel_tech(chan)->send_text(chan, text);
@@ -4859,7 +4847,7 @@ int ast_prod(struct ast_channel *chan)
        /* Send an empty audio frame to get things moving */
        if (ast_channel_state(chan) != AST_STATE_UP) {
                ast_debug(1, "Prodding channel '%s'\n", ast_channel_name(chan));
-               ast_format_copy(&a.subclass.format, ast_channel_rawwriteformat(chan));
+               a.subclass.format = ast_channel_rawwriteformat(chan);
                a.data.ptr = nothing + AST_FRIENDLY_OFFSET;
                a.src = "ast_prod"; /* this better match check in ast_write */
                if (ast_write(chan, &a))
@@ -5071,7 +5059,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                CHECK_BLOCKING(chan);
                break;
        case AST_FRAME_TEXT:
-               if (fr->subclass.integer == AST_FORMAT_T140) {
+               if (ast_format_cmp(fr->subclass.format, ast_format_t140) == AST_FORMAT_CMP_EQUAL) {
                        res = (ast_channel_tech(chan)->write_text == NULL) ? 0 :
                                ast_channel_tech(chan)->write_text(chan, fr);
                } else {
@@ -5096,34 +5084,40 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                if (ast_channel_tech(chan)->write == NULL)
                        break;  /*! \todo XXX should return 0 maybe ? */
 
-               if (ast_opt_generic_plc && fr->subclass.format.id == AST_FORMAT_SLINEAR) {
+               if (ast_opt_generic_plc && ast_format_cmp(fr->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) {
                        apply_plc(chan, 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_NOT_EQUAL) {
+               if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) == AST_FORMAT_CMP_EQUAL) {
                        f = fr;
                } else {
-                       if ((!ast_format_cap_iscompatible(ast_channel_nativeformats(chan), &fr->subclass.format)) &&
-                           (ast_format_cmp(ast_channel_writeformat(chan), &fr->subclass.format) != AST_FORMAT_CMP_EQUAL)) {
-                               char nf[512];
+                       if (ast_format_cmp(ast_channel_writeformat(chan), fr->subclass.format) != AST_FORMAT_CMP_EQUAL) {
+                               struct ast_str *codec_buf = ast_str_alloca(256);
 
                                /*
-                                * XXX Something is not right.  We are not compatible with this
-                                * frame.  Bad things can happen.  Problems range from no audio,
-                                * one-way audio, to unexplained line hangups.  As a last resort
-                                * try to adjust the format.  Ideally, we do not want to do this
-                                * because it indicates a deeper problem.  For now, we log these
-                                * events to reduce user impact and help identify the problem
-                                * areas.
+                                * We are not setup to write this frame.  Things may have changed
+                                * on the peer side of the world and we try to adjust the format to
+                                * make it compatible again.  However, bad things can happen if we
+                                * cannot setup a new translation path.  Problems range from no
+                                * audio, one-way audio, to garbled audio.  The best we can do is
+                                * request the call to hangup since we could not make it compatible.
+                                *
+                                * Being continuously spammed by this message likely indicates a
+                                * problem with the peer because it cannot make up its mind about
+                                * which format to use.
                                 */
-                               ast_log(LOG_WARNING, "Codec mismatch on channel %s setting write format to %s from %s native formats %s\n",
-                                       ast_channel_name(chan), ast_getformatname(&fr->subclass.format), ast_getformatname(ast_channel_writeformat(chan)),
-                                       ast_getformatname_multiple(nf, sizeof(nf), ast_channel_nativeformats(chan)));
-                               ast_set_write_format_by_id(chan, fr->subclass.format.id);
+                               ast_debug(1, "Channel %s changing write format from %s to %s, native formats %s\n",
+                                       ast_channel_name(chan),
+                                       ast_format_get_name(ast_channel_writeformat(chan)),
+                                       ast_format_get_name(fr->subclass.format),
+                                       ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf));
+                               if (ast_set_write_format(chan, fr->subclass.format)) {
+                                       /* Could not handle the new write format.  Induce a hangup. */
+                                       break;
+                               }
                        }
-
-                       f = (ast_channel_writetrans(chan)) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
+                       f = ast_channel_writetrans(chan) ? ast_translate(ast_channel_writetrans(chan), fr, 0) : fr;
                }
 
                if (!f) {
@@ -5188,7 +5182,9 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 #ifndef MONITOR_CONSTANT_DELAY
                                int jump = ast_channel_insmpl(chan) - ast_channel_outsmpl(chan) - 4 * cur->samples;
                                if (jump >= 0) {
-                                       jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)), ast_format_rate(&f->subclass.format), ast_format_rate(&ast_channel_monitor(chan)->read_stream->fmt->format));
+                                       jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)),
+                                                                ast_format_get_sample_rate(f->subclass.format),
+                                                                ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
                                        if (ast_seekstream(ast_channel_monitor(chan)->write_stream, jump, SEEK_FORCECUR) == -1) {
                                                ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
                                        }
@@ -5197,7 +5193,9 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                                        ast_channel_outsmpl_set(chan, ast_channel_outsmpl(chan) + cur->samples);
                                }
 #else
-                               int jump = calc_monitor_jump((ast_channel_insmpl(chan) - ast_channel_outsmpl(chan)), ast_format_rate(f->subclass.codec), ast_format_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
+                               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(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) {
                                                ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
@@ -5279,52 +5277,129 @@ done:
        return res;
 }
 
-struct set_format_trans_access {
-       struct ast_trans_pvt *(*get)(const struct ast_channel *chan);
-       void (*set)(struct ast_channel *chan, struct ast_trans_pvt *value);
+int ast_set_read_format_path(struct ast_channel *chan, struct ast_format *raw_format, struct ast_format *core_format)
+{
+       struct ast_trans_pvt *trans_old;
+       struct ast_trans_pvt *trans_new;
+
+       if (ast_format_cmp(ast_channel_rawreadformat(chan), raw_format) == AST_FORMAT_CMP_EQUAL
+               && ast_format_cmp(ast_channel_readformat(chan), core_format) == AST_FORMAT_CMP_EQUAL) {
+               /* Nothing to setup */
+               return 0;
+       }
+
+       ast_debug(1, "Channel %s setting read format path: %s -> %s\n",
+               ast_channel_name(chan),
+               ast_format_get_name(raw_format),
+               ast_format_get_name(core_format));
+
+       /* Setup new translation path. */
+       if (ast_format_cmp(raw_format, core_format) != AST_FORMAT_CMP_EQUAL) {
+               trans_new = ast_translator_build_path(core_format, raw_format);
+               if (!trans_new) {
+                       return -1;
+               }
+       } else {
+               /* No translation needed. */
+               trans_new = NULL;
+       }
+       trans_old = ast_channel_readtrans(chan);
+       if (trans_old) {
+               ast_translator_free_path(trans_old);
+       }
+       ast_channel_readtrans_set(chan, trans_new);
+       ast_channel_set_rawreadformat(chan, raw_format);
+       ast_channel_set_readformat(chan, core_format);
+       return 0;
+}
+
+struct set_format_access {
+       const char *direction;
+       struct ast_trans_pvt *(*get_trans)(const struct ast_channel *chan);
+       void (*set_trans)(struct ast_channel *chan, struct ast_trans_pvt *value);
+       struct ast_format *(*get_format)(struct ast_channel *chan);
+       void (*set_format)(struct ast_channel *chan, struct ast_format *format);
+       struct ast_format *(*get_rawformat)(struct ast_channel *chan);
+       void (*set_rawformat)(struct ast_channel *chan, struct ast_format *format);
+       int setoption;
 };
 
-static const struct set_format_trans_access set_format_readtrans = {
-       .get = ast_channel_readtrans,
-       .set = ast_channel_readtrans_set,
+static const struct set_format_access set_format_access_read = {
+       .direction = "read",
+       .get_trans = ast_channel_readtrans,
+       .set_trans = ast_channel_readtrans_set,
+       .get_format = ast_channel_readformat,
+       .set_format = ast_channel_set_readformat,
+       .get_rawformat = ast_channel_rawreadformat,
+       .set_rawformat = ast_channel_set_rawreadformat,
+       .setoption = AST_OPTION_FORMAT_READ,
 };
 
-static const struct set_format_trans_access set_format_writetrans = {
-       .get = ast_channel_writetrans,
-       .set = ast_channel_writetrans_set,
+static const struct set_format_access set_format_access_write = {
+       .direction = "write",
+       .get_trans = ast_channel_writetrans,
+       .set_trans = ast_channel_writetrans_set,
+       .get_format = ast_channel_writeformat,
+       .set_format = ast_channel_set_writeformat,
+       .get_rawformat = ast_channel_rawwriteformat,
+       .set_rawformat = ast_channel_set_rawwriteformat,
+       .setoption = AST_OPTION_FORMAT_WRITE,
 };
 
-static int set_format(struct ast_channel *chan,
-       struct ast_format_cap *cap_set,
-       struct ast_format *rawformat,
-       struct ast_format *format,
-       const struct set_format_trans_access *trans,
-       const int direction)
+static int set_format(struct ast_channel *chan, struct ast_format_cap *cap_set, const int direction)
 {
        struct ast_trans_pvt *trans_pvt;
        struct ast_format_cap *cap_native;
-       struct ast_format best_set_fmt;
-       struct ast_format best_native_fmt;
+       const struct set_format_access *access;
+       struct ast_format *rawformat;
+       struct ast_format *format;
+       RAII_VAR(struct ast_format *, best_set_fmt, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_format *, best_native_fmt, NULL, ao2_cleanup);
        int res;
 
-       ast_best_codec(cap_set, &best_set_fmt);
+       if (!direction) {
+               /* reading */
+               access = &set_format_access_read;
+       } else {
+               /* writing */
+               access = &set_format_access_write;
+       }
+
+       best_set_fmt = ast_format_cap_get_best_by_type(cap_set, AST_MEDIA_TYPE_AUDIO);
+       if (!best_set_fmt) {
+               /*
+                * Not setting any audio formats?
+                * Assume a call without any sounds (video, text)
+                */
+               return 0;
+       }
 
        /* See if the underlying channel driver is capable of performing transcoding for us */
-       if (!ast_channel_setoption(chan, direction ? AST_OPTION_FORMAT_WRITE : AST_OPTION_FORMAT_READ, &best_set_fmt, sizeof(best_set_fmt), 0)) {
-               ast_debug(1, "Channel driver natively set channel %s to %s format %s\n", ast_channel_name(chan),
-                         direction ? "write" : "read", ast_getformatname(&best_set_fmt));
+       res = ast_channel_setoption(chan, access->setoption,
+               &best_set_fmt, sizeof(best_set_fmt), 0);
+       if (!res) {
+               ast_debug(1, "Channel driver natively set channel %s to %s format %s\n",
+                       ast_channel_name(chan), access->direction, ast_format_get_name(best_set_fmt));
 
                ast_channel_lock(chan);
-               ast_format_copy(format, &best_set_fmt);
-               ast_format_copy(rawformat, &best_set_fmt);
-               ast_format_cap_set(ast_channel_nativeformats(chan), &best_set_fmt);
-               ast_channel_unlock(chan);
+               cap_native = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+               if (!cap_native
+                       || ast_format_cap_append(cap_native, best_set_fmt, 0)) {
+                       ast_channel_unlock(chan);
+                       ao2_cleanup(cap_native);
+                       return -1;
+               }
+               ast_channel_nativeformats_set(chan, cap_native);
+               ao2_cleanup(cap_native);
+               access->set_format(chan, best_set_fmt);
+               access->set_rawformat(chan, best_set_fmt);
 
-               trans_pvt = trans->get(chan);
+               trans_pvt = access->get_trans(chan);
                if (trans_pvt) {
                        ast_translator_free_path(trans_pvt);
-                       trans->set(chan, NULL);
+                       access->set_trans(chan, NULL);
                }
+               ast_channel_unlock(chan);
 
                /* If there is a generator on the channel, it needs to know about this
                 * change if it is the write format. */
@@ -5332,15 +5407,23 @@ static int set_format(struct ast_channel *chan,
                        generator_write_format_change(chan);
                }
 
-               ast_channel_lock(chan);
-               ast_channel_publish_snapshot(chan);
-               ast_channel_unlock(chan);
-
                return 0;
        }
 
        ast_channel_lock(chan);
+
+       format = access->get_format(chan);
+       rawformat = access->get_rawformat(chan);
+       ast_assert(format != NULL);
+       ast_assert(rawformat != NULL);
+
        cap_native = ast_channel_nativeformats(chan);
+       if (ast_format_cap_empty(cap_native)) {
+               ast_channel_unlock(chan);
+               ast_log(LOG_ERROR, "Unable to set format because channel %s supports no formats\n",
+                               ast_channel_name(chan));
+               return -1;
+       }
 
        /* Find a translation path from the native format to one of the desired formats */
        if (!direction) {
@@ -5351,40 +5434,37 @@ static int set_format(struct ast_channel *chan,
                res = ast_translator_best_choice(cap_native, cap_set, &best_native_fmt, &best_set_fmt);
        }
        if (res < 0) {
-               char from[200];
-               char to[200];
+               struct ast_str *codec_native = ast_str_alloca(256);
+               struct ast_str *codec_set = ast_str_alloca(256);
 
-               ast_getformatname_multiple(from, sizeof(from), cap_native);
+               ast_format_cap_get_names(cap_native, &codec_native);
                ast_channel_unlock(chan);
-               ast_getformatname_multiple(to, sizeof(to), cap_set);
+               ast_format_cap_get_names(cap_set, &codec_set);
 
-               ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n",
-                       from, to);
+               ast_log(LOG_WARNING, "Unable to find a codec translation path: %s -> %s\n",
+                       ast_str_buffer(direction ? codec_set : codec_native),
+                       ast_str_buffer(direction ? codec_native : codec_set));
                return -1;
        }
 
        /* Now we have a good choice for both. */
-       if ((ast_format_cmp(rawformat, &best_native_fmt) != AST_FORMAT_CMP_NOT_EQUAL) &&
-               (ast_format_cmp(format, &best_set_fmt) != AST_FORMAT_CMP_NOT_EQUAL) &&
-               ((ast_format_cmp(rawformat, format) != AST_FORMAT_CMP_NOT_EQUAL) || trans->get(chan))) {
+       if ((ast_format_cmp(rawformat, best_native_fmt) != AST_FORMAT_CMP_NOT_EQUAL) &&
+               (ast_format_cmp(format, best_set_fmt) != AST_FORMAT_CMP_NOT_EQUAL) &&
+               ((ast_format_cmp(rawformat, format) != AST_FORMAT_CMP_NOT_EQUAL) || access->get_trans(chan))) {
                /* the channel is already in these formats, so nothing to do */
                ast_channel_unlock(chan);
                return 0;
        }
 
-       ast_format_copy(rawformat, &best_native_fmt);
-       /* User perspective is fmt */
-       ast_format_copy(format, &best_set_fmt);
-
        /* Free any translation we have right now */
-       trans_pvt = trans->get(chan);
+       trans_pvt = access->get_trans(chan);
        if (trans_pvt) {
                ast_translator_free_path(trans_pvt);
-               trans->set(chan, NULL);
+               access->set_trans(chan, NULL);
        }
 
        /* Build a translation path from the raw format to the desired format */
-       if (ast_format_cmp(format, rawformat) != AST_FORMAT_CMP_NOT_EQUAL) {
+       if (ast_format_cmp(best_set_fmt, best_native_fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
                /*
                 * If we were able to swap the native format to the format that
                 * has been requested, then there is no need to try to build
@@ -5394,20 +5474,27 @@ static int set_format(struct ast_channel *chan,
        } else {
                if (!direction) {
                        /* reading */
-                       trans_pvt = ast_translator_build_path(format, rawformat);
+                       trans_pvt = ast_translator_build_path(best_set_fmt, best_native_fmt);
                } else {
                        /* writing */
-                       trans_pvt = ast_translator_build_path(rawformat, format);
+                       trans_pvt = ast_translator_build_path(best_native_fmt, best_set_fmt);
                }
-               trans->set(chan, trans_pvt);
+               access->set_trans(chan, trans_pvt);
                res = trans_pvt ? 0 : -1;
        }
-       ast_channel_unlock(chan);
 
-       ast_debug(1, "Set channel %s to %s format %s\n",
-               ast_channel_name(chan),
-               direction ? "write" : "read",
-               ast_getformatname(&best_set_fmt));
+       if (!res) {
+               access->set_format(chan, best_set_fmt);
+               access->set_rawformat(chan, best_native_fmt);
+
+               ast_debug(1, "Channel %s setting %s format path: %s -> %s\n",
+                       ast_channel_name(chan),
+                       access->direction,
+                       ast_format_get_name(direction ? best_set_fmt : best_native_fmt),
+                       ast_format_get_name(direction ? best_native_fmt : best_set_fmt));
+       }
+
+       ast_channel_unlock(chan);
 
        /* If there is a generator on the channel, it needs to know about this
         * change if it is the write format. */
@@ -5415,117 +5502,53 @@ static int set_format(struct ast_channel *chan,
                generator_write_format_change(chan);
        }
 
-       ast_channel_lock(chan);
-       ast_channel_publish_snapshot(chan);
-       ast_channel_unlock(chan);
-
        return res;
 }
 
 int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
 {
-       struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
+       struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
        int res;
 
-       if (!cap) {
-               return -1;
-       }
-       ast_format_cap_add(cap, format);
-
-       res = set_format(chan,
-               cap,
-               ast_channel_rawreadformat(chan),
-               ast_channel_readformat(chan),
-               &set_format_readtrans,
-               0);
-
-       ast_format_cap_destroy(cap);
-       return res;
-}
-
-int ast_set_read_format_by_id(struct ast_channel *chan, enum ast_format_id id)
-{
-       struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
-       struct ast_format tmp_format;
-       int res;
+       ast_assert(format != NULL);
 
        if (!cap) {
                return -1;
        }
-       ast_format_cap_add(cap, ast_format_set(&tmp_format, id, 0));
+       ast_format_cap_append(cap, format, 0);
 
-       res = set_format(chan,
-               cap,
-               ast_channel_rawreadformat(chan),
-               ast_channel_readformat(chan),
-               &set_format_readtrans,
-               0);
+       res = set_format(chan, cap, 0);
 
-       ast_format_cap_destroy(cap);
+       ao2_cleanup(cap);
        return res;
 }
 
 int ast_set_read_format_from_cap(struct ast_channel *chan, struct ast_format_cap *cap)
 {
-       return set_format(chan,
-               cap,
-               ast_channel_rawreadformat(chan),
-               ast_channel_readformat(chan),
-               &set_format_readtrans,
-               0);
+       return set_format(chan, cap, 0);
 }
 
 int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
 {
-       struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
+       struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
        int res;
 
-       if (!cap) {
-               return -1;
-       }
-       ast_format_cap_add(cap, format);
-
-       res = set_format(chan,
-               cap,
-               ast_channel_rawwriteformat(chan),
-               ast_channel_writeformat(chan),
-               &set_format_writetrans,
-               1);
-
-       ast_format_cap_destroy(cap);
-       return res;
-}
-
-int ast_set_write_format_by_id(struct ast_channel *chan, enum ast_format_id id)
-{
-       struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
-       struct ast_format tmp_format;
-       int res;
+       ast_assert(format != NULL);
 
        if (!cap) {
                return -1;
        }
-       ast_format_cap_add(cap, ast_format_set(&tmp_format, id, 0));
+       ast_format_cap_append(cap, format, 0);
 
-       res = set_format(chan,
-               cap,
-               ast_channel_rawwriteformat(chan),
-               ast_channel_writeformat(chan),
-               &set_format_writetrans,
-               1);
+       res = set_format(chan, cap, 1);
 
-       ast_format_cap_destroy(cap);
+       ao2_cleanup(cap);
        return res;
 }
 
 int ast_set_write_format_from_cap(struct ast_channel *chan, struct ast_format_cap *cap)
 {
-       return set_format(chan,
-               cap,
-               ast_channel_rawwriteformat(chan),
-               ast_channel_writeformat(chan),
-               &set_format_writetrans,
-               1);
+       return set_format(chan, cap, 1);
 }
 
 const char *ast_channel_reason2str(int reason)
@@ -5641,9 +5664,12 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
                if (oh->parent_channel) {
                        call_forward_inherit(new_chan, oh->parent_channel, orig);
                }
-               if (oh->account) {
+               if (!ast_strlen_zero(oh->account)) {
                        ast_channel_lock(new_chan);
+                       ast_channel_stage_snapshot(new_chan);
                        ast_channel_accountcode_set(new_chan, oh->account);
+                       ast_channel_peeraccount_set(new_chan, oh->account);
+                       ast_channel_stage_snapshot_done(new_chan);
                        ast_channel_unlock(new_chan);
                }
        } else if (caller) { /* no outgoing helper so use caller if available */
@@ -5652,9 +5678,9 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
        ast_set_flag(ast_channel_flags(new_chan), AST_FLAG_ORIGINATED);
 
        ast_channel_lock_both(orig, new_chan);
-       ast_channel_accountcode_set(new_chan, ast_channel_accountcode(orig));
        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));
+       ast_channel_req_accountcodes(new_chan, orig, AST_CHANNEL_REQUESTOR_REPLACEMENT);
        ast_channel_unlock(new_chan);
        ast_channel_unlock(orig);
 
@@ -5717,9 +5743,12 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
                        ast_channel_unlock(oh->parent_channel);
                        ast_channel_unlock(chan);
                }
-               if (oh->account) {
+               if (!ast_strlen_zero(oh->account)) {
                        ast_channel_lock(chan);
+                       ast_channel_stage_snapshot(chan);
                        ast_channel_accountcode_set(chan, oh->account);
+                       ast_channel_peeraccount_set(chan, oh->account);
+                       ast_channel_stage_snapshot_done(chan);
                        ast_channel_unlock(chan);
                }
        }
@@ -5747,6 +5776,12 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
                connected.id.name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
        }
        ast_channel_set_connected_line(chan, &connected, NULL);
+       if (requestor) {
+               ast_channel_lock_both(chan, (struct ast_channel *) requestor);
+               ast_channel_req_accountcodes(chan, requestor, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
+               ast_channel_unlock(chan);
+               ast_channel_unlock((struct ast_channel *) requestor);
+       }
 
        if (ast_call(chan, addr, 0)) {  /* ast_call failed... */
                ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, addr);
@@ -5923,26 +5958,29 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
 
        AST_RWLIST_TRAVERSE(&backends, chan, list) {
                struct ast_format_cap *tmp_cap;
-               struct ast_format tmp_fmt;
-               struct ast_format best_audio_fmt;
+               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;
 
-               ast_format_clear(&best_audio_fmt);
                /* find the best audio format to use */
-               if ((tmp_cap = ast_format_cap_get_type(request_cap, AST_FORMAT_TYPE_AUDIO))) {
+               tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+               if (tmp_cap) {
+                       ast_format_cap_append_from_cap(tmp_cap, request_cap, AST_MEDIA_TYPE_AUDIO);
                        /* We have audio - is it possible to connect the various calls to each other?
                                (Avoid this check for calls without audio, like text+video calls)
                        */
                        res = ast_translator_best_choice(tmp_cap, chan->tech->capabilities, &tmp_fmt, &best_audio_fmt);
-                       ast_format_cap_destroy(tmp_cap);
+                       ao2_ref(tmp_cap, -1);
                        if (res < 0) {
-                               char tmp1[256], tmp2[256];
+                               struct ast_str *tech_codecs = ast_str_alloca(64);
+                               struct ast_str *request_codecs = ast_str_alloca(64);
+
                                ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %s) to %s\n", type,
-                                       ast_getformatname_multiple(tmp1, sizeof(tmp1), chan->tech->capabilities),
-                                       ast_getformatname_multiple(tmp2, sizeof(tmp2), request_cap));
+                                       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);
                                return NULL;
@@ -5953,32 +5991,39 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
                        return NULL;
 
                /* XXX Only the audio format calculated as being the best for translation
-                * purposes is used for the request. This needs to be re-evaluated.  It may be
-                * a better choice to send all the audio formats capable of being translated
-                * during the request and allow the channel drivers to pick the best one. */
-               if (!(joint_cap = ast_format_cap_dup(request_cap))) {
+                * purposes is used for the request. This is because we don't have the ability
+                * to signal to the initiator which one of their codecs that was offered is
+                * the one that was selected, particularly in a chain of Local channels.
+                */
+               joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+               if (!joint_cap) {
                        return NULL;
                }
-               ast_format_cap_remove_bytype(joint_cap, AST_FORMAT_TYPE_AUDIO);
-               ast_format_cap_add(joint_cap, &best_audio_fmt);
+               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);
 
                if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) {
-                       ast_format_cap_destroy(joint_cap);
+                       ao2_ref(joint_cap, -1);
                        return NULL;
                }
 
-               /* Set newly created channel callid to same as the requestor */
                if (requestor) {
-                       struct ast_callid *callid = ast_channel_callid(requestor);
+                       ast_callid callid;
+
+                       ast_channel_lock_both(c, (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_lock(c);
                                ast_channel_callid_set(c, callid);
-                               ast_channel_unlock(c);
-                               callid = ast_callid_unref(callid);
                        }
+
+                       ast_channel_unlock(c);
+                       ast_channel_unlock((struct ast_channel *) requestor);
                }
 
-               joint_cap = ast_format_cap_destroy(joint_cap);
+               ao2_ref(joint_cap, -1);
 
                if (set_security_requirements(requestor, c)) {
                        ast_log(LOG_WARNING, "Setting security requirements failed\n");
@@ -5998,6 +6043,88 @@ struct ast_channel *ast_request(const char *type, struct ast_format_cap *request
        return NULL;
 }
 
+/*!
+ * \internal
+ * \brief Setup new channel accountcodes from the requestor channel after ast_request().
+ * \since 13.0.0
+ *
+ * \param chan New channel to get accountcodes setup.
+ * \param requestor Requesting channel to get accountcodes from.
+ * \param relationship What the new channel was created for.
+ * \param precious TRUE if pre-existing accountcodes on chan will not be overwritten.
+ *
+ * \pre The chan and requestor channels are already locked.
+ *
+ * \return Nothing
+ */
+static void channel_req_accountcodes(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship, int precious)
+{
+       /*
+        * The primary reason for the existence of this function is
+        * so local channels can propagate accountcodes to the ;2
+        * channel before ast_call().
+        *
+        * The secondary reason is to propagate the CHANNEL(peeraccount)
+        * value set before Dial, FollowMe, and Queue while maintaining
+        * the historic straight across accountcode propagation as a
+        * fallback.
+        */
+       switch (relationship) {
+       case AST_CHANNEL_REQUESTOR_BRIDGE_PEER:
+               /* Crossover the requestor's accountcode and peeraccount */
+               if (!precious || ast_strlen_zero(ast_channel_accountcode(chan))) {
+                       /*
+                        * The newly created channel does not have an accountcode
+                        * or we don't care.
+                        */
+                       if (!ast_strlen_zero(ast_channel_peeraccount(requestor))) {
+                               /*
+                                * Set it to the requestor's peeraccount.  This allows the
+                                * dialplan to indicate the accountcode to use when dialing
+                                * by setting CHANNEL(peeraccount).
+                                */
+                               ast_channel_accountcode_set(chan, ast_channel_peeraccount(requestor));
+                       } else if (!precious
+                               && !ast_strlen_zero(ast_channel_accountcode(requestor))) {
+                               /*
+                                * Fallback to the historic propagation and set it to the
+                                * requestor's accountcode.
+                                */
+                               ast_channel_accountcode_set(chan, ast_channel_accountcode(requestor));
+                       }
+               }
+               if (!ast_strlen_zero(ast_channel_accountcode(requestor))) {
+                       ast_channel_peeraccount_set(chan, ast_channel_accountcode(requestor));
+               }
+               break;
+       case AST_CHANNEL_REQUESTOR_REPLACEMENT:
+               /* Pass the requestor's accountcode and peeraccount straight. */
+               if (!precious || ast_strlen_zero(ast_channel_accountcode(chan))) {
+                       /*
+                        * The newly created channel does not have an accountcode
+                        * or we don't care.
+                        */
+                       if (!ast_strlen_zero(ast_channel_accountcode(requestor))) {
+                               ast_channel_accountcode_set(chan, ast_channel_accountcode(requestor));
+                       }
+               }
+               if (!ast_strlen_zero(ast_channel_peeraccount(requestor))) {
+                       ast_channel_peeraccount_set(chan, ast_channel_peeraccount(requestor));
+               }
+               break;
+       }
+}
+
+void ast_channel_req_accountcodes(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
+{
+       channel_req_accountcodes(chan, requestor, relationship, 0);
+}
+
+void ast_channel_req_accountcodes_precious(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
+{
+       channel_req_accountcodes(chan, requestor, relationship, 1);
+}
+
 int ast_pre_call(struct ast_channel *chan, const char *sub_args)
 {
        int (*pre_call)(struct ast_channel *chan, const char *sub_args);
@@ -6175,26 +6302,28 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a
 {
        struct ast_format_cap *src_cap;
        struct ast_format_cap *dst_cap;
-       struct ast_format best_src_fmt;
-       struct ast_format best_dst_fmt;
+       RAII_VAR(struct ast_format *, best_src_fmt, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_format *, best_dst_fmt, NULL, ao2_cleanup);
        int no_path;
 
-       ast_channel_lock_both(from, to);
+       /*
+        * We cannot short circuit this code because it is possible to ask
+        * to make compatible two channels that are "compatible" because
+        * they already have translation paths setup but together make for
+        * a sub-optimal path.  e.g., The From channel has g722 -> ulaw
+        * and the To channel has ulaw -> g722.  They are "compatible" but
+        * together the translations are unnecessary and the audio loses
+        * fidelity in the process.
+        */
 
-       if ((ast_format_cmp(ast_channel_readformat(from), ast_channel_writeformat(to)) != AST_FORMAT_CMP_NOT_EQUAL) &&
-               (ast_format_cmp(ast_channel_readformat(to), ast_channel_writeformat(from)) != AST_FORMAT_CMP_NOT_EQUAL)) {
-               /* Already compatible!  Moving on ... */
-               ast_channel_unlock(to);
-               ast_channel_unlock(from);
-               return 0;
-       }
+       ast_channel_lock_both(from, to);
 
        src_cap = ast_channel_nativeformats(from); /* shallow copy, do not destroy */
        dst_cap = ast_channel_nativeformats(to);   /* shallow copy, do not destroy */
 
        /* If there's no audio in this call, don't bother with trying to find a translation path */
-       if (!ast_format_cap_has_type(src_cap, AST_FORMAT_TYPE_AUDIO)
-               || !ast_format_cap_has_type(dst_cap, AST_FORMAT_TYPE_AUDIO)) {
+       if (!ast_format_cap_has_type(src_cap, AST_MEDIA_TYPE_AUDIO)
+               || !ast_format_cap_has_type(dst_cap, AST_MEDIA_TYPE_AUDIO)) {
                ast_channel_unlock(to);
                ast_channel_unlock(from);
                return 0;
@@ -6217,28 +6346,28 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a
         * no direct conversion available. If generic PLC is
         * desired, then transcoding via SLINEAR is a requirement
         */
-       if (ast_format_cmp(&best_dst_fmt, &best_src_fmt) == AST_FORMAT_CMP_NOT_EQUAL
+       if (ast_format_cmp(best_dst_fmt, best_src_fmt) == AST_FORMAT_CMP_NOT_EQUAL
                && (ast_opt_generic_plc || ast_opt_transcode_via_slin)) {
-               int use_slin = (ast_format_is_slinear(&best_src_fmt)
-                       || ast_format_is_slinear(&best_dst_fmt)) ? 1 : 0;
+               int use_slin = (ast_format_cache_is_slinear(best_src_fmt)
+                       || ast_format_cache_is_slinear(best_dst_fmt)) ? 1 : 0;
 
-               if (use_slin || ast_translate_path_steps(&best_dst_fmt, &best_src_fmt) != 1) {
-                       int best_sample_rate = (ast_format_rate(&best_src_fmt) > ast_format_rate(&best_dst_fmt)) ?
-                               ast_format_rate(&best_src_fmt) : ast_format_rate(&best_dst_fmt);
+               if (use_slin || ast_translate_path_steps(best_dst_fmt, best_src_fmt) != 1) {
+                       int best_sample_rate = (ast_format_get_sample_rate(best_src_fmt) > ast_format_get_sample_rate(best_dst_fmt)) ?
+                               ast_format_get_sample_rate(best_src_fmt) : ast_format_get_sample_rate(best_dst_fmt);
 
                        /* pick the best signed linear format based upon what preserves the sample rate the best. */
-                       ast_format_set(&best_src_fmt, ast_format_slin_by_rate(best_sample_rate), 0);
+                       ao2_replace(best_src_fmt, ast_format_cache_get_slin_by_rate(best_sample_rate));
                }
        }
 
-       if (ast_set_read_format(from, &best_src_fmt)) {
+       if (ast_set_read_format(from, best_src_fmt)) {
                ast_log(LOG_WARNING, "Unable to set read format on channel %s to %s\n",
-                       ast_channel_name(from), ast_getformatname(&best_src_fmt));
+                       ast_channel_name(from), ast_format_get_name(best_src_fmt));
                return -1;
        }
-       if (ast_set_write_format(to, &best_src_fmt)) {
+       if (ast_set_write_format(to, best_src_fmt)) {
                ast_log(LOG_WARNING, "Unable to set write format on channel %s to %s\n",
-                       ast_channel_name(to), ast_getformatname(&best_src_fmt));
+                       ast_channel_name(to), ast_format_get_name(best_src_fmt));
                return -1;
        }
        return 0;
@@ -6295,41 +6424,42 @@ void ast_change_name(struct ast_channel *chan, const char *newname)
 
 void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
 {
-       struct ast_var_t *current, *newvar;
+       struct ast_var_t *current;
+       struct ast_var_t *newvar;
        const char *varname;
+       int vartype;
 
        AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) parent), current, entries) {
-               int vartype = 0;
-
                varname = ast_var_full_name(current);
-               if (!varname)
+               if (!varname) {
                        continue;
+               }
 
+               vartype = 0;
                if (varname[0] == '_') {
                        vartype = 1;
-                       if (varname[1] == '_')
+                       if (varname[1] == '_') {
                                vartype = 2;
+                       }
                }
 
                switch (vartype) {
                case 1:
                        newvar = ast_var_assign(&varname[1], ast_var_value(current));
-                       if (newvar) {
-                               AST_LIST_INSERT_TAIL(ast_channel_varshead(child), newvar, entries);
-                               ast_debug(1, "Inheriting variable %s from %s to %s.\n",
-                                       ast_var_name(newvar), ast_channel_name(parent), ast_channel_name(child));
-                       }
                        break;
                case 2:
                        newvar = ast_var_assign(varname, ast_var_value(current));
-                       if (newvar) {
-                               AST_LIST_INSERT_TAIL(ast_channel_varshead(child), newvar, entries);
-                               ast_debug(1, "Inheriting variable %s from %s to %s.\n",
-                                       ast_var_name(newvar), ast_channel_name(parent), ast_channel_name(child));
-                       }
                        break;
                default:
-                       break;
+                       continue;
+               }
+               if (newvar) {
+                       ast_debug(1, "Inheriting variable %s from %s to %s.\n",
+                               ast_var_full_name(newvar), ast_channel_name(parent),
+                               ast_channel_name(child));
+                       AST_LIST_INSERT_TAIL(ast_channel_varshead(child), newvar, entries);
+                       ast_channel_publish_varset(child, ast_var_full_name(newvar),
+                               ast_var_value(newvar));
                }
        }
 }
@@ -6398,9 +6528,10 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
                struct ast_party_redirecting redirecting;
        } exchange;
        struct ast_channel *bridged;
-       struct ast_format rformat;
-       struct ast_format wformat;
-       struct ast_format tmp_format;
+       struct ast_format *rformat;
+       struct ast_format *wformat;
+       struct ast_format *tmp_format;
+       struct ast_format_cap *tmp_cap;
        char tmp_name[AST_CHANNEL_NAME];
        char clone_sending_dtmf_digit;
        struct timeval clone_sending_dtmf_tv;
@@ -6410,6 +6541,11 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
         * original channel's backend.  While the features are nice, which is the
         * reason we're keeping it, it's still awesomely weird. XXX */
 
+       /* Indicate to each channel that a masquerade is about to begin. */
+       x = 1;
+       ast_indicate_data(original, AST_CONTROL_MASQUERADE_NOTIFY, &x, sizeof(x));
+       ast_indicate_data(clonechan, AST_CONTROL_MASQUERADE_NOTIFY, &x, sizeof(x));
+
        /*
         * The container lock is necessary for proper locking order
         * because the channels must be unlinked to change their
@@ -6450,15 +6586,16 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        /* Start the masquerade channel contents rearangement. */
        ast_channel_lock_both(original, clonechan);
 
-       ast_debug(4, "Actually Masquerading %s(%u) into the structure of %s(%u)\n",
-               ast_channel_name(clonechan), ast_channel_state(clonechan), ast_channel_name(original), ast_channel_state(original));
+       ast_debug(1, "Actually Masquerading %s(%u) into the structure of %s(%u)\n",
+               ast_channel_name(clonechan), ast_channel_state(clonechan),
+               ast_channel_name(original), ast_channel_state(original));
 
        /*
         * Remember the original read/write formats.  We turn off any
         * translation on either one
         */
-       ast_format_copy(&rformat, ast_channel_readformat(original));
-       ast_format_copy(&wformat, ast_channel_writeformat(original));
+       rformat = ao2_bump(ast_channel_readformat(original));
+       wformat = ao2_bump(ast_channel_writeformat(original));
        free_translation(clonechan);
        free_translation(original);
 
@@ -6473,6 +6610,9 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
         */
        ast_channel_internal_swap_uniqueid_and_linkedid(clonechan, original);
 
+       /* Make sure the Stasis topic on the channel is updated appropriately */
+       ast_channel_internal_swap_topics(clonechan, original);
+
        /* Swap channel names. This uses ast_channel_name_set directly, so we
         * don't get any spurious rename events.
         */
@@ -6519,13 +6659,15 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        }
 
        /* Swap the raw formats */
-       ast_format_copy(&tmp_format, ast_channel_rawreadformat(original));
-       ast_format_copy(ast_channel_rawreadformat(original), ast_channel_rawreadformat(clonechan));
-       ast_format_copy(ast_channel_rawreadformat(clonechan), &tmp_format);
+       tmp_format = ao2_bump(ast_channel_rawreadformat(original));
+       ast_channel_set_rawreadformat(original, ast_channel_rawreadformat(clonechan));
+       ast_channel_set_rawreadformat(clonechan, tmp_format);
+       ao2_cleanup(tmp_format);
 
-       ast_format_copy(&tmp_format, ast_channel_rawwriteformat(original));
-       ast_format_copy(ast_channel_rawwriteformat(original), ast_channel_rawwriteformat(clonechan));
-       ast_format_copy(ast_channel_rawwriteformat(clonechan), &tmp_format);
+       tmp_format = ao2_bump(ast_channel_rawwriteformat(original));
+       ast_channel_set_rawwriteformat(original, ast_channel_rawwriteformat(clonechan));
+       ast_channel_set_rawwriteformat(clonechan, tmp_format);
+       ao2_cleanup(tmp_format);
 
        ast_channel_softhangup_internal_flag_set(clonechan, AST_SOFTHANGUP_DEV);
 
@@ -6575,20 +6717,45 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        *ast_channel_hangup_handlers(original) = *ast_channel_hangup_handlers(clonechan);
        *ast_channel_hangup_handlers(clonechan) = exchange.handlers;
 
-       /* Move data stores over */
+       /* Call fixup handlers for the clone chan */
        if (AST_LIST_FIRST(ast_channel_datastores(clonechan))) {
                struct ast_datastore *ds;
                /* We use a safe traversal here because some fixup routines actually
                 * remove the datastore from the list and free them.
                 */
                AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_datastores(clonechan), ds, entry) {
-                       if (ds->info->chan_fixup)
+                       if (ds->info->chan_fixup) {
                                ds->info->chan_fixup(ds->data, clonechan, original);
+                       }
+               }
+               AST_LIST_TRAVERSE_SAFE_END;
+       }
+
+       /* Call breakdown handlers for the original chan */
+       if (AST_LIST_FIRST(ast_channel_datastores(original))) {
+               struct ast_datastore *ds;
+               /* We use a safe traversal here because some breakdown routines may
+                * remove the datastore from the list and free them.
+                */
+               AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_datastores(original), ds, entry) {
+                       if (ds->info->chan_breakdown) {
+                               ds->info->chan_breakdown(ds->data, clonechan, original);
+                       }
                }
                AST_LIST_TRAVERSE_SAFE_END;
+       }
+
+       /* Move data stores over */
+       if (AST_LIST_FIRST(ast_channel_datastores(clonechan))) {
                AST_LIST_APPEND_LIST(ast_channel_datastores(original), ast_channel_datastores(clonechan), entry);
        }
 
+       /* Move framehooks over */
+       ast_framehook_list_fixup(clonechan, original);
+
+       /* Move audiohooks over */
+       ast_audiohook_move_all(clonechan, original);
+
        ast_autochan_new_channel(clonechan, original);
 
        clone_variables(original, clonechan);
@@ -6647,16 +6814,21 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        ast_channel_set_fd(original, AST_TIMING_FD, ast_channel_timingfd(original));
 
        /* Our native formats are different now */
-       ast_format_cap_copy(ast_channel_nativeformats(original), ast_channel_nativeformats(clonechan));
+       tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (tmp_cap) {
+               ast_format_cap_append_from_cap(tmp_cap, ast_channel_nativeformats(clonechan), AST_MEDIA_TYPE_UNKNOWN);
+               ast_channel_nativeformats_set(original, tmp_cap);
+               ao2_ref(tmp_cap, -1);
+       }
 
        /* Context, extension, priority, app data, jump table,  remain the same */
        /* pvt switches.  pbx stays the same, as does next */
 
        /* Set the write format */
-       ast_set_write_format(original, &wformat);
+       ast_set_write_format(original, wformat);
 
        /* Set the read format */
-       ast_set_read_format(original, &rformat);
+       ast_set_read_format(original, rformat);
 
        /* Copy the music class */
        ast_channel_musicclass_set(original, ast_channel_musicclass(clonechan));
@@ -6665,7 +6837,7 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        ast_channel_accountcode_set(original, S_OR(ast_channel_accountcode(clonechan), ""));
 
        ast_debug(1, "Putting channel %s in %s/%s formats\n", ast_channel_name(original),
-               ast_getformatname(&wformat), ast_getformatname(&rformat));
+               ast_format_get_name(wformat), ast_format_get_name(rformat));
 
        /* Fixup the original clonechan's physical side */
        if (ast_channel_tech(original)->fixup && ast_channel_tech(original)->fixup(clonechan, original)) {
@@ -6692,6 +6864,19 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        ast_channel_unlock(original);
        ast_channel_unlock(clonechan);
 
+       /*
+        * Indicate to each channel that a masquerade is complete.
+        *
+        * We can still do this to clonechan even though it is a
+        * zombie because ast_indicate_data() will explicitly pass
+        * this control and ast_hangup() is held off until the
+        * ast_channel_masq() and ast_channel_masqr() pointers are
+        * cleared.
+        */
+       x = 0;
+       ast_indicate_data(original, AST_CONTROL_MASQUERADE_NOTIFY, &x, sizeof(x));
+       ast_indicate_data(clonechan, AST_CONTROL_MASQUERADE_NOTIFY, &x, sizeof(x));
+
        ast_bridge_notify_masquerade(original);
 
        if (clone_hold_state == AST_CONTROL_HOLD) {
@@ -6778,6 +6963,9 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        /* Release our held safety references. */
        ast_channel_unref(original);
        ast_channel_unref(clonechan);
+
+       ao2_cleanup(rformat);
+       ao2_cleanup(wformat);
 }
 
 void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
@@ -6922,7 +7110,7 @@ struct tonepair_state {
        int v1_2;
        int v2_2;
        int v3_2;
-       struct ast_format origwfmt;
+       struct ast_format *origwfmt;
        int pos;
        int duration;
        int modulate;
@@ -6935,8 +7123,10 @@ static void tonepair_release(struct ast_channel *chan, void *params)
 {
        struct tonepair_state *ts = params;
 
-       if (chan)
-               ast_set_write_format(chan, &ts->origwfmt);
+       if (chan) {
+               ast_set_write_format(chan, ts->origwfmt);
+       }
+       ao2_cleanup(ts->origwfmt);
        ast_free(ts);
 }
 
@@ -6945,10 +7135,12 @@ static void *tonepair_alloc(struct ast_channel *chan, void *params)
        struct tonepair_state *ts;
        struct tonepair_def *td = params;
 
-       if (!(ts = ast_calloc(1, sizeof(*ts))))
+       if (!(ts = ast_calloc(1, sizeof(*ts)))) {
                return NULL;
-       ast_format_copy(&ts->origwfmt, ast_channel_writeformat(chan));
-       if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) {
+       }
+
+       ts->origwfmt = ao2_bump(ast_channel_writeformat(chan));
+       if (ast_set_write_format(chan, ast_format_slin)) {
                ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", ast_channel_name(chan));
                tonepair_release(NULL, ts);
                ts = NULL;
@@ -7002,7 +7194,7 @@ static int tonepair_generator(struct ast_channel *chan, void *data, int len, int
                        ts->data[x] = ts->v3_1 + ts->v3_2;
        }
        ts->f.frametype = AST_FRAME_VOICE;
-       ast_format_set(&ts->f.subclass.format, AST_FORMAT_SLINEAR, 0);
+       ts->f.subclass.format = ast_format_slin;
        ts->f.datalen = len;
        ts->f.samples = samples;
        ts->f.offset = AST_FRIENDLY_OFFSET;
@@ -7314,6 +7506,7 @@ static int data_channeltypes_provider_handler(const struct ast_data_search *sear
                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);
@@ -7414,6 +7607,17 @@ static void free_channelvars(void)
        AST_RWLIST_UNLOCK(&channelvars);
 }
 
+int ast_channel_has_manager_vars(void)
+{
+       int vars_present;
+
+       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)
 {
        size_t i;
@@ -7551,7 +7755,7 @@ void ast_channels_init(void)
 
        ast_plc_reload();
 
-       ast_register_atexit(channels_shutdown);
+       ast_register_cleanup(channels_shutdown);
 
 }
 
@@ -7644,12 +7848,9 @@ void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
 {
        struct ast_variable *cur;
 
-       ast_channel_stage_snapshot(chan);
-
-       for (cur = vars; cur; cur = cur->next)
+       for (cur = vars; cur; cur = cur->next) {
                pbx_builtin_setvar_helper(chan, cur->name, cur->value);
-
-       ast_channel_stage_snapshot_done(chan);
+       }
 }
 
 static void *silence_generator_alloc(struct ast_channel *chan, void *data)
@@ -7672,7 +7873,7 @@ static int silence_generator_generate(struct ast_channel *chan, void *data, int
                .samples = samples,
                .datalen = sizeof(buf),
        };
-       ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0);
+       frame.subclass.format = ast_format_slin;
 
        memset(buf, 0, sizeof(buf));
 
@@ -7689,7 +7890,7 @@ static struct ast_generator silence_generator = {
 };
 
 struct ast_silence_generator {
-       struct ast_format old_write_format;
+       struct ast_format *old_write_format;
 };
 
 struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan)
@@ -7700,9 +7901,9 @@ struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_cha
                return NULL;
        }
 
-       ast_format_copy(&state->old_write_format, ast_channel_writeformat(chan));
+       state->old_write_format = ao2_bump(ast_channel_writeformat(chan));
 
-       if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
+       if (ast_set_write_format(chan, ast_format_slin) < 0) {
                ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n");
                ast_free(state);
                return NULL;
@@ -7746,9 +7947,11 @@ void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_sil
 
        if (deactivate_silence_generator(chan)) {
                ast_debug(1, "Stopped silence generator on '%s'\n", ast_channel_name(chan));
-               if (ast_set_write_format(chan, &state->old_write_format) < 0)
+               if (ast_set_write_format(chan, state->old_write_format) < 0) {
                        ast_log(LOG_ERROR, "Could not return write format to its original state\n");
+               }
        }
+       ao2_cleanup(state->old_write_format);
        ast_free(state);
 }
 
@@ -7849,6 +8052,7 @@ void ast_channel_set_connected_line(struct ast_channel *chan, const struct ast_p
 
        ast_channel_lock(chan);
        ast_party_connected_line_set(ast_channel_connected(chan), connected, update);
+       ast_channel_publish_snapshot(chan);
        ast_channel_unlock(chan);
 }
 
@@ -10149,13 +10353,15 @@ int ast_channel_is_bridged(const struct ast_channel *chan)
 int ast_channel_is_leaving_bridge(struct ast_channel *chan)
 {
        int hangup_flags = ast_channel_softhangup_internal_flag(chan);
-       int hangup_test = hangup_flags & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
+       int hangup_test = hangup_flags & AST_SOFTHANGUP_ASYNCGOTO;
+       int unbridge = ast_channel_unbridged(chan);
 
-       /* This function should only return true if either ASYNCGOTO
-        * or UNBRIDGE is set, or both flags are set. It should return
-        * false if any other flag is set.
+       /* This function should only return true if either the unbridged flag or
+        * the ASYNCGOTO soft hangup flag is set and when no other soft hangup
+        * flags are set. Any other soft hangup flags being set should make it
+        * return false.
         */
-       return (hangup_test && (hangup_test == hangup_flags));
+       return ((hangup_test || unbridge) && (hangup_test == hangup_flags));
 }
 
 struct ast_channel *ast_channel_bridge_peer(struct ast_channel *chan)
@@ -10196,8 +10402,8 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
                char *context;
                char *name;
                int amaflags;
-               struct ast_format readformat;
-               struct ast_format writeformat;
+               struct ast_format *readformat;
+               struct ast_format *writeformat;
        } my_vars = { 0, };
 
        ast_channel_lock(yankee);
@@ -10206,8 +10412,8 @@ 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);
-       ast_format_copy(&my_vars.writeformat, ast_channel_writeformat(yankee));
-       ast_format_copy(&my_vars.readformat, ast_channel_readformat(yankee));
+       my_vars.writeformat = ao2_bump(ast_channel_writeformat(yankee));
+       my_vars.readformat = ao2_bump(ast_channel_readformat(yankee));
        ast_channel_unlock(yankee);
 
        /* Do not hold any channel locks while calling channel_alloc() since the function
@@ -10215,12 +10421,16 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
        if (!(yanked_chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, my_vars.accountcode,
                                        my_vars.exten, my_vars.context, NULL, yankee, my_vars.amaflags,
                                        "Surrogate/%s", my_vars.name))) {
+               ao2_cleanup(my_vars.writeformat);
+               ao2_cleanup(my_vars.readformat);
                return NULL;
        }
 
        /* Make formats okay */
-       ast_format_copy(ast_channel_readformat(yanked_chan), &my_vars.readformat);
-       ast_format_copy(ast_channel_writeformat(yanked_chan), &my_vars.writeformat);
+       ast_channel_set_readformat(yanked_chan, my_vars.readformat);
+       ast_channel_set_writeformat(yanked_chan, my_vars.writeformat);
+       ao2_cleanup(my_vars.readformat);
+       ao2_cleanup(my_vars.writeformat);
 
        ast_channel_unlock(yanked_chan);
 
@@ -10298,6 +10508,13 @@ struct suppress_data {
        int framehook_id;
 };
 
+static void suppress_framehook_fixup_cb(void *data, int framehook_id, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+       struct suppress_data *suppress = data;
+
+       suppress->framehook_id = framehook_id;
+}
+
 static struct ast_frame *suppress_framehook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
 {
        struct suppress_data *suppress = data;
@@ -10349,6 +10566,7 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
                .version = AST_FRAMEHOOK_INTERFACE_VERSION,
                .event_cb = suppress_framehook_event_cb,
                .destroy_cb = suppress_framehook_destroy_cb,
+               .chan_fixup_cb = suppress_framehook_fixup_cb,
        };
        int framehook_id;
 
@@ -10359,10 +10577,7 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
 
        if ((datastore = ast_channel_datastore_find(chan, datastore_info, NULL))) {
                suppress = datastore->data;
-               ao2_ref(suppress, +1);
-
                suppress->direction |= direction;
-
                return 0;
        }
 
@@ -10394,13 +10609,12 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
                return -1;
        }
 
+       /* and another ref for the datastore */
+       ao2_ref(suppress, +1);
        datastore->data = suppress;
 
        ast_channel_datastore_add(chan, datastore);
 
-       /* and another ref for the datastore */
-       ao2_ref(suppress, +1);
-
        return 0;
 }
 
@@ -10428,6 +10642,7 @@ int ast_channel_unsuppress(struct ast_channel *chan, unsigned int direction, enu
                /* Nothing left to suppress.  Bye! */
                ast_framehook_detach(chan, suppress->framehook_id);
                ast_channel_datastore_remove(chan, datastore);
+               ast_datastore_free(datastore);
        }
 
        return 0;
@@ -10441,7 +10656,7 @@ void ast_channel_end_dtmf(struct ast_channel *chan, char digit, struct timeval s
        ast_channel_lock(chan);
        dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
                || (ast_channel_softhangup_internal_flag(chan)
-                       & ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE));
+                       & ~AST_SOFTHANGUP_ASYNCGOTO);
        ast_channel_unlock(chan);
        if (dead) {
                /* Channel is a zombie or a real hangup. */
@@ -10456,3 +10671,71 @@ void ast_channel_end_dtmf(struct ast_channel *chan, char digit, struct timeval s
        ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
                digit, ast_channel_name(chan), why, duration);
 }
+
+static void features_destroy(void *obj)
+{
+       ast_bridge_features_destroy(obj);
+}
+
+static const struct ast_datastore_info bridge_features_info = {
+       .type = "bridge-features",
+       .destroy = features_destroy,
+};
+
+struct ast_bridge_features *ast_channel_feature_hooks_get(struct ast_channel *chan)
+{
+       struct ast_datastore *datastore;
+
+       datastore = ast_channel_datastore_find(chan, &bridge_features_info, NULL);
+       if (!datastore) {
+               return NULL;
+       }
+       return datastore->data;
+}
+
+static int channel_feature_hooks_set_full(struct ast_channel *chan, struct ast_bridge_features *features, int replace)
+{
+       struct ast_datastore *datastore;
+       struct ast_bridge_features *ds_features;
+
+       datastore = ast_channel_datastore_find(chan, &bridge_features_info, NULL);
+       if (datastore) {
+               ds_features = datastore->data;
+               if (replace) {
+                       ast_bridge_features_cleanup(ds_features);
+                       ast_bridge_features_init(ds_features);
+               }
+               if (features) {
+                       ast_bridge_features_merge(ds_features, features);
+               }
+               return 0;
+       }
+
+       datastore = ast_datastore_alloc(&bridge_features_info, NULL);
+       if (!datastore) {
+               return -1;
+       }
+
+       ds_features = ast_bridge_features_new();
+       if (!ds_features) {
+               ast_datastore_free(datastore);
+               return -1;
+       }
+
+       if (features) {
+               ast_bridge_features_merge(ds_features, features);
+       }
+       datastore->data = ds_features;
+       ast_channel_datastore_add(chan, datastore);
+       return 0;
+}
+
+int ast_channel_feature_hooks_append(struct ast_channel *chan, struct ast_bridge_features *features)
+{
+       return channel_feature_hooks_set_full(chan, features, 0);
+}
+
+int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridge_features *features)
+{
+       return channel_feature_hooks_set_full(chan, features, 1);
+}