git migration: Refactor the ASTERISK_FILE_VERSION macro
[asterisk/asterisk.git] / main / channel.c
index 567d8c6..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);
@@ -738,7 +727,7 @@ const char *ast_state2str(enum ast_channel_state state)
        default:
                if (!(buf = ast_threadstorage_get(&state2str_threadbuf, STATE2STR_BUFSIZE)))
                        return "Unknown";
-               snprintf(buf, STATE2STR_BUFSIZE, "Unknown (%d)", state);
+               snprintf(buf, STATE2STR_BUFSIZE, "Unknown (%u)", state);
                return buf;
        }
 }
@@ -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 char *linkedid, enum ama_flags amaflag, const char *file, int line,
+                      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, va_list ap)
 {
        struct ast_channel *tmp;
@@ -876,19 +794,25 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
                return NULL;
        }
 
-       if (!(tmp = ast_channel_internal_alloc(ast_channel_destructor, linkedid))) {
+       if (!(tmp = ast_channel_internal_alloc(ast_channel_destructor, assignedids, requestor))) {
                /* Channel structure allocation failure. */
                return NULL;
        }
 
        ast_channel_stage_snapshot(tmp);
 
-       if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_CACHE_STRINGS))) {
-               ao2_ref(tmp, -1);
+       if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
                /* format capabilities structure allocation failure */
-               return NULL;
+               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
@@ -1029,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
@@ -1039,8 +967,9 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
 
 struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *cid_num,
                                        const char *cid_name, const char *acctcode,
-                                       const char *exten, const char *context,
-                                       const char *linkedid, enum ama_flags amaflag,
+                                       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, ...)
 {
@@ -1049,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,
-                                       linkedid, amaflag, file, line, function, name_fmt, ap);
+                                       assignedids, requestor, amaflag, endpoint, file, line, function, name_fmt, ap);
        va_end(ap);
 
        return result;
@@ -1066,7 +995,7 @@ struct ast_channel *ast_dummy_channel_alloc(void)
        struct ast_channel *tmp;
        struct varshead *headp;
 
-       if (!(tmp = ast_channel_internal_alloc(ast_dummy_channel_destructor, NULL))) {
+       if (!(tmp = ast_channel_internal_alloc(ast_dummy_channel_destructor, NULL, NULL))) {
                /* Dummy channel structure allocation failure. */
                return NULL;
        }
@@ -1199,7 +1128,7 @@ static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, in
 
        if (ast_channel_alert_writable(chan)) {
                if (ast_channel_alert_write(chan)) {
-                       ast_log(LOG_WARNING, "Unable to write to alert pipe on %s (qlen = %d): %s!\n",
+                       ast_log(LOG_WARNING, "Unable to write to alert pipe on %s (qlen = %u): %s!\n",
                                ast_channel_name(chan), queued_frames, strerror(errno));
                }
        } else if (ast_channel_timingfd(chan) > -1) {
@@ -1267,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;
@@ -1280,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;
 }
 
@@ -1292,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;
 }
 
@@ -1531,6 +1460,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame)
         */
        switch (frame->frametype) {
        case AST_FRAME_BRIDGE_ACTION:
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
        case AST_FRAME_CONTROL:
        case AST_FRAME_TEXT:
        case AST_FRAME_IMAGE:
@@ -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)) {
@@ -2262,10 +2192,16 @@ static void ast_channel_destructor(void *obj)
 
        /* Things that may possibly raise Stasis messages shouldn't occur after this point */
        ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEAD);
-       ast_channel_lock(chan);
-       ast_channel_publish_snapshot(chan);
-       ast_channel_unlock(chan);
-       publish_cache_clear(chan);
+
+       if (ast_channel_internal_is_finalized(chan)) {
+               /* A channel snapshot should not be in the process of being staged now. */
+               ast_assert(!ast_test_flag(ast_channel_flags(chan), AST_FLAG_SNAPSHOT_STAGE));
+
+               ast_channel_lock(chan);
+               ast_channel_publish_snapshot(chan);
+               ast_channel_unlock(chan);
+               publish_cache_clear(chan);
+       }
 
        ast_channel_lock(chan);
 
@@ -2313,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));
@@ -2369,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);
@@ -2590,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 up channel '%s'\n", 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);
@@ -2619,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)
@@ -2662,6 +2595,13 @@ int ast_channel_has_audio_frame_or_monitor(struct ast_channel *chan)
                || !ast_framehook_list_contains_no_active(ast_channel_framehooks(chan));
 }
 
+int ast_channel_has_hook_requiring_audio(struct ast_channel *chan)
+{
+       return ast_channel_monitor(chan)
+               || !ast_audiohook_write_list_empty(ast_channel_audiohooks(chan))
+               || !ast_framehook_list_contains_no_active_of_type(ast_channel_framehooks(chan), AST_FRAME_VOICE);
+}
+
 static void destroy_hooks(struct ast_channel *chan)
 {
        if (ast_channel_audiohooks(chan)) {
@@ -2832,7 +2772,7 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
                                        break;
                                }
                                if (ms == 0) {
-                                       ast_debug(2, "Didn't receive a media frame from %s within %d ms of answering. Continuing anyway\n", ast_channel_name(chan), MAX(delay, 500));
+                                       ast_debug(2, "Didn't receive a media frame from %s within %u ms of answering. Continuing anyway\n", ast_channel_name(chan), MAX(delay, 500));
                                        break;
                                }
                                cur = ast_read(chan);
@@ -2875,6 +2815,7 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
                                case AST_FRAME_CONTROL:
                                case AST_FRAME_IAX:
                                case AST_FRAME_BRIDGE_ACTION:
+                               case AST_FRAME_BRIDGE_ACTION_SYNC:
                                case AST_FRAME_NULL:
                                case AST_FRAME_CNG:
                                        break;
@@ -2885,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:
@@ -2936,12 +2877,13 @@ int ast_channel_get_up_time(struct ast_channel *chan)
        return (ast_tvdiff_ms(ast_tvnow(), ast_channel_answertime(chan)) / 1000);
 }
 
-void ast_deactivate_generator(struct ast_channel *chan)
+static void deactivate_generator_nolock(struct ast_channel *chan)
 {
-       ast_channel_lock(chan);
        if (ast_channel_generatordata(chan)) {
-               if (ast_channel_generator(chan) && ast_channel_generator(chan)->release) {
-                       ast_channel_generator(chan)->release(chan, ast_channel_generatordata(chan));
+               struct ast_generator *generator = ast_channel_generator(chan);
+
+               if (generator && generator->release) {
+                       generator->release(chan, ast_channel_generatordata(chan));
                }
                ast_channel_generatordata_set(chan, NULL);
                ast_channel_generator_set(chan, NULL);
@@ -2949,14 +2891,23 @@ void ast_deactivate_generator(struct ast_channel *chan)
                ast_clear_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
                ast_settimeout(chan, 0, NULL, NULL);
        }
+}
+
+void ast_deactivate_generator(struct ast_channel *chan)
+{
+       ast_channel_lock(chan);
+       deactivate_generator_nolock(chan);
        ast_channel_unlock(chan);
 }
 
 static void generator_write_format_change(struct ast_channel *chan)
 {
+       struct ast_generator *generator;
+
        ast_channel_lock(chan);
-       if (ast_channel_generator(chan) && ast_channel_generator(chan)->write_format_change) {
-               ast_channel_generator(chan)->write_format_change(chan, ast_channel_generatordata(chan));
+       generator = ast_channel_generator(chan);
+       if (generator && generator->write_format_change) {
+               generator->write_format_change(chan, ast_channel_generatordata(chan));
        }
        ast_channel_unlock(chan);
 }
@@ -2976,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) {
@@ -3002,8 +2954,10 @@ int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen,
 
        ast_channel_lock(chan);
        if (ast_channel_generatordata(chan)) {
-               if (ast_channel_generator(chan) && ast_channel_generator(chan)->release) {
-                       ast_channel_generator(chan)->release(chan, ast_channel_generatordata(chan));
+               struct ast_generator *generator_old = ast_channel_generator(chan);
+
+               if (generator_old && generator_old->release) {
+                       generator_old->release(chan, ast_channel_generatordata(chan));
                }
        }
        if (gen->alloc && !(generatordata = gen->alloc(chan, params))) {
@@ -3421,6 +3375,11 @@ int ast_waitfordigit(struct ast_channel *c, int ms)
 
 int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data)
 {
+       return ast_settimeout_full(c, rate, func, data, 0);
+}
+
+int ast_settimeout_full(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data, unsigned int is_ao2_obj)
+{
        int res;
        unsigned int real_rate = rate, max_rate;
 
@@ -3444,9 +3403,20 @@ int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const v
 
        res = ast_timer_set_rate(ast_channel_timer(c), real_rate);
 
+       if (ast_channel_timingdata(c) && ast_test_flag(ast_channel_flags(c), AST_FLAG_TIMINGDATA_IS_AO2_OBJ)) {
+               ao2_ref(ast_channel_timingdata(c), -1);
+       }
+
        ast_channel_timingfunc_set(c, func);
        ast_channel_timingdata_set(c, data);
 
+       if (data && is_ao2_obj) {
+               ao2_ref(data, 1);
+               ast_set_flag(ast_channel_flags(c), AST_FLAG_TIMINGDATA_IS_AO2_OBJ);
+       } else {
+               ast_clear_flag(ast_channel_flags(c), AST_FLAG_TIMINGDATA_IS_AO2_OBJ);
+       }
+
        if (func == NULL && rate == 0 && ast_channel_fdno(c) == AST_TIMING_FD) {
                /* Clearing the timing func and setting the rate to 0
                 * means that we don't want to be reading from the timingfd
@@ -3597,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,
@@ -3614,54 +3584,60 @@ 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)
 {
-       if (ast_channel_generator(chan) && ast_channel_generator(chan)->generate && ast_channel_generatordata(chan) &&  !ast_internal_timing_enabled(chan)) {
-               void *tmp = ast_channel_generatordata(chan);
-               int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples) = ast_channel_generator(chan)->generate;
-               int res;
-               int samples;
-
-               if (ast_channel_timingfunc(chan)) {
-                       ast_debug(1, "Generator got voice, switching to phase locked mode\n");
-                       ast_settimeout(chan, 0, NULL, NULL);
-               }
+       struct ast_generator *generator;
+       void *gendata;
+       int res;
+       int samples;
+
+       generator = ast_channel_generator(chan);
+       if (!generator
+               || !generator->generate
+               || f->frametype != AST_FRAME_VOICE
+               || !ast_channel_generatordata(chan)
+               || ast_channel_timingfunc(chan)) {
+               return;
+       }
 
-               ast_channel_generatordata_set(chan, NULL);     /* reset, to let writes go through */
+       /*
+        * 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) {
+               float factor;
+               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;
+       }
 
-               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));
-                       samples = (int) ( ((float) f->samples) * factor );
-               } else {
-                       samples = f->samples;
-               }
+       gendata = ast_channel_generatordata(chan);
+       ast_channel_generatordata_set(chan, NULL);     /* reset, to let writes go through */
 
-               /* This unlock is here based on two assumptions that hold true at this point in the
-                * code. 1) this function is only called from within __ast_read() and 2) all generators
-                * call ast_write() in their generate callback.
-                *
-                * The reason this is added is so that when ast_write is called, the lock that occurs
-                * there will not recursively lock the channel. Doing this will cause intended deadlock
-                * avoidance not to work in deeper functions
-                */
-               ast_channel_unlock(chan);
-               res = generate(chan, tmp, f->datalen, samples);
-               ast_channel_lock(chan);
-               ast_channel_generatordata_set(chan, tmp);
+       /*
+        * This unlock is here based on two assumptions that hold true at
+        * this point in the code. 1) this function is only called from
+        * within __ast_read() and 2) all generators call ast_write() in
+        * their generate callback.
+        *
+        * The reason this is added is so that when ast_write is called,
+        * the lock that occurs there will not recursively lock the
+        * channel.  Doing this will allow deadlock avoidance to work in
+        * deeper functions.
+        */
+       ast_channel_unlock(chan);
+       res = generator->generate(chan, gendata, f->datalen, samples);
+       ast_channel_lock(chan);
+       if (generator == ast_channel_generator(chan)) {
+               ast_channel_generatordata_set(chan, gendata);
                if (res) {
                        ast_debug(1, "Auto-deactivating generator\n");
                        ast_deactivate_generator(chan);
                }
-
-       } else if (f->frametype == AST_FRAME_CNG) {
-               if (ast_channel_generator(chan) && !ast_channel_timingfunc(chan) && (ast_channel_timingfd(chan) > -1)) {
-                       ast_debug(1, "Generator got CNG, switching to timed mode\n");
-                       ast_settimeout(chan, 50, generator_force, chan);
-               }
        }
 }
 
@@ -3795,9 +3771,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                /* save a copy of func/data before unlocking the channel */
                                ast_timing_func_t func = ast_channel_timingfunc(chan);
                                void *data = ast_channel_timingdata(chan);
+                               int got_ref = 0;
+                               if (data && ast_test_flag(ast_channel_flags(chan), AST_FLAG_TIMINGDATA_IS_AO2_OBJ)) {
+                                       ao2_ref(data, 1);
+                                       got_ref = 1;
+                               }
                                ast_channel_fdno_set(chan, -1);
                                ast_channel_unlock(chan);
                                func(data);
+                               if (got_ref) {
+                                       ao2_ref(data, -1);
+                               }
                        } else {
                                ast_timer_set_rate(ast_channel_timer(chan), 0);
                                ast_channel_fdno_set(chan, -1);
@@ -3930,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) {
@@ -4024,7 +4007,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                        f->len = option_dtmfminduration;
                                }
                                if (f->len < option_dtmfminduration && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY)) {
-                                       ast_log(LOG_DTMF, "DTMF end '%c' has duration %ld but want minimum %d, emulating on %s\n", f->subclass.integer, f->len, option_dtmfminduration, ast_channel_name(chan));
+                                       ast_log(LOG_DTMF, "DTMF end '%c' has duration %ld but want minimum %u, emulating on %s\n", f->subclass.integer, f->len, option_dtmfminduration, ast_channel_name(chan));
                                        ast_set_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
                                        ast_channel_dtmf_digit_to_emulate_set(chan, f->subclass.integer);
                                        ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration - f->len);
@@ -4131,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! */
@@ -4234,11 +4276,6 @@ done:
        return f;
 }
 
-int ast_internal_timing_enabled(struct ast_channel *chan)
-{
-       return (ast_opt_internal_timing && ast_channel_timingfd(chan) > -1);
-}
-
 struct ast_frame *ast_read(struct ast_channel *chan)
 {
        return __ast_read(chan, 0);
@@ -4286,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:
@@ -4452,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;
        }
@@ -4600,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:
@@ -4617,14 +4658,14 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
        if (ts) {
                /* We have a tone to play, yay. */
-               ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", ast_channel_name(chan), condition);
+               ast_debug(1, "Driver for channel '%s' does not support indication %u, emulating it\n", ast_channel_name(chan), condition);
                res = ast_playtones_start(chan, 0, ts->data, 1);
                ts = ast_tone_zone_sound_unref(ts);
        }
 
        if (res) {
                /* not handled */
-               ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, ast_channel_name(chan));
+               ast_log(LOG_WARNING, "Unable to handle indication %u for '%s'\n", condition, ast_channel_name(chan));
        }
 
 indicate_cleanup:
@@ -4698,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;
@@ -4709,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);
@@ -4806,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))
@@ -5018,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 {
@@ -5043,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) {
@@ -5135,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");
                                        }
@@ -5144,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");
@@ -5226,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. */
@@ -5279,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) {
@@ -5298,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
@@ -5341,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. */
@@ -5362,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)
@@ -5571,7 +5647,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
                data = tmpchan;
                type = "Local";
        }
-       if (!(new_chan = ast_request(type, cap, orig, data, &cause))) {
+       if (!(new_chan = ast_request(type, cap, NULL, orig, data, &cause))) {
                ast_log(LOG_NOTICE, "Unable to create channel for call forward to '%s/%s' (cause = %d)\n", type, data, cause);
                handle_cause(cause, outstate);
                ast_hangup(orig);
@@ -5588,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 */
@@ -5599,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);
 
@@ -5621,7 +5700,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
        return new_chan;
 }
 
-struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *addr, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
+struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
 {
        int dummy_outstate;
        int cause = 0;
@@ -5635,7 +5714,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
        else
                outstate = &dummy_outstate;     /* make outstate always a valid pointer */
 
-       chan = ast_request(type, cap, requestor, addr, &cause);
+       chan = ast_request(type, cap, assignedids, requestor, addr, &cause);
        if (!chan) {
                ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, addr);
                handle_cause(cause, outstate);
@@ -5664,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);
                }
        }
@@ -5694,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);
@@ -5807,9 +5895,9 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
        return chan;
 }
 
-struct ast_channel *ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *addr, int timeout, int *outstate, const char *cidnum, const char *cidname)
+struct ast_channel *ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int timeout, int *outstate, const char *cidnum, const char *cidname)
 {
-       return __ast_request_and_dial(type, cap, requestor, addr, timeout, outstate, cidnum, cidname, NULL);
+       return __ast_request_and_dial(type, cap, assignedids, requestor, addr, timeout, outstate, cidnum, cidname, NULL);
 }
 
 static int set_security_requirements(const struct ast_channel *requestor, struct ast_channel *out)
@@ -5852,7 +5940,7 @@ static int set_security_requirements(const struct ast_channel *requestor, struct
        return 0;
 }
 
-struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_channel *requestor, const char *addr, int *cause)
+struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
 {
        struct chanlist *chan;
        struct ast_channel *c;
@@ -5870,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;
@@ -5900,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, requestor, addr, cause))) {
-                       ast_format_cap_destroy(joint_cap);
+               if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) {
+                       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");
@@ -5945,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);
@@ -6122,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;
@@ -6164,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;
@@ -6242,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));
                }
        }
 }
@@ -6306,55 +6489,6 @@ static void clone_variables(struct ast_channel *original, struct ast_channel *cl
        }
 }
 
-const char *ast_channel_oldest_linkedid(const char *a, const char *b)
-{
-       const char *satime, *saseq;
-       const char *sbtime, *sbseq;
-       const char *dash;
-       unsigned int atime, aseq, btime, bseq;
-
-       if (ast_strlen_zero(a)) {
-               return b;
-       }
-
-       if (ast_strlen_zero(b)) {
-               return a;
-       }
-
-       satime = a;
-       sbtime = b;
-
-       /* jump over the system name */
-       if ((dash = strrchr(satime, '-'))) {
-               satime = dash+1;
-       }
-       if ((dash = strrchr(sbtime, '-'))) {
-               sbtime = dash+1;
-       }
-
-       /* the sequence comes after the '.' */
-       saseq = strchr(satime, '.');
-       sbseq = strchr(sbtime, '.');
-       if (!saseq || !sbseq) {
-               return NULL;
-       }
-       saseq++;
-       sbseq++;
-
-       /* convert it all to integers */
-       atime = atoi(satime); /* note that atoi is ignoring the '.' after the time string */
-       btime = atoi(sbtime); /* note that atoi is ignoring the '.' after the time string */
-       aseq = atoi(saseq);
-       bseq = atoi(sbseq);
-
-       /* and finally compare */
-       if (atime == btime) {
-               return (aseq < bseq) ? a : b;
-       }
-       else {
-               return (atime < btime) ? a : b;
-       }
-}
 
 void ast_channel_name_to_dial_string(char *channel_name)
 {
@@ -6382,6 +6516,7 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        unsigned int clone_disablestatecache;
        int visible_indication;
        int clone_hold_state;
+       int moh_is_playing;
        struct ast_frame *current;
        const struct ast_channel_tech *t;
        void *t_pvt;
@@ -6393,11 +6528,11 @@ 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];
-       const char *tmp_id;
        char clone_sending_dtmf_digit;
        struct timeval clone_sending_dtmf_tv;
 
@@ -6406,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
@@ -6428,6 +6568,12 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        ao2_unlink(channels, original);
        ao2_unlink(channels, clonechan);
 
+       moh_is_playing = ast_test_flag(ast_channel_flags(original), AST_FLAG_MOH);
+       if (moh_is_playing) {
+               /* Stop MOH on the old original channel. */
+               ast_moh_stop(original);
+       }
+
        /*
         * Stop any visible indication on the original channel so we can
         * transfer it to the clonechan taking the original's place.
@@ -6440,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(%d) into the structure of %s(%d)\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);
 
@@ -6461,9 +6608,10 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
        /* Swap uniqueid's of the channels. This needs to happen before channel renames,
         * so rename events get the proper id's.
         */
-       tmp_id = ast_strdupa(ast_channel_uniqueid(clonechan));
-       ast_channel_uniqueid_set(clonechan, ast_channel_uniqueid(original));
-       ast_channel_uniqueid_set(original, tmp_id);
+       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.
@@ -6511,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);
 
@@ -6567,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);
@@ -6639,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));
@@ -6657,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)) {
@@ -6684,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) {
@@ -6729,6 +6922,15 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
                }
        }
 
+       /*
+        * If MOH was playing on the original channel then it needs to be
+        * maintained on the channel that is replacing it.
+        */
+       if (moh_is_playing) {
+               /* Start MOH on the new original channel. */
+               ast_moh_start(original, NULL, NULL);
+       }
+
        ast_channel_lock(original);
 
        /* Signal any blocker */
@@ -6736,7 +6938,7 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
                pthread_kill(ast_channel_blocker(original), SIGURG);
        }
 
-       ast_debug(1, "Done Masquerading %s (%d)\n", ast_channel_name(original), ast_channel_state(original));
+       ast_debug(1, "Done Masquerading %s (%u)\n", ast_channel_name(original), ast_channel_state(original));
        ast_channel_unlock(original);
 
        if ((bridged = ast_channel_bridge_peer(original))) {
@@ -6761,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)
@@ -6905,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;
@@ -6918,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);
 }
 
@@ -6928,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;
@@ -6985,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;
@@ -7297,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);
@@ -7397,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;
@@ -7534,7 +7755,7 @@ void ast_channels_init(void)
 
        ast_plc_reload();
 
-       ast_register_atexit(channels_shutdown);
+       ast_register_cleanup(channels_shutdown);
 
 }
 
@@ -7627,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)
@@ -7655,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));
 
@@ -7672,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)
@@ -7683,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;
@@ -7698,30 +7916,24 @@ struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_cha
        return state;
 }
 
-static int internal_deactivate_generator(struct ast_channel *chan, void* generator)
+static int deactivate_silence_generator(struct ast_channel *chan)
 {
        ast_channel_lock(chan);
 
        if (!ast_channel_generatordata(chan)) {
-               ast_debug(1, "Trying to stop silence generator when there is no "
-                   "generator on '%s'\n", ast_channel_name(chan));
+               ast_debug(1, "Trying to stop silence generator when there is no generator on '%s'\n",
+                       ast_channel_name(chan));
                ast_channel_unlock(chan);
                return 0;
        }
-       if (ast_channel_generator(chan) != generator) {
-               ast_debug(1, "Trying to stop silence generator when it is not the current "
-                   "generator on '%s'\n", ast_channel_name(chan));
+       if (ast_channel_generator(chan) != &silence_generator) {
+               ast_debug(1, "Trying to stop silence generator when it is not the current generator on '%s'\n",
+                       ast_channel_name(chan));
                ast_channel_unlock(chan);
                return 0;
        }
-       if (ast_channel_generator(chan) && ast_channel_generator(chan)->release) {
-               ast_channel_generator(chan)->release(chan, ast_channel_generatordata(chan));
-       }
-       ast_channel_generatordata_set(chan, NULL);
-       ast_channel_generator_set(chan, NULL);
-       ast_channel_set_fd(chan, AST_GENERATOR_FD, -1);
-       ast_clear_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
-       ast_settimeout(chan, 0, NULL, NULL);
+       deactivate_generator_nolock(chan);
+
        ast_channel_unlock(chan);
 
        return 1;
@@ -7729,14 +7941,17 @@ static int internal_deactivate_generator(struct ast_channel *chan, void* generat
 
 void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
 {
-       if (!state)
+       if (!state) {
                return;
+       }
 
-       if (internal_deactivate_generator(chan, &silence_generator)) {
+       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);
 }
 
@@ -7837,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);
 }
 
@@ -10134,6 +10350,20 @@ int ast_channel_is_bridged(const struct ast_channel *chan)
        return ast_channel_internal_bridge(chan) != NULL;
 }
 
+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;
+       int unbridge = ast_channel_unbridged(chan);
+
+       /* 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 || unbridge) && (hangup_test == hangup_flags));
+}
+
 struct ast_channel *ast_channel_bridge_peer(struct ast_channel *chan)
 {
        struct ast_channel *peer;
@@ -10170,35 +10400,37 @@ struct ast_channel *ast_channel_yank(struct ast_channel *yankee)
                char *accountcode;
                char *exten;
                char *context;
-               char *linkedid;
                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);
        my_vars.accountcode = ast_strdupa(ast_channel_accountcode(yankee));
        my_vars.exten = ast_strdupa(ast_channel_exten(yankee));
        my_vars.context = ast_strdupa(ast_channel_context(yankee));
-       my_vars.linkedid = ast_strdupa(ast_channel_linkedid(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
         * locks the channel container when linking the new channel in. */
        if (!(yanked_chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, my_vars.accountcode,
-                                       my_vars.exten, my_vars.context, my_vars.linkedid, my_vars.amaflags,
+                                       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);
 
@@ -10276,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;
@@ -10327,20 +10566,18 @@ 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;
 
        if (!(datastore_info = suppress_get_datastore_information(frametype))) {
-               ast_log(LOG_WARNING, "Attempted to suppress an unsupported frame type (%d).\n", frametype);
+               ast_log(LOG_WARNING, "Attempted to suppress an unsupported frame type (%u).\n", frametype);
                return -1;
        }
 
        if ((datastore = ast_channel_datastore_find(chan, datastore_info, NULL))) {
                suppress = datastore->data;
-               ao2_ref(suppress, +1);
-
                suppress->direction |= direction;
-
                return 0;
        }
 
@@ -10372,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;
 }
 
@@ -10389,7 +10625,7 @@ int ast_channel_unsuppress(struct ast_channel *chan, unsigned int direction, enu
        struct suppress_data *suppress;
 
        if (!(datastore_info = suppress_get_datastore_information(frametype))) {
-               ast_log(LOG_WARNING, "Attempted to unsuppress an unsupported frame type (%d).\n", frametype);
+               ast_log(LOG_WARNING, "Attempted to unsuppress an unsupported frame type (%u).\n", frametype);
                return -1;
        }
 
@@ -10406,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;
@@ -10419,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. */
@@ -10434,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);
+}