[Asterisk-code-review] stasis: Segment channel snapshot to reduce creation cost. (asterisk[master])

Jenkins2 asteriskteam at digium.com
Mon Nov 26 14:07:53 CST 2018


Jenkins2 has submitted this change and it was merged. ( https://gerrit.asterisk.org/10622 )

Change subject: stasis: Segment channel snapshot to reduce creation cost.
......................................................................

stasis: Segment channel snapshot to reduce creation cost.

When a channel snapshot was created it used to be done
from scratch, copying all data (many strings). This incurs
a cost when doing so.

This change segments the channel snapshot into different
components which can be reused if unchanged from the
previous snapshot creation, reducing the cost. In normal
cases this results in some pointers being copied with
reference count being bumped, some integers being set,
and a string or two copied. The other benefit is that it
is now possible to determine if a channel snapshot update
is redundant and thus stop it before a message is published
to stasis.

The specific segments in the channel snapshot were split up
based on whether they are changed together, how often they
are changed, and their general grouping. In practice only
1 (or 0) of the segments actually get changed in normal
operation.

Invalidation is done by setting a flag on the channel when
the segment source is changed, forcing creation of a new
segment when the channel snapshot is created.

ASTERISK-28119

Change-Id: I5d7ef3df963a88ac47bc187d73c5225c315f8423
---
M apps/app_queue.c
M channels/chan_pjsip.c
M channels/pjsip/cli_commands.c
M configs/samples/ari.conf.sample
M configs/samples/manager.conf.sample
M include/asterisk/channel.h
M include/asterisk/stasis_channels.h
M main/bridge.c
M main/cdr.c
M main/cel.c
M main/channel.c
M main/channel_internal_api.c
M main/cli.c
M main/core_local.c
M main/endpoints.c
M main/manager.c
M main/manager_bridges.c
M main/manager_channels.c
M main/stasis_bridges.c
M main/stasis_channels.c
M pbx/pbx_realtime.c
M res/ari/resource_bridges.c
M res/ari/resource_channels.c
M res/parking/parking_applications.c
M res/parking/parking_bridge_features.c
M res/res_chan_stats.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_refer.c
M res/res_stasis.c
M res/stasis/app.c
M tests/test_cel.c
M tests/test_stasis_channels.c
32 files changed, 878 insertions(+), 470 deletions(-)

Approvals:
  George Joseph: Looks good to me, approved
  Jenkins2: Approved for Submit



diff --git a/apps/app_queue.c b/apps/app_queue.c
index 17e35f9..2e59bf6 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -5890,13 +5890,13 @@
 	agent_blob = stasis_message_data(msg);
 
 	if (ast_channel_agent_login_type() == stasis_message_type(msg)) {
-		ast_queue_log("NONE", agent_blob->snapshot->uniqueid,
+		ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
 			ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
-			"AGENTLOGIN", "%s", agent_blob->snapshot->name);
+			"AGENTLOGIN", "%s", agent_blob->snapshot->base->name);
 	} else if (ast_channel_agent_logoff_type() == stasis_message_type(msg)) {
-		ast_queue_log("NONE", agent_blob->snapshot->uniqueid,
+		ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
 			ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
-			"AGENTLOGOFF", "%s|%ld", agent_blob->snapshot->name,
+			"AGENTLOGOFF", "%s|%ld", agent_blob->snapshot->base->name,
 			(long) ast_json_integer_get(ast_json_object_get(agent_blob->blob, "logintime")));
 	}
 }
@@ -6088,8 +6088,8 @@
 		ast_str_set(&transfer_str, 0, "APP|%s", atxfer_msg->dest.app);
 		break;
 	case AST_ATTENDED_TRANSFER_DEST_LINK:
-		ast_str_set(&transfer_str, 0, "LINK|%s|%s", atxfer_msg->dest.links[0]->name,
-				atxfer_msg->dest.links[1]->name);
+		ast_str_set(&transfer_str, 0, "LINK|%s|%s", atxfer_msg->dest.links[0]->base->name,
+				atxfer_msg->dest.links[1]->base->name);
 		break;
 	case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
 	case AST_ATTENDED_TRANSFER_DEST_FAIL:
@@ -6098,7 +6098,7 @@
 		return;
 	}
 
-	ast_queue_log(queue_data->queue->name, caller->uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
+	ast_queue_log(queue_data->queue->name, caller->base->uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
 			ast_str_buffer(transfer_str),
 			(long) (queue_data->starttime - queue_data->holdstart),
 			(long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
@@ -6131,11 +6131,11 @@
 		return;
 	}
 
-	if (!strcmp(enter_blob->channel->uniqueid, queue_data->caller_uniqueid)) {
+	if (!strcmp(enter_blob->channel->base->uniqueid, queue_data->caller_uniqueid)) {
 		ast_string_field_set(queue_data, bridge_uniqueid,
 				enter_blob->bridge->uniqueid);
 		ast_debug(3, "Detected entry of caller channel %s into bridge %s\n",
-				enter_blob->channel->name, queue_data->bridge_uniqueid);
+				enter_blob->channel->base->name, queue_data->bridge_uniqueid);
 	}
 }
 
@@ -6186,7 +6186,7 @@
 	context = transfer_msg->context;
 
 	ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
-	ast_queue_log(queue_data->queue->name, caller_snapshot->uniqueid, queue_data->member->membername,
+	ast_queue_log(queue_data->queue->name, caller_snapshot->base->uniqueid, queue_data->member->membername,
 			"BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
 			exten, context,
 			(long) (queue_data->starttime - queue_data->holdstart),
@@ -6302,9 +6302,9 @@
 		return;
 	}
 
-	if (!strcmp(local_one->uniqueid, queue_data->member_uniqueid)) {
+	if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
 		optimization = &queue_data->member_optimize;
-	} else if (!strcmp(local_two->uniqueid, queue_data->caller_uniqueid)) {
+	} else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
 		optimization = &queue_data->caller_optimize;
 	} else {
 		return;
@@ -6313,9 +6313,9 @@
 	/* We only allow move-swap optimizations, so there had BETTER be a source */
 	ast_assert(source != NULL);
 
-	optimization->source_chan_uniqueid = ast_strdup(source->uniqueid);
+	optimization->source_chan_uniqueid = ast_strdup(source->base->uniqueid);
 	if (!optimization->source_chan_uniqueid) {
-		ast_log(LOG_ERROR, "Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->name);
+		ast_log(LOG_ERROR, "Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->base->name);
 		return;
 	}
 	id = ast_json_integer_get(ast_json_object_get(ast_multi_channel_blob_get_json(optimization_blob), "id"));
@@ -6354,10 +6354,10 @@
 		return;
 	}
 
-	if (!strcmp(local_one->uniqueid, queue_data->member_uniqueid)) {
+	if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
 		optimization = &queue_data->member_optimize;
 		is_caller = 0;
-	} else if (!strcmp(local_two->uniqueid, queue_data->caller_uniqueid)) {
+	} else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
 		optimization = &queue_data->caller_optimize;
 		is_caller = 1;
 	} else {
@@ -6420,16 +6420,16 @@
 		return;
 	}
 
-	if (!strcmp(channel_blob->snapshot->uniqueid, queue_data->caller_uniqueid)) {
+	if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
 		reason = CALLER;
-	} else if (!strcmp(channel_blob->snapshot->uniqueid, queue_data->member_uniqueid)) {
+	} else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
 		reason = AGENT;
 	} else {
 		ao2_unlock(queue_data);
 		return;
 	}
 
-	chan = ast_channel_get_by_name(channel_blob->snapshot->name);
+	chan = ast_channel_get_by_name(channel_blob->snapshot->base->name);
 	if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
 		     !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
 		     !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
@@ -6446,9 +6446,9 @@
 	ao2_unlock(queue_data);
 
 	ast_debug(3, "Detected hangup of queue %s channel %s\n", reason == CALLER ? "caller" : "member",
-			channel_blob->snapshot->name);
+			channel_blob->snapshot->base->name);
 
-	ast_queue_log(queue_data->queue->name, caller_snapshot->uniqueid, queue_data->member->membername,
+	ast_queue_log(queue_data->queue->name, caller_snapshot->base->uniqueid, queue_data->member->membername,
 			reason == CALLER ? "COMPLETECALLER" : "COMPLETEAGENT", "%ld|%ld|%d",
 		(long) (queue_data->starttime - queue_data->holdstart),
 		(long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index d0a74cd..e44f328 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -1169,7 +1169,7 @@
 			continue;
 		}
 
-		if (chan_pjsip_get_hold(snapshot->uniqueid)) {
+		if (chan_pjsip_get_hold(snapshot->base->uniqueid)) {
 			ast_devstate_aggregate_add(&aggregate, AST_DEVICE_ONHOLD);
 		} else {
 			ast_devstate_aggregate_add(&aggregate, ast_state_chan2dev(snapshot->state));
diff --git a/channels/pjsip/cli_commands.c b/channels/pjsip/cli_commands.c
index 9a8dc29..2ce2369 100644
--- a/channels/pjsip/cli_commands.c
+++ b/channels/pjsip/cli_commands.c
@@ -61,13 +61,13 @@
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		right_key = right_obj->name;
+		right_key = right_obj->base->name;
 		/* Fall through */
 	case OBJ_SEARCH_KEY:
-		cmp = strcmp(left_obj->name, right_key);
+		cmp = strcmp(left_obj->base->name, right_key);
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
-		cmp = strncmp(left_obj->name, right_key, strlen(right_key));
+		cmp = strncmp(left_obj->base->name, right_key, strlen(right_key));
 		break;
 	default:
 		cmp = 0;
@@ -86,17 +86,17 @@
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		cmp = strcmp(left_obj->bridgeid, right_obj->bridgeid);
+		cmp = strcmp(left_obj->bridge->id, right_obj->bridge->id);
 		if (cmp) {
 			return cmp;
 		}
-		right_key = right_obj->name;
+		right_key = right_obj->base->name;
 		/* Fall through */
 	case OBJ_SEARCH_KEY:
-		cmp = strcmp(left_obj->name, right_key);
+		cmp = strcmp(left_obj->base->name, right_key);
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
-		cmp = strncmp(left_obj->name, right_key, strlen(right_key));
+		cmp = strncmp(left_obj->base->name, right_key, strlen(right_key));
 		break;
 	default:
 		cmp = 0;
@@ -115,15 +115,15 @@
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		right_key = right_obj->name;
+		right_key = right_obj->base->name;
 		/* Fall through */
 	case OBJ_SEARCH_KEY:
-		if (strcmp(left_obj->name, right_key) == 0) {
+		if (strcmp(left_obj->base->name, right_key) == 0) {
 			cmp = CMP_MATCH | CMP_STOP;
 		}
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
-		if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
+		if (strncmp(left_obj->base->name, right_key, strlen(right_key)) == 0) {
 			cmp = CMP_MATCH;
 		}
 		break;
@@ -144,18 +144,18 @@
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		if (strcmp(left_obj->bridgeid, right_obj->bridgeid) == 0
-			&& strcmp(left_obj->name, right_obj->name) == 0) {
+		if (strcmp(left_obj->bridge->id, right_obj->bridge->id) == 0
+			&& strcmp(left_obj->base->name, right_obj->base->name) == 0) {
 			return CMP_MATCH | CMP_STOP;
 		}
 		break;
 	case OBJ_SEARCH_KEY:
-		if (strcmp(left_obj->name, right_key) == 0) {
+		if (strcmp(left_obj->base->name, right_key) == 0) {
 			cmp = CMP_MATCH | CMP_STOP;
 		}
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
-		if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
+		if (strncmp(left_obj->base->name, right_key, strlen(right_key)) == 0) {
 			cmp = CMP_MATCH;
 		}
 		break;
@@ -172,7 +172,7 @@
 	struct ast_channel_snapshot *snapshot = obj;
 	struct ao2_container *snapshots = arg;
 
-	if (!strcmp(snapshot->type, "PJSIP")) {
+	if (!strcmp(snapshot->base->type, "PJSIP")) {
 		ao2_link(snapshots, snapshot);
 		return CMP_MATCH;
 	}
@@ -185,8 +185,8 @@
 	struct ast_channel_snapshot *channel = obj;
 	regex_t *regexbuf = arg;
 
-	if (!regexec(regexbuf, channel->name, 0, NULL, 0)
-		|| !regexec(regexbuf, channel->appl, 0, NULL, 0)) {
+	if (!regexec(regexbuf, channel->base->name, 0, NULL, 0)
+		|| !regexec(regexbuf, channel->dialplan->appl, 0, NULL, 0)) {
 		return 0;
 	}
 
@@ -236,7 +236,7 @@
 {
 	const struct ast_channel_snapshot *snapshot = obj;
 
-	return snapshot->name;
+	return snapshot->base->name;
 }
 
 static void *cli_channel_retrieve_by_id(const char *id)
@@ -280,16 +280,16 @@
 
 	ast_assert(context->output_buffer != NULL);
 
-	print_name_len = strlen(snapshot->name) + strlen(snapshot->appl) + 2;
+	print_name_len = strlen(snapshot->base->name) + strlen(snapshot->dialplan->appl) + 2;
 	print_name = alloca(print_name_len);
 
 	/* Append the application */
-	snprintf(print_name, print_name_len, "%s/%s", snapshot->name, snapshot->appl);
+	snprintf(print_name, print_name_len, "%s/%s", snapshot->base->name, snapshot->dialplan->appl);
 
 	indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	flexwidth = CLI_LAST_TABSTOP - indent;
 
-	ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, 32);
+	ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->base->creationtime.tv_sec, print_time, 32);
 
 	ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s  %-11.11s\n",
 		CLI_INDENT_TO_SPACES(context->indent_level), "Channel",
@@ -307,9 +307,9 @@
 			"%*s: %-*.*s  CLCID: \"%s\" <%s>\n",
 			indent, "Exten",
 			flexwidth, flexwidth,
-			snapshot->exten,
-			snapshot->connected_name,
-			snapshot->connected_number
+			snapshot->dialplan->exten,
+			snapshot->connected->name,
+			snapshot->connected->number
 			);
 		context->indent_level--;
 		if (context->indent_level == 0) {
@@ -338,7 +338,7 @@
 {
 	struct ast_sip_cli_context *context = arg;
 	const struct ast_channel_snapshot *snapshot = obj;
-	struct ast_channel *channel = ast_channel_get_by_name(snapshot->name);
+	struct ast_channel *channel = ast_channel_get_by_name(snapshot->base->name);
 	struct ast_sip_channel_pvt *cpvt = channel ? ast_channel_tech_pvt(channel) : NULL;
 	struct ast_sip_session *session;
 	struct ast_sip_session_media *media;
@@ -351,7 +351,7 @@
 	ast_assert(context->output_buffer != NULL);
 
 	if (!channel) {
-		ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->name);
+		ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
 		return -1;
 	}
 
@@ -359,7 +359,7 @@
 
 	session = cpvt->session;
 	if (!session) {
-		ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->name);
+		ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
 		ast_channel_unlock(channel);
 		ao2_cleanup(channel);
 		return -1;
@@ -367,7 +367,7 @@
 
 	media = session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
 	if (!media || !media->rtp) {
-		ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->name);
+		ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
 		ast_channel_unlock(channel);
 		ao2_cleanup(channel);
 		return -1;
@@ -383,18 +383,18 @@
 
 	ast_channel_unlock(channel);
 
-	print_name = ast_strdupa(snapshot->name);
+	print_name = ast_strdupa(snapshot->base->name);
 	/* Skip the PJSIP/.  We know what channel type it is and we need the space. */
 	print_name += 6;
 
-	ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, 32);
+	ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->base->creationtime.tv_sec, print_time, 32);
 
 	if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
-		ast_str_append(&context->output_buffer, 0, "%s direct media\n", snapshot->name);
+		ast_str_append(&context->output_buffer, 0, "%s direct media\n", snapshot->base->name);
 	} else {
 		ast_str_append(&context->output_buffer, 0,
 			" %8.8s %-18.18s %-8.8s %-6.6s %6u%s %6u%s %3u %7.3f %6u%s %6u%s %3u %7.3f %7.3f\n",
-			snapshot->bridgeid,
+			snapshot->bridge->id,
 			print_name,
 			print_time,
 			codec_in_use,
diff --git a/configs/samples/ari.conf.sample b/configs/samples/ari.conf.sample
index 8729b1e..5ce3166 100644
--- a/configs/samples/ari.conf.sample
+++ b/configs/samples/ari.conf.sample
@@ -17,6 +17,8 @@
 ; Display certain channel variables every time a channel-oriented
 ; event is emitted:
 ;
+; Note that this does incur a performance penalty and should be avoided if possible.
+;
 ;channelvars = var1,var2,var3
 
 ;[username]
diff --git a/configs/samples/manager.conf.sample b/configs/samples/manager.conf.sample
index 989441a..405e0d3 100644
--- a/configs/samples/manager.conf.sample
+++ b/configs/samples/manager.conf.sample
@@ -55,6 +55,8 @@
 ; Display certain channel variables every time a channel-oriented
 ; event is emitted:
 ;
+; Note that this does incur a performance penalty and should be avoided if possible.
+;
 ;channelvars = var1,var2,var3
 
 ; debug = on	; enable some debugging info in AMI messages (default off).
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 9627ae2..58a4879 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -4258,6 +4258,7 @@
 ast_callid ast_channel_callid(const struct ast_channel *chan);
 struct ast_channel_snapshot *ast_channel_snapshot(const struct ast_channel *chan);
 void ast_channel_snapshot_set(struct ast_channel *chan, struct ast_channel_snapshot *snapshot);
+struct ast_flags *ast_channel_snapshot_segment_flags(struct ast_channel *chan);
 
 /*!
  * \pre chan is locked
diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h
index 2aeff6f..c90470a 100644
--- a/include/asterisk/stasis_channels.h
+++ b/include/asterisk/stasis_channels.h
@@ -29,50 +29,133 @@
  */
 
 /*!
+ * \since 17
+ * \brief Channel snapshot invalidation flags, used to force generation of segments
+ */
+enum ast_channel_snapshot_segment_invalidation {
+	/*! Invalidate the bridge segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_BRIDGE = (1 << 1),
+	/*! Invalidate the dialplan segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN = (1 << 2),
+	/*! Invalidate the connected segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_CONNECTED = (1 << 3),
+	/*! Invalidate the caller segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER = (1 << 4),
+	/*! Invalidate the hangup segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_HANGUP = (1 << 5),
+	/*! Invalidate the peer segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_PEER = (1 << 6),
+	/*! Invalidate the base segment */
+	AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE = (1 << 7),
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing bridge information for a channel snapshot.
+ */
+struct ast_channel_snapshot_bridge {
+	char id[0]; /*!< Unique Bridge Identifier */
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing dialplan information for a channel snapshot.
+ */
+struct ast_channel_snapshot_dialplan {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(appl);             /*!< Current application */
+		AST_STRING_FIELD(data);             /*!< Data passed to current application */
+		AST_STRING_FIELD(context);          /*!< Current extension context */
+		AST_STRING_FIELD(exten);            /*!< Current extension number */
+	);
+	int priority; /*!< Current extension priority */
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing caller information for a channel snapshot.
+ */
+struct ast_channel_snapshot_caller {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);           /*!< Caller ID Name */
+		AST_STRING_FIELD(number);         /*!< Caller ID Number */
+		AST_STRING_FIELD(dnid);           /*!< Dialed ID Number */
+		AST_STRING_FIELD(dialed_subaddr); /*!< Dialed subaddress */
+		AST_STRING_FIELD(ani);            /*!< Caller ID ANI Number */
+		AST_STRING_FIELD(rdnis);          /*!< Caller ID RDNIS Number */
+		AST_STRING_FIELD(subaddr);        /*!< Caller subaddress */
+	);
+	int pres; /*!< Caller ID presentation. */
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing connected information for a channel snapshot.
+ */
+struct ast_channel_snapshot_connected {
+	char *number; /*!< Connected Line Number */
+	char name[0]; /*!< Connected Line Name */
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing base information for a channel snapshot.
+ */
+struct ast_channel_snapshot_base {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);        /*!< ASCII unique channel name */
+		AST_STRING_FIELD(uniqueid);    /*!< Unique Channel Identifier */
+		AST_STRING_FIELD(accountcode); /*!< Account code for billing */
+		AST_STRING_FIELD(userfield);   /*!< Userfield for CEL billing */
+		AST_STRING_FIELD(language);    /*!< The default spoken language for the channel */
+		AST_STRING_FIELD(type);        /*!< Type of channel technology */
+	);
+	struct timeval creationtime; /*!< The time of channel creation */
+	int tech_properties;         /*!< Properties of the channel's technology */
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing peer information for a channel snapshot.
+ */
+struct ast_channel_snapshot_peer {
+	char *linkedid;   /*!< Linked Channel Identifier -- gets propagated by linkage */
+	char account[0]; /*!< Peer account code for billing */
+};
+
+/*!
+ * \since 17
+ * \brief Structure containing hangup information for a channel snapshot.
+ */
+struct ast_channel_snapshot_hangup {
+	int cause;      /*!< Why is the channel hanged up. See causes.h */
+	char source[0]; /*!< Who is responsible for hanging up this channel */
+};
+
+/*!
  * \since 12
  * \brief Structure representing a snapshot of channel state.
  *
  * While not enforced programmatically, this object is shared across multiple
  * threads, and should be treated as an immutable object.
+ *
+ * It is guaranteed that the segments of this snapshot will always exist
+ * when accessing the snapshot.
  */
 struct ast_channel_snapshot {
-	AST_DECLARE_STRING_FIELDS(
-		AST_STRING_FIELD(name);             /*!< ASCII unique channel name */
-		AST_STRING_FIELD(uniqueid);         /*!< Unique Channel Identifier */
-		AST_STRING_FIELD(linkedid);         /*!< Linked Channel Identifier -- gets propagated by linkage */
-		AST_STRING_FIELD(appl);             /*!< Current application */
-		AST_STRING_FIELD(data);             /*!< Data passed to current application */
-		AST_STRING_FIELD(context);          /*!< Dialplan: Current extension context */
-		AST_STRING_FIELD(exten);            /*!< Dialplan: Current extension number */
-		AST_STRING_FIELD(accountcode);      /*!< Account code for billing */
-		AST_STRING_FIELD(peeraccount);      /*!< Peer account code for billing */
-		AST_STRING_FIELD(userfield);        /*!< Userfield for CEL billing */
-		AST_STRING_FIELD(hangupsource);     /*!< Who is responsible for hanging up this channel */
-		AST_STRING_FIELD(caller_name);      /*!< Caller ID Name */
-		AST_STRING_FIELD(caller_number);    /*!< Caller ID Number */
-		AST_STRING_FIELD(caller_dnid);      /*!< Dialed ID Number */
-		AST_STRING_FIELD(caller_ani);       /*!< Caller ID ANI Number */
-		AST_STRING_FIELD(caller_rdnis);     /*!< Caller ID RDNIS Number */
-		AST_STRING_FIELD(caller_subaddr);   /*!< Caller subaddress */
-		AST_STRING_FIELD(dialed_subaddr);   /*!< Dialed subaddress */
-		AST_STRING_FIELD(connected_name);   /*!< Connected Line Name */
-		AST_STRING_FIELD(connected_number); /*!< Connected Line Number */
-		AST_STRING_FIELD(language);         /*!< The default spoken language for the channel */
-		AST_STRING_FIELD(bridgeid);         /*!< Unique Bridge Identifier */
-		AST_STRING_FIELD(type);             /*!< Type of channel technology */
-	);
-
-	struct timeval creationtime;            /*!< The time of channel creation */
-	enum ast_channel_state state;           /*!< State of line */
-	int priority;                           /*!< Dialplan: Current extension priority */
-	int amaflags;                           /*!< AMA flags for billing */
-	int hangupcause;                        /*!< Why is the channel hanged up. See causes.h */
-	int caller_pres;                        /*!< Caller ID presentation. */
-	struct ast_flags flags;                 /*!< channel flags of AST_FLAG_ type */
-	struct ast_flags softhangup_flags;      /*!< softhangup channel flags */
-	struct varshead *manager_vars;          /*!< Variables to be appended to manager events */
-	int tech_properties;                    /*!< Properties of the channel's technology */
-	struct varshead *ari_vars;              /*!< Variables to be appended to ARI events */
+	struct ast_channel_snapshot_base *base;           /*!< Base information about the channel */
+	struct ast_channel_snapshot_peer *peer;           /*!< Peer information */
+	struct ast_channel_snapshot_caller *caller;       /*!< Information about the caller */
+	struct ast_channel_snapshot_connected *connected; /*!< Information about who this channel is connected to */
+	struct ast_channel_snapshot_bridge *bridge;       /*!< Information about the bridge */
+	struct ast_channel_snapshot_dialplan *dialplan;   /*!< Information about the dialplan */
+	struct ast_channel_snapshot_hangup *hangup;       /*!< Hangup information */
+	enum ast_channel_state state;                     /*!< State of line */
+	int amaflags;                                     /*!< AMA flags for billing */
+	struct ast_flags flags;                           /*!< channel flags of AST_FLAG_ type */
+	struct ast_flags softhangup_flags;                /*!< softhangup channel flags */
+	struct varshead *manager_vars;                    /*!< Variables to be appended to manager events */
+	struct varshead *ari_vars;                        /*!< Variables to be appended to ARI events */
 };
 
 /*!
@@ -360,6 +443,18 @@
 void ast_channel_stage_snapshot_done(struct ast_channel *chan);
 
 /*!
+ * \since 17
+ * \brief Invalidate a channel snapshot segment from being reused
+ *
+ * \pre chan is locked
+ *
+ * \param chan Channel to invalidate the segment on.
+ * \param segment The segment to invalidate.
+ */
+void ast_channel_snapshot_invalidate_segment(struct ast_channel *chan,
+	enum ast_channel_snapshot_segment_invalidation segment);
+
+/*!
  * \since 12
  * \brief Publish a \ref ast_channel_snapshot for a channel.
  *
diff --git a/main/bridge.c b/main/bridge.c
index 024c6ab..a65927d 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -5157,7 +5157,7 @@
 		return 0;
 	}
 
-	ast_cli(a->fd, "Channel: %s\n", snapshot->name);
+	ast_cli(a->fd, "Channel: %s\n", snapshot->base->name);
 	ao2_ref(snapshot, -1);
 
 	return 0;
diff --git a/main/cdr.c b/main/cdr.c
index e321c22..2e3b205 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -803,7 +803,7 @@
 static void cdr_object_transition_state(struct cdr_object *cdr, struct cdr_object_fn_table *fn_table)
 {
 	CDR_DEBUG("%p - Transitioning CDR for %s from state %s to %s\n",
-		cdr, cdr->party_a.snapshot->name,
+		cdr, cdr->party_a.snapshot->base->name,
 		cdr->fn_table ? cdr->fn_table->name : "NONE", fn_table->name);
 	cdr->fn_table = fn_table;
 	if (cdr->fn_table->init_function) {
@@ -938,9 +938,9 @@
 {
 	ao2_lock(active_cdrs_all);
 	if (cdr->party_b.snapshot) {
-		if (strcasecmp(cdr->party_b_name, cdr->party_b.snapshot->name)) {
+		if (strcasecmp(cdr->party_b_name, cdr->party_b.snapshot->base->name)) {
 			ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
-			ast_string_field_set(cdr, party_b_name, cdr->party_b.snapshot->name);
+			ast_string_field_set(cdr, party_b_name, cdr->party_b.snapshot->base->name);
 			ao2_link_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
 		}
 	} else {
@@ -1039,16 +1039,16 @@
 		ao2_cleanup(cdr);
 		return NULL;
 	}
-	ast_string_field_set(cdr, uniqueid, chan->uniqueid);
-	ast_string_field_set(cdr, name, chan->name);
-	ast_string_field_set(cdr, linkedid, chan->linkedid);
+	ast_string_field_set(cdr, uniqueid, chan->base->uniqueid);
+	ast_string_field_set(cdr, name, chan->base->name);
+	ast_string_field_set(cdr, linkedid, chan->peer->linkedid);
 	cdr->disposition = AST_CDR_NULL;
 	cdr->sequence = ast_atomic_fetchadd_int(&global_cdr_sequence, +1);
 
 	cdr->party_a.snapshot = chan;
 	ao2_t_ref(cdr->party_a.snapshot, +1, "bump snapshot during CDR creation");
 
-	CDR_DEBUG("%p - Created CDR for channel %s\n", cdr, chan->name);
+	CDR_DEBUG("%p - Created CDR for channel %s\n", cdr, chan->base->name);
 
 	cdr_object_transition_state(cdr, &single_state_fn_table);
 
@@ -1145,11 +1145,11 @@
 	 * will attempt to clear the application and restore the dummy originate application
 	 * of "AppDialX". Ignore application changes to AppDialX as a result.
 	 */
-	if (strcmp(new_snapshot->appl, old_snapshot->appl)
-		&& strncasecmp(new_snapshot->appl, "appdial", 7)
-		&& (strcmp(new_snapshot->context, old_snapshot->context)
-			|| strcmp(new_snapshot->exten, old_snapshot->exten)
-			|| new_snapshot->priority != old_snapshot->priority)) {
+	if (strcmp(new_snapshot->dialplan->appl, old_snapshot->dialplan->appl)
+		&& strncasecmp(new_snapshot->dialplan->appl, "appdial", 7)
+		&& (strcmp(new_snapshot->dialplan->context, old_snapshot->dialplan->context)
+			|| strcmp(new_snapshot->dialplan->exten, old_snapshot->dialplan->exten)
+			|| new_snapshot->dialplan->priority != old_snapshot->dialplan->priority)) {
 		return 1;
 	}
 
@@ -1196,11 +1196,11 @@
 
 	/* Neither party is dialed and neither has the Party A flag - defer to
 	 * creation time */
-	if (left->snapshot->creationtime.tv_sec < right->snapshot->creationtime.tv_sec) {
+	if (left->snapshot->base->creationtime.tv_sec < right->snapshot->base->creationtime.tv_sec) {
 		return left;
-	} else if (left->snapshot->creationtime.tv_sec > right->snapshot->creationtime.tv_sec) {
+	} else if (left->snapshot->base->creationtime.tv_sec > right->snapshot->base->creationtime.tv_sec) {
 		return right;
-	} else if (left->snapshot->creationtime.tv_usec > right->snapshot->creationtime.tv_usec) {
+	} else if (left->snapshot->base->creationtime.tv_usec > right->snapshot->base->creationtime.tv_usec) {
 		return right;
 	} else {
 		/* Okay, fine, take the left one */
@@ -1285,7 +1285,7 @@
 		/* Don't create records for CDRs where the party A was a dialed channel */
 		if (snapshot_is_dialed(it_cdr->party_a.snapshot) && !it_cdr->party_b.snapshot) {
 			ast_debug(1, "CDR for %s is dialed and has no Party B; discarding\n",
-				it_cdr->party_a.snapshot->name);
+				it_cdr->party_a.snapshot->base->name);
 			continue;
 		}
 
@@ -1300,12 +1300,12 @@
 
 		/* Party A */
 		ast_assert(party_a != NULL);
-		ast_copy_string(cdr_copy->accountcode, party_a->accountcode, sizeof(cdr_copy->accountcode));
+		ast_copy_string(cdr_copy->accountcode, party_a->base->accountcode, sizeof(cdr_copy->accountcode));
 		cdr_copy->amaflags = party_a->amaflags;
-		ast_copy_string(cdr_copy->channel, party_a->name, sizeof(cdr_copy->channel));
-		ast_callerid_merge(cdr_copy->clid, sizeof(cdr_copy->clid), party_a->caller_name, party_a->caller_number, "");
-		ast_copy_string(cdr_copy->src, party_a->caller_number, sizeof(cdr_copy->src));
-		ast_copy_string(cdr_copy->uniqueid, party_a->uniqueid, sizeof(cdr_copy->uniqueid));
+		ast_copy_string(cdr_copy->channel, party_a->base->name, sizeof(cdr_copy->channel));
+		ast_callerid_merge(cdr_copy->clid, sizeof(cdr_copy->clid), party_a->caller->name, party_a->caller->number, "");
+		ast_copy_string(cdr_copy->src, party_a->caller->number, sizeof(cdr_copy->src));
+		ast_copy_string(cdr_copy->uniqueid, party_a->base->uniqueid, sizeof(cdr_copy->uniqueid));
 		ast_copy_string(cdr_copy->lastapp, it_cdr->appl, sizeof(cdr_copy->lastapp));
 		ast_copy_string(cdr_copy->lastdata, it_cdr->data, sizeof(cdr_copy->lastdata));
 		ast_copy_string(cdr_copy->dst, it_cdr->exten, sizeof(cdr_copy->dst));
@@ -1313,8 +1313,8 @@
 
 		/* Party B */
 		if (party_b) {
-			ast_copy_string(cdr_copy->dstchannel, party_b->name, sizeof(cdr_copy->dstchannel));
-			ast_copy_string(cdr_copy->peeraccount, party_b->accountcode, sizeof(cdr_copy->peeraccount));
+			ast_copy_string(cdr_copy->dstchannel, party_b->base->name, sizeof(cdr_copy->dstchannel));
+			ast_copy_string(cdr_copy->peeraccount, party_b->base->accountcode, sizeof(cdr_copy->peeraccount));
 			if (!ast_strlen_zero(it_cdr->party_b.userfield)) {
 				snprintf(cdr_copy->userfield, sizeof(cdr_copy->userfield), "%s;%s", it_cdr->party_a.userfield, it_cdr->party_b.userfield);
 			}
@@ -1375,8 +1375,8 @@
 	struct ast_cdr *pub_cdr;
 
 	CDR_DEBUG("%p - Dispatching CDR for Party A %s, Party B %s\n", cdr,
-		cdr->party_a.snapshot->name,
-		cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<none>");
+		cdr->party_a.snapshot->base->name,
+		cdr->party_b.snapshot ? cdr->party_b.snapshot->base->name : "<none>");
 	pub_cdr = cdr_object_create_public_records(cdr);
 	cdr_detach(pub_cdr);
 }
@@ -1434,10 +1434,10 @@
 	if (cdr->disposition == AST_CDR_NULL) {
 		if (!ast_tvzero(cdr->answer)) {
 			cdr->disposition = AST_CDR_ANSWERED;
-		} else if (cdr->party_a.snapshot->hangupcause) {
-			cdr_object_set_disposition(cdr, cdr->party_a.snapshot->hangupcause);
-		} else if (cdr->party_b.snapshot && cdr->party_b.snapshot->hangupcause) {
-			cdr_object_set_disposition(cdr, cdr->party_b.snapshot->hangupcause);
+		} else if (cdr->party_a.snapshot->hangup->cause) {
+			cdr_object_set_disposition(cdr, cdr->party_a.snapshot->hangup->cause);
+		} else if (cdr->party_b.snapshot && cdr->party_b.snapshot->hangup->cause) {
+			cdr_object_set_disposition(cdr, cdr->party_b.snapshot->hangup->cause);
 		} else {
 			cdr->disposition = AST_CDR_FAILED;
 		}
@@ -1445,7 +1445,7 @@
 
 	/* tv_usec is suseconds_t, which could be int or long */
 	ast_debug(1, "Finalized CDR for %s - start %ld.%06ld answer %ld.%06ld end %ld.%06ld dispo %s\n",
-			cdr->party_a.snapshot->name,
+			cdr->party_a.snapshot->base->name,
 			(long)cdr->start.tv_sec,
 			(long)cdr->start.tv_usec,
 			(long)cdr->answer.tv_sec,
@@ -1491,19 +1491,19 @@
 static void cdr_object_update_cid(struct cdr_object_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot)
 {
 	if (!old_snapshot->snapshot) {
-		set_variable(&old_snapshot->variables, "dnid", new_snapshot->caller_dnid);
-		set_variable(&old_snapshot->variables, "callingsubaddr", new_snapshot->caller_subaddr);
-		set_variable(&old_snapshot->variables, "calledsubaddr", new_snapshot->dialed_subaddr);
+		set_variable(&old_snapshot->variables, "dnid", new_snapshot->caller->dnid);
+		set_variable(&old_snapshot->variables, "callingsubaddr", new_snapshot->caller->subaddr);
+		set_variable(&old_snapshot->variables, "calledsubaddr", new_snapshot->caller->dialed_subaddr);
 		return;
 	}
-	if (strcmp(old_snapshot->snapshot->caller_dnid, new_snapshot->caller_dnid)) {
-		set_variable(&old_snapshot->variables, "dnid", new_snapshot->caller_dnid);
+	if (strcmp(old_snapshot->snapshot->caller->dnid, new_snapshot->caller->dnid)) {
+		set_variable(&old_snapshot->variables, "dnid", new_snapshot->caller->dnid);
 	}
-	if (strcmp(old_snapshot->snapshot->caller_subaddr, new_snapshot->caller_subaddr)) {
-		set_variable(&old_snapshot->variables, "callingsubaddr", new_snapshot->caller_subaddr);
+	if (strcmp(old_snapshot->snapshot->caller->subaddr, new_snapshot->caller->subaddr)) {
+		set_variable(&old_snapshot->variables, "callingsubaddr", new_snapshot->caller->subaddr);
 	}
-	if (strcmp(old_snapshot->snapshot->dialed_subaddr, new_snapshot->dialed_subaddr)) {
-		set_variable(&old_snapshot->variables, "calledsubaddr", new_snapshot->dialed_subaddr);
+	if (strcmp(old_snapshot->snapshot->caller->dialed_subaddr, new_snapshot->caller->dialed_subaddr)) {
+		set_variable(&old_snapshot->variables, "calledsubaddr", new_snapshot->caller->dialed_subaddr);
 	}
 }
 
@@ -1524,7 +1524,7 @@
 
 static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
-	ast_assert(strcasecmp(snapshot->name, cdr->party_a.snapshot->name) == 0);
+	ast_assert(strcasecmp(snapshot->base->name, cdr->party_a.snapshot->base->name) == 0);
 
 	/* Finalize the CDR if we're in hangup logic and we're set to do so */
 	if (ast_test_flag(&snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)
@@ -1539,11 +1539,11 @@
 	 */
 	if (!ast_test_flag(&snapshot->flags, AST_FLAG_SUBROUTINE_EXEC)
 		|| ast_test_flag(&snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
-		if (strcmp(cdr->context, snapshot->context)) {
-			ast_string_field_set(cdr, context, snapshot->context);
+		if (strcmp(cdr->context, snapshot->dialplan->context)) {
+			ast_string_field_set(cdr, context, snapshot->dialplan->context);
 		}
-		if (strcmp(cdr->exten, snapshot->exten)) {
-			ast_string_field_set(cdr, exten, snapshot->exten);
+		if (strcmp(cdr->exten, snapshot->dialplan->exten)) {
+			ast_string_field_set(cdr, exten, snapshot->dialplan->exten);
 		}
 	}
 
@@ -1555,13 +1555,13 @@
 	 * here.
 	 */
 	if (!ast_test_flag(&cdr->flags, AST_CDR_LOCK_APP)
-		&& !ast_strlen_zero(snapshot->appl)
-		&& (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))) {
-		if (strcmp(cdr->appl, snapshot->appl)) {
-			ast_string_field_set(cdr, appl, snapshot->appl);
+		&& !ast_strlen_zero(snapshot->dialplan->appl)
+		&& (strncasecmp(snapshot->dialplan->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))) {
+		if (strcmp(cdr->appl, snapshot->dialplan->appl)) {
+			ast_string_field_set(cdr, appl, snapshot->dialplan->appl);
 		}
-		if (strcmp(cdr->data, snapshot->data)) {
-			ast_string_field_set(cdr, data, snapshot->data);
+		if (strcmp(cdr->data, snapshot->dialplan->data)) {
+			ast_string_field_set(cdr, data, snapshot->dialplan->data);
 		}
 
 		/* Dial (app_dial) is a special case. Because pre-dial handlers, which
@@ -1569,13 +1569,13 @@
 		 * something people typically don't want to see, if we see a channel enter
 		 * into Dial here, we set the appl/data accordingly and lock it.
 		 */
-		if (!strcmp(snapshot->appl, "Dial")) {
+		if (!strcmp(snapshot->dialplan->appl, "Dial")) {
 			ast_set_flag(&cdr->flags, AST_CDR_LOCK_APP);
 		}
 	}
 
-	if (strcmp(cdr->linkedid, snapshot->linkedid)) {
-		ast_string_field_set(cdr, linkedid, snapshot->linkedid);
+	if (strcmp(cdr->linkedid, snapshot->peer->linkedid)) {
+		ast_string_field_set(cdr, linkedid, snapshot->peer->linkedid);
 	}
 	cdr_object_check_party_a_answer(cdr);
 	cdr_object_check_party_a_hangup(cdr);
@@ -1603,7 +1603,7 @@
 {
 	char park_info[128];
 
-	ast_assert(!strcasecmp(parking_info->parkee->name, cdr->party_a.snapshot->name));
+	ast_assert(!strcasecmp(parking_info->parkee->base->name, cdr->party_a.snapshot->base->name));
 
 	/* Update Party A information regardless */
 	cdr->fn_table->process_party_a(cdr, parking_info->parkee);
@@ -1637,25 +1637,25 @@
 
 static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
 {
-	if (caller && !strcasecmp(cdr->party_a.snapshot->name, caller->name)) {
+	if (caller && !strcasecmp(cdr->party_a.snapshot->base->name, caller->base->name)) {
 		base_process_party_a(cdr, caller);
 		CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
-			cdr->party_a.snapshot->name);
+			cdr->party_a.snapshot->base->name);
 		cdr_object_swap_snapshot(&cdr->party_b, peer);
 		cdr_all_relink(cdr);
 		CDR_DEBUG("%p - Updated Party B %s snapshot\n", cdr,
-			cdr->party_b.snapshot->name);
+			cdr->party_b.snapshot->base->name);
 
 		/* If we have two parties, lock the application that caused the
 		 * two parties to be associated. This prevents mid-call event
 		 * macros/gosubs from perturbing the CDR application/data
 		 */
 		ast_set_flag(&cdr->flags, AST_CDR_LOCK_APP);
-	} else if (!strcasecmp(cdr->party_a.snapshot->name, peer->name)) {
+	} else if (!strcasecmp(cdr->party_a.snapshot->base->name, peer->base->name)) {
 		/* We're the entity being dialed, i.e., outbound origination */
 		base_process_party_a(cdr, peer);
 		CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
-			cdr->party_a.snapshot->name);
+			cdr->party_a.snapshot->base->name);
 	}
 
 	cdr_object_transition_state(cdr, &dial_state_fn_table);
@@ -1680,15 +1680,15 @@
 	struct cdr_object_snapshot *party_a;
 
 	/* Don't match on ourselves */
-	if (!strcasecmp(cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name)) {
+	if (!strcasecmp(cdr->party_a.snapshot->base->name, cand_cdr->party_a.snapshot->base->name)) {
 		return 1;
 	}
 
 	/* Try the candidate CDR's Party A first */
 	party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_a);
-	if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
+	if (!strcasecmp(party_a->snapshot->base->name, cdr->party_a.snapshot->base->name)) {
 		CDR_DEBUG("%p - Party A %s has new Party B %s\n",
-			cdr, cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name);
+			cdr, cdr->party_a.snapshot->base->name, cand_cdr->party_a.snapshot->base->name);
 		cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
 		cdr_all_relink(cdr);
 		if (!cand_cdr->party_b.snapshot) {
@@ -1703,13 +1703,13 @@
 
 	/* Try their Party B, unless it's us */
 	if (!cand_cdr->party_b.snapshot
-		|| !strcasecmp(cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name)) {
+		|| !strcasecmp(cdr->party_a.snapshot->base->name, cand_cdr->party_b.snapshot->base->name)) {
 		return 1;
 	}
 	party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_b);
-	if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
+	if (!strcasecmp(party_a->snapshot->base->name, cdr->party_a.snapshot->base->name)) {
 		CDR_DEBUG("%p - Party A %s has new Party B %s\n",
-			cdr, cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name);
+			cdr, cdr->party_a.snapshot->base->name, cand_cdr->party_b.snapshot->base->name);
 		cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_b);
 		cdr_all_relink(cdr);
 		return 0;
@@ -1788,7 +1788,7 @@
 {
 	ast_assert(snapshot != NULL);
 	ast_assert(cdr->party_b.snapshot
-		&& !strcasecmp(cdr->party_b.snapshot->name, snapshot->name));
+		&& !strcasecmp(cdr->party_b.snapshot->base->name, snapshot->base->name));
 
 	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
@@ -1839,11 +1839,11 @@
 	} else {
 		party_a = peer;
 	}
-	ast_assert(!strcasecmp(cdr->party_a.snapshot->name, party_a->name));
+	ast_assert(!strcasecmp(cdr->party_a.snapshot->base->name, party_a->base->name));
 	cdr_object_swap_snapshot(&cdr->party_a, party_a);
 
 	if (cdr->party_b.snapshot) {
-		if (strcasecmp(cdr->party_b.snapshot->name, peer->name)) {
+		if (strcasecmp(cdr->party_b.snapshot->base->name, peer->base->name)) {
 			/* Not the status for this CDR - defer back to the message router */
 			return 1;
 		}
@@ -1901,7 +1901,7 @@
 				}
 
 				/* Skip any records that aren't our Party B */
-				if (strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name)) {
+				if (strcasecmp(cdr->party_b.snapshot->base->name, cand_cdr->party_a.snapshot->base->name)) {
 					continue;
 				}
 				cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
@@ -1982,7 +1982,7 @@
 static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
 	ast_assert(cdr->party_b.snapshot
-		&& !strcasecmp(cdr->party_b.snapshot->name, snapshot->name));
+		&& !strcasecmp(cdr->party_b.snapshot->base->name, snapshot->base->name));
 
 	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
@@ -1997,9 +1997,9 @@
 	if (strcmp(cdr->bridge, bridge->uniqueid)) {
 		return 1;
 	}
-	if (strcasecmp(cdr->party_a.snapshot->name, channel->name)
+	if (strcasecmp(cdr->party_a.snapshot->base->name, channel->base->name)
 		&& cdr->party_b.snapshot
-		&& strcasecmp(cdr->party_b.snapshot->name, channel->name)) {
+		&& strcasecmp(cdr->party_b.snapshot->base->name, channel->base->name)) {
 		return 1;
 	}
 	cdr_object_transition_state(cdr, &finalized_state_fn_table);
@@ -2011,7 +2011,7 @@
 
 static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
-	if (strcasecmp(cdr->party_a.snapshot->name, channel->name)) {
+	if (strcasecmp(cdr->party_a.snapshot->base->name, channel->base->name)) {
 		return 1;
 	}
 	cdr_object_transition_state(cdr, &finalized_state_fn_table);
@@ -2043,7 +2043,7 @@
  */
 static int filter_channel_snapshot(struct ast_channel_snapshot *snapshot)
 {
-	return snapshot->tech_properties & AST_CHAN_TP_INTERNAL;
+	return snapshot->base->tech_properties & AST_CHAN_TP_INTERNAL;
 }
 
 /*!
@@ -2114,19 +2114,19 @@
 
 	CDR_DEBUG("Dial %s message for %s, %s: %u.%08u\n",
 		ast_strlen_zero(dial_status) ? "Begin" : "End",
-		caller ? caller->name : "(none)",
-		peer ? peer->name : "(none)",
+		caller ? caller->base->name : "(none)",
+		peer ? peer->base->name : "(none)",
 		(unsigned int)stasis_message_timestamp(message)->tv_sec,
 		(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
 	/* Figure out who is running this show */
 	if (caller) {
-		cdr = ao2_find(active_cdrs_master, caller->uniqueid, OBJ_SEARCH_KEY);
+		cdr = ao2_find(active_cdrs_master, caller->base->uniqueid, OBJ_SEARCH_KEY);
 	} else {
-		cdr = ao2_find(active_cdrs_master, peer->uniqueid, OBJ_SEARCH_KEY);
+		cdr = ao2_find(active_cdrs_master, peer->base->uniqueid, OBJ_SEARCH_KEY);
 	}
 	if (!cdr) {
-		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", caller ? caller->name : peer->name);
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", caller ? caller->base->name : peer->base->name);
 		ast_assert(0);
 		return;
 	}
@@ -2139,8 +2139,8 @@
 			}
 			CDR_DEBUG("%p - Processing Dial Begin message for channel %s, peer %s\n",
 				it_cdr,
-				caller ? caller->name : "(none)",
-				peer ? peer->name : "(none)");
+				caller ? caller->base->name : "(none)",
+				peer ? peer->base->name : "(none)");
 			res &= it_cdr->fn_table->process_dial_begin(it_cdr,
 					caller,
 					peer);
@@ -2150,8 +2150,8 @@
 			}
 			CDR_DEBUG("%p - Processing Dial End message for channel %s, peer %s\n",
 				it_cdr,
-				caller ? caller->name : "(none)",
-				peer ? peer->name : "(none)");
+				caller ? caller->base->name : "(none)",
+				peer ? peer->base->name : "(none)");
 			it_cdr->fn_table->process_dial_end(it_cdr,
 					caller,
 					peer,
@@ -2185,7 +2185,7 @@
 		 * is consistent with the key.
 		 */
 		ast_assert(cdr->party_b.snapshot
-			&& !strcasecmp(cdr->party_b.snapshot->name, party_b->name));
+			&& !strcasecmp(cdr->party_b.snapshot->base->name, party_b->base->name));
 #endif
 
 		/* Don't transition to the finalized state - let the Party A do
@@ -2210,11 +2210,11 @@
 		 * asserts the snapshot to be this way.
 		 */
 		if (!cdr->party_b.snapshot
-			|| strcasecmp(cdr->party_b.snapshot->name, party_b->name)) {
+			|| strcasecmp(cdr->party_b.snapshot->base->name, party_b->base->name)) {
 			ast_log(LOG_NOTICE,
 				"CDR for Party A %s(%s) has inconsistent Party B %s name.  Message can be ignored but this shouldn't happen.\n",
 				cdr->linkedid,
-				cdr->party_a.snapshot->name,
+				cdr->party_a.snapshot->base->name,
 				cdr->party_b_name);
 			return 0;
 		}
@@ -2236,7 +2236,7 @@
 	}
 
 	/* Auto-fall through will increment the priority but have no application */
-	if (ast_strlen_zero(new_snapshot->appl)) {
+	if (ast_strlen_zero(new_snapshot->dialplan->appl)) {
 		return 0;
 	}
 
@@ -2272,12 +2272,12 @@
 		cdr->is_root = 1;
 		ao2_link(active_cdrs_master, cdr);
 	} else {
-		cdr = ao2_find(active_cdrs_master, update->new_snapshot->uniqueid, OBJ_SEARCH_KEY);
+		cdr = ao2_find(active_cdrs_master, update->new_snapshot->base->uniqueid, OBJ_SEARCH_KEY);
 	}
 
 	/* Handle Party A */
 	if (!cdr) {
-		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", update->new_snapshot->name);
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", update->new_snapshot->base->name);
 		ast_assert(0);
 	} else {
 		int all_reject = 1;
@@ -2303,7 +2303,7 @@
 
 	if (ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {
 		ao2_lock(cdr);
-		CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, update->old_snapshot->name);
+		CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, update->old_snapshot->base->name);
 		for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 			cdr_object_finalize(it_cdr);
 		}
@@ -2317,12 +2317,12 @@
 	/* Handle Party B */
 	if (update->new_snapshot) {
 		ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
-			cdr_object_update_party_b, (char *) update->new_snapshot->name, update->new_snapshot);
+			cdr_object_update_party_b, (char *) update->new_snapshot->base->name, update->new_snapshot);
 	}
 
 	if (ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {
 		ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
-			cdr_object_finalize_party_b, (char *) update->new_snapshot->name, update->new_snapshot);
+			cdr_object_finalize_party_b, (char *) update->new_snapshot->base->name, update->new_snapshot);
 	}
 
 	ao2_cleanup(cdr);
@@ -2347,7 +2347,7 @@
 		 * is consistent with the key.
 		 */
 		ast_assert(cdr->party_b.snapshot
-			&& !strcasecmp(cdr->party_b.snapshot->name, leave_data->channel->name));
+			&& !strcasecmp(cdr->party_b.snapshot->base->name, leave_data->channel->base->name));
 
 		/* It is our Party B, in our bridge. Set the end time and let the handler
 		 * transition our CDR appropriately when we leave the bridge.
@@ -2399,13 +2399,13 @@
 	}
 
 	CDR_DEBUG("Bridge Leave message for %s: %u.%08u\n",
-		channel->name,
+		channel->base->name,
 		(unsigned int)stasis_message_timestamp(message)->tv_sec,
 		(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
-	cdr = ao2_find(active_cdrs_master, channel->uniqueid, OBJ_SEARCH_KEY);
+	cdr = ao2_find(active_cdrs_master, channel->base->uniqueid, OBJ_SEARCH_KEY);
 	if (!cdr) {
-		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->base->name);
 		ast_assert(0);
 		return;
 	}
@@ -2417,7 +2417,7 @@
 			continue;
 		}
 		CDR_DEBUG("%p - Processing Bridge Leave for %s\n",
-			it_cdr, channel->name);
+			it_cdr, channel->base->name);
 		if (!it_cdr->fn_table->process_bridge_leave(it_cdr, bridge, channel)) {
 			ast_string_field_set(it_cdr, bridge, "");
 			left_bridge = 1;
@@ -2429,7 +2429,7 @@
 	if (left_bridge
 		&& strcmp(bridge->subclass, "parking")) {
 		ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
-			cdr_object_party_b_left_bridge_cb, (char *) leave_data.channel->name,
+			cdr_object_party_b_left_bridge_cb, (char *) leave_data.channel->base->name,
 			&leave_data);
 	}
 
@@ -2457,8 +2457,8 @@
 	ast_string_field_set(new_cdr, bridge, cdr->bridge);
 	cdr_object_transition_state(new_cdr, &bridge_state_fn_table);
 	CDR_DEBUG("%p - Party A %s has new Party B %s\n",
-		new_cdr, new_cdr->party_a.snapshot->name,
-		party_b->snapshot->name);
+		new_cdr, new_cdr->party_a.snapshot->base->name,
+		party_b->snapshot->base->name);
 }
 
 /*!
@@ -2487,16 +2487,16 @@
 		}
 
 		/* If the candidate is us or someone we've taken on, pass on by */
-		if (!strcasecmp(cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name)
+		if (!strcasecmp(cdr->party_a.snapshot->base->name, cand_cdr->party_a.snapshot->base->name)
 			|| (cdr->party_b.snapshot
-				&& !strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name))) {
+				&& !strcasecmp(cdr->party_b.snapshot->base->name, cand_cdr->party_a.snapshot->base->name))) {
 			break;
 		}
 
 		party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_a);
 		/* We're party A - make a new CDR, append it to us, and set the candidate as
 		 * Party B */
-		if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
+		if (!strcasecmp(party_a->snapshot->base->name, cdr->party_a.snapshot->base->name)) {
 			bridge_candidate_add_to_cdr(cdr, &cand_cdr->party_a);
 			break;
 		}
@@ -2504,12 +2504,12 @@
 		/* We're Party B. Check if we can add ourselves immediately or if we need
 		 * a new CDR for them (they already have a Party B) */
 		if (cand_cdr->party_b.snapshot
-			&& strcasecmp(cand_cdr->party_b.snapshot->name, cdr->party_a.snapshot->name)) {
+			&& strcasecmp(cand_cdr->party_b.snapshot->base->name, cdr->party_a.snapshot->base->name)) {
 			bridge_candidate_add_to_cdr(cand_cdr, &cdr->party_a);
 		} else {
 			CDR_DEBUG("%p - Party A %s has new Party B %s\n",
-				cand_cdr, cand_cdr->party_a.snapshot->name,
-				cdr->party_a.snapshot->name);
+				cand_cdr, cand_cdr->party_a.snapshot->base->name,
+				cdr->party_a.snapshot->base->name);
 			cdr_object_snapshot_copy(&cand_cdr->party_b, &cdr->party_a);
 			cdr_all_relink(cand_cdr);
 			/* It's possible that this joined at one point and was never chosen
@@ -2572,7 +2572,7 @@
 		}
 		if (it_cdr->fn_table->process_party_a) {
 			CDR_DEBUG("%p - Updating Party A %s snapshot\n", it_cdr,
-				channel->name);
+				channel->base->name);
 			it_cdr->fn_table->process_party_a(it_cdr, channel);
 		}
 	}
@@ -2609,14 +2609,14 @@
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		if (it_cdr->fn_table->process_party_a) {
 			CDR_DEBUG("%p - Updating Party A %s snapshot\n", it_cdr,
-				channel->name);
+				channel->base->name);
 			it_cdr->fn_table->process_party_a(it_cdr, channel);
 		}
 
 		/* Notify all states that they have entered a bridge */
 		if (it_cdr->fn_table->process_bridge_enter) {
 			CDR_DEBUG("%p - Processing bridge enter for %s\n", it_cdr,
-				channel->name);
+				channel->base->name);
 			result = it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel);
 			switch (result) {
 			case BRIDGE_ENTER_ONLY_PARTY:
@@ -2689,13 +2689,13 @@
 	}
 
 	CDR_DEBUG("Bridge Enter message for channel %s: %u.%08u\n",
-		channel->name,
+		channel->base->name,
 		(unsigned int)stasis_message_timestamp(message)->tv_sec,
 		(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
-	cdr = ao2_find(active_cdrs_master, channel->uniqueid, OBJ_SEARCH_KEY);
+	cdr = ao2_find(active_cdrs_master, channel->base->uniqueid, OBJ_SEARCH_KEY);
 	if (!cdr) {
-		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->base->name);
 		ast_assert(0);
 		return;
 	}
@@ -2739,13 +2739,13 @@
 	}
 
 	CDR_DEBUG("Parked Call message for channel %s: %u.%08u\n",
-		channel->name,
+		channel->base->name,
 		(unsigned int)stasis_message_timestamp(message)->tv_sec,
 		(unsigned int)stasis_message_timestamp(message)->tv_usec);
 
-	cdr = ao2_find(active_cdrs_master, channel->uniqueid, OBJ_SEARCH_KEY);
+	cdr = ao2_find(active_cdrs_master, channel->base->uniqueid, OBJ_SEARCH_KEY);
 	if (!cdr) {
-		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+		ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->base->name);
 		ast_assert(0);
 		return;
 	}
@@ -3094,8 +3094,8 @@
 	struct cdr_object *cdr = obj;
 	const char *name = arg;
 
-	if (!strcasecmp(cdr->party_a.snapshot->name, name) ||
-			(cdr->party_b.snapshot && !strcasecmp(cdr->party_b.snapshot->name, name))) {
+	if (!strcasecmp(cdr->party_a.snapshot->base->name, name) ||
+			(cdr->party_b.snapshot && !strcasecmp(cdr->party_b.snapshot->base->name, name))) {
 		return CMP_MATCH;
 	}
 	return 0;
@@ -3110,7 +3110,7 @@
 	struct cdr_object *cdr = obj;
 	const char *name = arg;
 
-	if (!strcasecmp(cdr->party_a.snapshot->name, name)) {
+	if (!strcasecmp(cdr->party_a.snapshot->base->name, name)) {
 		return CMP_MATCH;
 	}
 	return 0;
@@ -3170,10 +3170,10 @@
 			if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
 				continue;
 			}
-			if (!strcasecmp(channel_name, it_cdr->party_a.snapshot->name)) {
+			if (!strcasecmp(channel_name, it_cdr->party_a.snapshot->base->name)) {
 				headp = &it_cdr->party_a.variables;
 			} else if (it_cdr->party_b.snapshot
-				&& !strcasecmp(channel_name, it_cdr->party_b.snapshot->name)) {
+				&& !strcasecmp(channel_name, it_cdr->party_b.snapshot->base->name)) {
 				headp = &it_cdr->party_b.variables;
 			}
 			if (headp) {
@@ -3212,25 +3212,25 @@
 	struct ast_channel_snapshot *party_b = cdr_obj->party_b.snapshot;
 
 	if (!strcasecmp(name, "clid")) {
-		ast_callerid_merge(value, length, party_a->caller_name, party_a->caller_number, "");
+		ast_callerid_merge(value, length, party_a->caller->name, party_a->caller->number, "");
 	} else if (!strcasecmp(name, "src")) {
-		ast_copy_string(value, party_a->caller_number, length);
+		ast_copy_string(value, party_a->caller->number, length);
 	} else if (!strcasecmp(name, "dst")) {
-		ast_copy_string(value, party_a->exten, length);
+		ast_copy_string(value, party_a->dialplan->exten, length);
 	} else if (!strcasecmp(name, "dcontext")) {
-		ast_copy_string(value, party_a->context, length);
+		ast_copy_string(value, party_a->dialplan->context, length);
 	} else if (!strcasecmp(name, "channel")) {
-		ast_copy_string(value, party_a->name, length);
+		ast_copy_string(value, party_a->base->name, length);
 	} else if (!strcasecmp(name, "dstchannel")) {
 		if (party_b) {
-			ast_copy_string(value, party_b->name, length);
+			ast_copy_string(value, party_b->base->name, length);
 		} else {
 			ast_copy_string(value, "", length);
 		}
 	} else if (!strcasecmp(name, "lastapp")) {
-		ast_copy_string(value, party_a->appl, length);
+		ast_copy_string(value, party_a->dialplan->appl, length);
 	} else if (!strcasecmp(name, "lastdata")) {
-		ast_copy_string(value, party_a->data, length);
+		ast_copy_string(value, party_a->dialplan->data, length);
 	} else if (!strcasecmp(name, "start")) {
 		cdr_get_tv(cdr_obj->start, NULL, value, length);
 	} else if (!strcasecmp(name, "answer")) {
@@ -3246,15 +3246,15 @@
 	} else if (!strcasecmp(name, "amaflags")) {
 		snprintf(value, length, "%d", party_a->amaflags);
 	} else if (!strcasecmp(name, "accountcode")) {
-		ast_copy_string(value, party_a->accountcode, length);
+		ast_copy_string(value, party_a->base->accountcode, length);
 	} else if (!strcasecmp(name, "peeraccount")) {
 		if (party_b) {
-			ast_copy_string(value, party_b->accountcode, length);
+			ast_copy_string(value, party_b->base->accountcode, length);
 		} else {
 			ast_copy_string(value, "", length);
 		}
 	} else if (!strcasecmp(name, "uniqueid")) {
-		ast_copy_string(value, party_a->uniqueid, length);
+		ast_copy_string(value, party_a->base->uniqueid, length);
 	} else if (!strcasecmp(name, "linkedid")) {
 		ast_copy_string(value, cdr_obj->linkedid, length);
 	} else if (!strcasecmp(name, "userfield")) {
@@ -3431,7 +3431,7 @@
 		 * is consistent with the key.
 		 */
 		ast_assert(cdr->party_b.snapshot
-			&& !strcasecmp(cdr->party_b.snapshot->name, info->channel_name));
+			&& !strcasecmp(cdr->party_b.snapshot->base->name, info->channel_name));
 
 		ast_copy_string(cdr->party_b.userfield, info->userfield,
 			sizeof(cdr->party_b.userfield));
@@ -3624,7 +3624,7 @@
 		/* Copy over the basic CDR information. The Party A information is
 		 * copied over automatically as part of the append
 		 */
-		ast_debug(1, "Forking CDR for channel %s\n", cdr->party_a.snapshot->name);
+		ast_debug(1, "Forking CDR for channel %s\n", cdr->party_a.snapshot->base->name);
 		new_cdr = cdr_object_create_and_append(cdr);
 		if (!new_cdr) {
 			return -1;
@@ -3938,8 +3938,8 @@
 
 	it_cdrs = ao2_iterator_init(active_cdrs_master, 0);
 	while ((cdr = ao2_iterator_next(&it_cdrs))) {
-		if (!strncasecmp(a->word, cdr->party_a.snapshot->name, wordlen)) {
-			if (ast_cli_completion_add(ast_strdup(cdr->party_a.snapshot->name))) {
+		if (!strncasecmp(a->word, cdr->party_a.snapshot->base->name, wordlen)) {
+			if (ast_cli_completion_add(ast_strdup(cdr->party_a.snapshot->base->name))) {
 				ao2_ref(cdr, -1);
 				break;
 			}
@@ -4001,8 +4001,8 @@
 		cdr_get_tv(start_time, "%T", start_time_buffer, sizeof(start_time_buffer));
 		cdr_get_tv(answer_time, "%T", answer_time_buffer, sizeof(answer_time_buffer));
 		cdr_get_tv(end_time, "%T", end_time_buffer, sizeof(end_time_buffer));
-		ast_cli(a->fd, FORMAT_STRING, it_cdr->party_a.snapshot->name,
-				it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->name : "<none>",
+		ast_cli(a->fd, FORMAT_STRING, it_cdr->party_a.snapshot->base->name,
+				it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->base->name : "<none>",
 				it_cdr->appl,
 				start_time_buffer,
 				answer_time_buffer,
@@ -4046,7 +4046,7 @@
 		if (snapshot_is_dialed(it_cdr->party_a.snapshot)) {
 			continue;
 		}
-		ast_callerid_merge(clid, sizeof(clid), it_cdr->party_a.snapshot->caller_name, it_cdr->party_a.snapshot->caller_number, "");
+		ast_callerid_merge(clid, sizeof(clid), it_cdr->party_a.snapshot->caller->name, it_cdr->party_a.snapshot->caller->number, "");
 		if (ast_tvzero(it_cdr->end)) {
 			end = ast_tvnow();
 		} else {
@@ -4056,9 +4056,9 @@
 		cdr_get_tv(it_cdr->answer, "%T", answer_time_buffer, sizeof(answer_time_buffer));
 		cdr_get_tv(end, "%T", end_time_buffer, sizeof(end_time_buffer));
 		ast_cli(a->fd, FORMAT_STRING,
-				it_cdr->party_a.snapshot->accountcode,
+				it_cdr->party_a.snapshot->base->accountcode,
 				clid,
-				it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->name : "<none>",
+				it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->base->name : "<none>",
 				it_cdr->appl,
 				it_cdr->data,
 				start_time_buffer,
@@ -4415,8 +4415,8 @@
 	}
 	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		prnt(where, "Party A: %s; Party B: %s; Bridge %s\n",
-			it_cdr->party_a.snapshot->name,
-			it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->name : "<unknown>",
+			it_cdr->party_a.snapshot->base->name,
+			it_cdr->party_b.snapshot ? it_cdr->party_b.snapshot->base->name : "<unknown>",
 			it_cdr->bridge);
 	}
 }
@@ -4440,8 +4440,8 @@
 		return;
 	}
 	prnt(where, "Party A: %s; Party B: %s; Bridge %s",
-		cdr->party_a.snapshot->name,
-		cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<unknown>",
+		cdr->party_a.snapshot->base->name,
+		cdr->party_b.snapshot ? cdr->party_b.snapshot->base->name : "<unknown>",
 		cdr->bridge);
 }
 
diff --git a/main/cel.c b/main/cel.c
index 97e35ad..242aeff 100644
--- a/main/cel.c
+++ b/main/cel.c
@@ -528,22 +528,22 @@
 		AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
 		AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
 		AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, S_OR(userdefevname, ""),
-		AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_name,
-		AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_number,
-		AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_ani,
-		AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_rdnis,
-		AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_dnid,
-		AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, snapshot->exten,
-		AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, snapshot->context,
-		AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->name,
-		AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->appl,
-		AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, snapshot->data,
+		AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->name,
+		AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->number,
+		AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->ani,
+		AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->rdnis,
+		AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->dnid,
+		AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, snapshot->dialplan->exten,
+		AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, snapshot->dialplan->context,
+		AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->base->name,
+		AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->dialplan->appl,
+		AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, snapshot->dialplan->data,
 		AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, snapshot->amaflags,
-		AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, snapshot->accountcode,
-		AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, snapshot->peeraccount,
-		AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->uniqueid,
-		AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->linkedid,
-		AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->userfield,
+		AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, snapshot->base->accountcode,
+		AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, snapshot->peer->account,
+		AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->base->uniqueid,
+		AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->peer->linkedid,
+		AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->base->userfield,
 		AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
 		AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer, ""),
 		AST_EVENT_IE_END);
@@ -573,7 +573,7 @@
 	 * reporting on CHANNEL_START so we can track when to send LINKEDID_END */
 	if (event_type == AST_CEL_CHANNEL_START
 		&& ast_cel_track_event(AST_CEL_LINKEDID_END)) {
-		if (cel_linkedid_ref(snapshot->linkedid)) {
+		if (cel_linkedid_ref(snapshot->peer->linkedid)) {
 			return -1;
 		}
 	}
@@ -583,7 +583,7 @@
 	}
 
 	if ((event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END)
-		&& !cel_track_app(snapshot->appl)) {
+		&& !cel_track_app(snapshot->dialplan->appl)) {
 		return 0;
 	}
 
@@ -606,14 +606,14 @@
 	RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
 	struct cel_linkedid *lid;
 
-	if (!linkedids || ast_strlen_zero(snapshot->linkedid)) {
+	if (!linkedids || ast_strlen_zero(snapshot->peer->linkedid)) {
 		/* The CEL module is shutdown.  Abort. */
 		return;
 	}
 
 	ao2_lock(linkedids);
 
-	lid = ao2_find(linkedids, (void *) snapshot->linkedid, OBJ_SEARCH_KEY);
+	lid = ao2_find(linkedids, (void *) snapshot->peer->linkedid, OBJ_SEARCH_KEY);
 	if (!lid) {
 		ao2_unlock(linkedids);
 
@@ -623,7 +623,7 @@
 		 * of change to make after starting Asterisk.
 		 */
 		ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n",
-			snapshot->linkedid);
+			snapshot->peer->linkedid);
 		return;
 	}
 
@@ -898,11 +898,11 @@
 
 	if (!was_hungup && is_hungup) {
 		struct ast_json *extra;
-		struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid);
+		struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->base->uniqueid);
 
 		extra = ast_json_pack("{s: i, s: s, s: s}",
-			"hangupcause", new_snapshot->hangupcause,
-			"hangupsource", new_snapshot->hangupsource,
+			"hangupcause", new_snapshot->hangup->cause,
+			"hangupsource", new_snapshot->hangup->source,
 			"dialstatus", dialstatus ? dialstatus->dialstatus : "");
 		cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
 		ast_json_unref(extra);
@@ -929,12 +929,12 @@
 		return;
 	}
 
-	ast_assert(!ast_strlen_zero(new_snapshot->linkedid));
-	ast_assert(!ast_strlen_zero(old_snapshot->linkedid));
+	ast_assert(!ast_strlen_zero(new_snapshot->peer->linkedid));
+	ast_assert(!ast_strlen_zero(old_snapshot->peer->linkedid));
 
 	if (ast_cel_track_event(AST_CEL_LINKEDID_END)
-		&& strcmp(old_snapshot->linkedid, new_snapshot->linkedid)) {
-		cel_linkedid_ref(new_snapshot->linkedid);
+		&& strcmp(old_snapshot->peer->linkedid, new_snapshot->peer->linkedid)) {
+		cel_linkedid_ref(new_snapshot->peer->linkedid);
 		check_retire_linkedid(old_snapshot);
 	}
 }
@@ -943,17 +943,17 @@
 	struct ast_channel_snapshot *old_snapshot,
 	struct ast_channel_snapshot *new_snapshot)
 {
-	if (old_snapshot && !strcmp(old_snapshot->appl, new_snapshot->appl)) {
+	if (old_snapshot && !strcmp(old_snapshot->dialplan->appl, new_snapshot->dialplan->appl)) {
 		return;
 	}
 
 	/* old snapshot has an application, end it */
-	if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
+	if (old_snapshot && !ast_strlen_zero(old_snapshot->dialplan->appl)) {
 		cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
 	}
 
 	/* new snapshot has an application, start it */
-	if (!ast_strlen_zero(new_snapshot->appl)) {
+	if (!ast_strlen_zero(new_snapshot->dialplan->appl)) {
 		cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
 	}
 }
@@ -974,7 +974,7 @@
 	if (!snapshot) {
 		return 0;
 	}
-	return snapshot->tech_properties & AST_CHAN_TP_INTERNAL;
+	return snapshot->base->tech_properties & AST_CHAN_TP_INTERNAL;
 }
 
 static void cel_snapshot_update_cb(void *data, struct stasis_subscription *sub,
@@ -1010,7 +1010,7 @@
 		struct ast_channel_snapshot *current_snapshot;
 
 		/* Don't add the channel for which this message is being generated */
-		if (!strcmp(current_chan, chan->uniqueid)) {
+		if (!strcmp(current_chan, chan->base->uniqueid)) {
 			continue;
 		}
 
@@ -1019,7 +1019,7 @@
 			continue;
 		}
 
-		ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
+		ast_str_append(&peer_str, 0, "%s,", current_snapshot->base->name);
 		ao2_cleanup(current_snapshot);
 	}
 	ao2_iterator_destroy(&i);
@@ -1125,7 +1125,7 @@
 	if (parked_payload->retriever) {
 		extra = ast_json_pack("{s: s, s: s}",
 			"reason", reason ?: "",
-			"retriever", parked_payload->retriever->name);
+			"retriever", parked_payload->retriever->base->name);
 	} else {
 		extra = ast_json_pack("{s: s}", "reason", reason ?: "");
 	}
@@ -1147,7 +1147,7 @@
 		return;
 	}
 
-	dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY);
+	dialstatus = ao2_find(dial_statuses, snapshot->base->uniqueid, OBJ_SEARCH_KEY);
 	if (dialstatus) {
 		if (!strcasecmp(dialstatus_string, "ANSWER") && strcasecmp(dialstatus->dialstatus, "ANSWER")) {
 			/* In the case of an answer after we already have a dial status we give
@@ -1171,7 +1171,7 @@
 		return;
 	}
 
-	ast_copy_string(dialstatus->uniqueid, snapshot->uniqueid, sizeof(dialstatus->uniqueid));
+	ast_copy_string(dialstatus->uniqueid, snapshot->base->uniqueid, sizeof(dialstatus->uniqueid));
 	ast_copy_string(dialstatus->dialstatus, dialstatus_string, dialstatus_string_len);
 
 	ao2_link(dial_statuses, dialstatus);
@@ -1273,8 +1273,8 @@
 		"extension", transfer_msg->exten,
 		"context", transfer_msg->context,
 		"bridge_id", bridge_snapshot->uniqueid,
-		"transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name : "N/A",
-		"transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->uniqueid  : "N/A");
+		"transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->base->name : "N/A",
+		"transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->base->uniqueid  : "N/A");
 	if (extra) {
 		cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
 		ast_json_unref(extra);
@@ -1312,13 +1312,13 @@
 	case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
 		extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
 			"bridge1_id", bridge1->uniqueid,
-			"channel2_name", channel2->name,
-			"channel2_uniqueid", channel2->uniqueid,
+			"channel2_name", channel2->base->name,
+			"channel2_uniqueid", channel2->base->uniqueid,
 			"bridge2_id", bridge2->uniqueid,
-			"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
-			"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
-			"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
-			"transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
+			"transferee_channel_name", xfer->transferee ? xfer->transferee->base->name : "N/A",
+			"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->base->uniqueid : "N/A",
+			"transfer_target_channel_name", xfer->target ? xfer->target->base->name : "N/A",
+			"transfer_target_channel_uniqueid", xfer->target ? xfer->target->base->uniqueid : "N/A");
 		if (!extra) {
 			return;
 		}
@@ -1327,13 +1327,13 @@
 	case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
 		extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
 			"bridge1_id", bridge1->uniqueid,
-			"channel2_name", channel2->name,
-			"channel2_uniqueid", channel2->uniqueid,
+			"channel2_name", channel2->base->name,
+			"channel2_uniqueid", channel2->base->uniqueid,
 			"app", xfer->dest.app,
-			"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
-			"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
-			"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
-			"transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
+			"transferee_channel_name", xfer->transferee ? xfer->transferee->base->name : "N/A",
+			"transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->base->uniqueid : "N/A",
+			"transfer_target_channel_name", xfer->target ? xfer->target->base->name : "N/A",
+			"transfer_target_channel_uniqueid", xfer->target ? xfer->target->base->uniqueid : "N/A");
 		if (!extra) {
 			return;
 		}
@@ -1357,8 +1357,8 @@
 	}
 
 	extra = ast_json_pack("{s: s, s: s}",
-		"pickup_channel", channel->name,
-		"pickup_channel_uniqueid", channel->uniqueid);
+		"pickup_channel", channel->base->name,
+		"pickup_channel_uniqueid", channel->base->uniqueid);
 	if (!extra) {
 		return;
 	}
@@ -1381,8 +1381,8 @@
 	}
 
 	extra = ast_json_pack("{s: s, s: s}",
-		"local_two", localtwo->name,
-		"local_two_uniqueid", localtwo->uniqueid);
+		"local_two", localtwo->base->name,
+		"local_two_uniqueid", localtwo->base->uniqueid);
 	if (!extra) {
 		return;
 	}
diff --git a/main/channel.c b/main/channel.c
index 3d8e244..7e12f30 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -6761,6 +6761,12 @@
 		ast_channel_name(clonechan), ast_channel_state(clonechan),
 		ast_channel_name(original), ast_channel_state(original));
 
+	/* When all is said and done force new snapshot segments so they are
+	 * up to date.
+	 */
+	ast_set_flag(ast_channel_snapshot_segment_flags(original), AST_FLAGS_ALL);
+	ast_set_flag(ast_channel_snapshot_segment_flags(clonechan), AST_FLAGS_ALL);
+
 	/*
 	 * Remember the original read/write formats.  We turn off any
 	 * translation on either one
@@ -7183,6 +7189,7 @@
 
 	ast_channel_lock(chan);
 	ast_party_caller_set(ast_channel_caller(chan), caller, update);
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER);
 	ast_channel_unlock(chan);
 }
 
@@ -7195,6 +7202,7 @@
 
 	ast_channel_lock(chan);
 	ast_party_caller_set(ast_channel_caller(chan), caller, update);
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER);
 	ast_channel_publish_snapshot(chan);
 	ast_channel_unlock(chan);
 }
@@ -8120,6 +8128,7 @@
 
 	ast_channel_lock(chan);
 	ast_party_connected_line_set(ast_channel_connected(chan), connected, update);
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CONNECTED);
 	ast_channel_publish_snapshot(chan);
 	ast_channel_unlock(chan);
 }
@@ -8930,6 +8939,7 @@
 
 	ast_channel_lock(chan);
 	ast_party_redirecting_set(ast_channel_redirecting(chan), redirecting, update);
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER);
 	ast_channel_unlock(chan);
 }
 
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index 436ba23..30d3909 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -221,6 +221,7 @@
 	void *stream_topology_change_source; /*!< Source that initiated a stream topology change */
 	struct ast_stream *default_streams[AST_MEDIA_TYPE_END]; /*!< Default streams indexed by media type */
 	struct ast_channel_snapshot *snapshot; /*!< The current up to date snapshot of the channel */
+	struct ast_flags snapshot_segment_flags; /*!< Flags regarding the segments of the snapshot */
 };
 
 /*! \brief The monotonically increasing integer counter for channel uniqueids */
@@ -228,18 +229,40 @@
 
 /* ACCESSORS */
 
-#define DEFINE_STRINGFIELD_SETTERS_FOR(field, publish, assert_on_null) \
+#define DEFINE_STRINGFIELD_SETTERS_FOR(field, assert_on_null) \
 void ast_channel_##field##_set(struct ast_channel *chan, const char *value) \
 { \
 	if ((assert_on_null)) ast_assert(!ast_strlen_zero(value)); \
 	if (!strcmp(value, chan->field)) return; \
 	ast_string_field_set(chan, field, value); \
+} \
+  \
+void ast_channel_##field##_build_va(struct ast_channel *chan, const char *fmt, va_list ap) \
+{ \
+	ast_string_field_build_va(chan, field, fmt, ap); \
+} \
+void ast_channel_##field##_build(struct ast_channel *chan, const char *fmt, ...) \
+{ \
+	va_list ap; \
+	va_start(ap, fmt); \
+	ast_channel_##field##_build_va(chan, fmt, ap); \
+	va_end(ap); \
+}
+
+#define DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(field, publish, assert_on_null, invalidate) \
+void ast_channel_##field##_set(struct ast_channel *chan, const char *value) \
+{ \
+	if ((assert_on_null)) ast_assert(!ast_strlen_zero(value)); \
+	if (!strcmp(value, chan->field)) return; \
+	ast_string_field_set(chan, field, value); \
+	ast_channel_snapshot_invalidate_segment(chan, invalidate); \
 	if (publish && ast_channel_internal_is_finalized(chan)) ast_channel_publish_snapshot(chan); \
 } \
   \
 void ast_channel_##field##_build_va(struct ast_channel *chan, const char *fmt, va_list ap) \
 { \
 	ast_string_field_build_va(chan, field, fmt, ap); \
+	ast_channel_snapshot_invalidate_segment(chan, invalidate); \
 	if (publish && ast_channel_internal_is_finalized(chan)) ast_channel_publish_snapshot(chan); \
 } \
 void ast_channel_##field##_build(struct ast_channel *chan, const char *fmt, ...) \
@@ -250,17 +273,17 @@
 	va_end(ap); \
 }
 
-DEFINE_STRINGFIELD_SETTERS_FOR(name, 0, 1);
-DEFINE_STRINGFIELD_SETTERS_FOR(language, 1, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(musicclass, 0, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(latest_musicclass, 0, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(accountcode, 1, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(peeraccount, 1, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(userfield, 0, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(call_forward, 0, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(parkinglot, 0, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(hangupsource, 0, 0);
-DEFINE_STRINGFIELD_SETTERS_FOR(dialcontext, 0, 0);
+DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(name, 0, 1, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
+DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(language, 1, 0, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
+DEFINE_STRINGFIELD_SETTERS_FOR(musicclass, 0);
+DEFINE_STRINGFIELD_SETTERS_FOR(latest_musicclass, 0);
+DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(accountcode, 1, 0, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
+DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(peeraccount, 1, 0, AST_CHANNEL_SNAPSHOT_INVALIDATE_PEER);
+DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(userfield, 0, 0, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
+DEFINE_STRINGFIELD_SETTERS_FOR(call_forward, 0);
+DEFINE_STRINGFIELD_SETTERS_FOR(parkinglot, 0);
+DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(hangupsource, 0, 0, AST_CHANNEL_SNAPSHOT_INVALIDATE_HANGUP);
+DEFINE_STRINGFIELD_SETTERS_FOR(dialcontext, 0);
 
 #define DEFINE_STRINGFIELD_GETTER_FOR(field) const char *ast_channel_##field(const struct ast_channel *chan) \
 { \
@@ -298,6 +321,7 @@
 void ast_channel_appl_set(struct ast_channel *chan, const char *value)
 {
 	chan->appl = value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN);
 }
 const char *ast_channel_blockproc(const struct ast_channel *chan)
 {
@@ -314,6 +338,7 @@
 void ast_channel_data_set(struct ast_channel *chan, const char *value)
 {
 	chan->data = value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN);
 }
 
 const char *ast_channel_context(const struct ast_channel *chan)
@@ -323,6 +348,7 @@
 void ast_channel_context_set(struct ast_channel *chan, const char *value)
 {
 	ast_copy_string(chan->context, value, sizeof(chan->context));
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN);
 }
 const char *ast_channel_exten(const struct ast_channel *chan)
 {
@@ -331,6 +357,7 @@
 void ast_channel_exten_set(struct ast_channel *chan, const char *value)
 {
 	ast_copy_string(chan->exten, value, sizeof(chan->exten));
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN);
 }
 const char *ast_channel_macrocontext(const struct ast_channel *chan)
 {
@@ -404,6 +431,7 @@
 void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
 {
 	chan->hangupcause = value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_HANGUP);
 }
 int ast_channel_macropriority(const struct ast_channel *chan)
 {
@@ -420,6 +448,7 @@
 void ast_channel_priority_set(struct ast_channel *chan, int value)
 {
 	chan->priority = value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN);
 }
 int ast_channel_rings(const struct ast_channel *chan)
 {
@@ -909,18 +938,22 @@
 void ast_channel_caller_set(struct ast_channel *chan, struct ast_party_caller *value)
 {
 	chan->caller = *value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER);
 }
 void ast_channel_connected_set(struct ast_channel *chan, struct ast_party_connected_line *value)
 {
 	chan->connected = *value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CONNECTED);
 }
 void ast_channel_dialed_set(struct ast_channel *chan, struct ast_party_dialed *value)
 {
 	chan->dialed = *value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER);
 }
 void ast_channel_redirecting_set(struct ast_channel *chan, struct ast_party_redirecting *value)
 {
 	chan->redirecting = *value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_CALLER);
 }
 void ast_channel_dtmf_tv_set(struct ast_channel *chan, struct timeval *value)
 {
@@ -941,6 +974,7 @@
 void ast_channel_creationtime_set(struct ast_channel *chan, struct timeval *value)
 {
 	chan->creationtime = *value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
 }
 
 struct timeval ast_channel_answertime(struct ast_channel *chan)
@@ -1204,6 +1238,7 @@
 void ast_channel_internal_bridge_set(struct ast_channel *chan, struct ast_bridge *value)
 {
 	chan->bridge = value;
+	ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_BRIDGE);
 	ast_channel_publish_snapshot(chan);
 }
 
@@ -1335,6 +1370,11 @@
 
 	AST_VECTOR_INIT(&tmp->fds, AST_MAX_FDS);
 
+	/* Force all channel snapshot segments to be created on first use, so we don't have to check if
+	 * an old snapshot exists.
+	 */
+	ast_set_flag(&tmp->snapshot_segment_flags, AST_FLAGS_ALL);
+
 	return tmp;
 }
 
@@ -1363,6 +1403,7 @@
 		return;
 	}
 	dest->linkedid = source->linkedid;
+	ast_channel_snapshot_invalidate_segment(dest, AST_CHANNEL_SNAPSHOT_INVALIDATE_PEER);
 	ast_channel_publish_snapshot(dest);
 }
 
@@ -1370,6 +1411,10 @@
 {
 	struct ast_channel_id temp;
 
+	/* This operation is used as part of masquerading and so does not invalidate the peer
+	 * segment. This is due to the masquerade process invalidating all segments.
+	 */
+
 	temp = a->uniqueid;
 	a->uniqueid = b->uniqueid;
 	b->uniqueid = temp;
@@ -1584,3 +1629,8 @@
 	ao2_cleanup(chan->snapshot);
 	chan->snapshot = ao2_bump(snapshot);
 }
+
+struct ast_flags *ast_channel_snapshot_segment_flags(struct ast_channel *chan)
+{
+	return &chan->snapshot_segment_flags;
+}
diff --git a/main/cli.c b/main/cli.c
index 5484e47..e2224f7 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -1004,8 +1004,8 @@
 		char durbuf[16] = "-";
 
 		if (!count) {
-			if ((concise || verbose)  && !ast_tvzero(cs->creationtime)) {
-				int duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->creationtime) / 1000);
+			if ((concise || verbose)  && !ast_tvzero(cs->base->creationtime)) {
+				int duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->base->creationtime) / 1000);
 				if (verbose) {
 					int durh = duration / 3600;
 					int durm = (duration % 3600) / 60;
@@ -1016,36 +1016,36 @@
 				}
 			}
 			if (concise) {
-				ast_cli(a->fd, CONCISE_FORMAT_STRING, cs->name, cs->context, cs->exten, cs->priority, ast_state2str(cs->state),
-					S_OR(cs->appl, "(None)"),
-					cs->data,
-					cs->caller_number,
-					cs->accountcode,
-					cs->peeraccount,
+				ast_cli(a->fd, CONCISE_FORMAT_STRING, cs->base->name, cs->dialplan->context, cs->dialplan->exten, cs->dialplan->priority, ast_state2str(cs->state),
+					S_OR(cs->dialplan->appl, "(None)"),
+					cs->dialplan->data,
+					cs->caller->number,
+					cs->base->accountcode,
+					cs->peer->account,
 					cs->amaflags,
 					durbuf,
-					cs->bridgeid,
-					cs->uniqueid);
+					cs->bridge->id,
+					cs->base->uniqueid);
 			} else if (verbose) {
-				ast_cli(a->fd, VERBOSE_FORMAT_STRING, cs->name, cs->context, cs->exten, cs->priority, ast_state2str(cs->state),
-					S_OR(cs->appl, "(None)"),
-					S_OR(cs->data, "(Empty)"),
-					cs->caller_number,
+				ast_cli(a->fd, VERBOSE_FORMAT_STRING, cs->base->name, cs->dialplan->context, cs->dialplan->exten, cs->dialplan->priority, ast_state2str(cs->state),
+					S_OR(cs->dialplan->appl, "(None)"),
+					S_OR(cs->dialplan->data, "(Empty)"),
+					cs->caller->number,
 					durbuf,
-					cs->accountcode,
-					cs->peeraccount,
-					cs->bridgeid);
+					cs->base->accountcode,
+					cs->peer->account,
+					cs->bridge->id);
 			} else {
 				char locbuf[40] = "(None)";
 				char appdata[40] = "(None)";
 
-				if (!ast_strlen_zero(cs->context) && !ast_strlen_zero(cs->exten)) {
-					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", cs->exten, cs->context, cs->priority);
+				if (!ast_strlen_zero(cs->dialplan->context) && !ast_strlen_zero(cs->dialplan->exten)) {
+					snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", cs->dialplan->exten, cs->dialplan->context, cs->dialplan->priority);
 				}
-				if (!ast_strlen_zero(cs->appl)) {
-					snprintf(appdata, sizeof(appdata), "%s(%s)", cs->appl, S_OR(cs->data, ""));
+				if (!ast_strlen_zero(cs->dialplan->appl)) {
+					snprintf(appdata, sizeof(appdata), "%s(%s)", cs->dialplan->appl, S_OR(cs->dialplan->data, ""));
 				}
-				ast_cli(a->fd, FORMAT_STRING, cs->name, locbuf, ast_state2str(cs->state), appdata);
+				ast_cli(a->fd, FORMAT_STRING, cs->base->name, locbuf, ast_state2str(cs->state), appdata);
 			}
 		}
 	}
@@ -1684,14 +1684,14 @@
 
 	iter = ao2_iterator_init(cached_channels, 0);
 	for (; (snapshot = ao2_iterator_next(&iter)); ao2_ref(snapshot, -1)) {
-		if (!strncasecmp(word, snapshot->name, wordlen) && (++which > state)) {
+		if (!strncasecmp(word, snapshot->base->name, wordlen) && (++which > state)) {
 			if (state != -1) {
-				ret = ast_strdup(snapshot->name);
+				ret = ast_strdup(snapshot->base->name);
 				ao2_ref(snapshot, -1);
 				break;
 			}
 
-			if (ast_cli_completion_add(ast_strdup(snapshot->name))) {
+			if (ast_cli_completion_add(ast_strdup(snapshot->base->name))) {
 				ao2_ref(snapshot, -1);
 				break;
 			}
diff --git a/main/core_local.c b/main/core_local.c
index c3fa15f..f56aac7 100644
--- a/main/core_local.c
+++ b/main/core_local.c
@@ -493,7 +493,7 @@
 		}
 
 		dest_uniqueid = ast_json_object_get(blob, "dest") == AST_UNREAL_OWNER ?
-				local_snapshot_one->uniqueid : local_snapshot_two->uniqueid;
+				local_snapshot_one->base->uniqueid : local_snapshot_two->base->uniqueid;
 
 		event = "LocalOptimizationBegin";
 		if (source_str) {
diff --git a/main/endpoints.c b/main/endpoints.c
index f3e3372..b958932 100644
--- a/main/endpoints.c
+++ b/main/endpoints.c
@@ -195,7 +195,7 @@
 	ast_assert(endpoint != NULL);
 
 	ao2_lock(endpoint);
-	ast_str_container_remove(endpoint->channel_ids, update->new_snapshot->uniqueid);
+	ast_str_container_remove(endpoint->channel_ids, update->new_snapshot->base->uniqueid);
 	ao2_unlock(endpoint);
 	endpoint_publish_snapshot(endpoint);
 }
diff --git a/main/manager.c b/main/manager.c
index 76a827c..3e41198 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -6271,10 +6271,10 @@
 			continue;
 		}
 
-		if (!ast_tvzero(cs->creationtime)) {
+		if (!ast_tvzero(cs->base->creationtime)) {
 			int duration, durh, durm, durs;
 
-			duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->creationtime) / 1000);
+			duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->base->creationtime) / 1000);
 			durh = duration / 3600;
 			durm = (duration % 3600) / 60;
 			durs = duration % 60;
@@ -6292,10 +6292,10 @@
 			"\r\n",
 			idText,
 			ast_str_buffer(built),
-			cs->appl,
-			cs->data,
+			cs->dialplan->appl,
+			cs->dialplan->data,
 			durbuf,
-			cs->bridgeid);
+			cs->bridge->id);
 
 		numchans++;
 
diff --git a/main/manager_bridges.c b/main/manager_bridges.c
index 1b57049..4f2cb35 100644
--- a/main/manager_bridges.c
+++ b/main/manager_bridges.c
@@ -536,7 +536,7 @@
 		return 0;
 	}
 
-	if (snapshot->tech_properties & AST_CHAN_TP_INTERNAL) {
+	if (snapshot->base->tech_properties & AST_CHAN_TP_INTERNAL) {
 		return 0;
 	}
 
diff --git a/main/manager_channels.c b/main/manager_channels.c
index 887f77e..edbc770 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -493,7 +493,7 @@
 	char *connected_name;
 	int res;
 
-	if (snapshot->tech_properties & AST_CHAN_TP_INTERNAL) {
+	if (snapshot->base->tech_properties & AST_CHAN_TP_INTERNAL) {
 		return NULL;
 	}
 
@@ -502,8 +502,8 @@
 		return NULL;
 	}
 
-	caller_name = ast_escape_c_alloc(snapshot->caller_name);
-	connected_name = ast_escape_c_alloc(snapshot->connected_name);
+	caller_name = ast_escape_c_alloc(snapshot->caller->name);
+	connected_name = ast_escape_c_alloc(snapshot->connected->name);
 
 	res = ast_str_set(&out, 0,
 		"%sChannel: %s\r\n"
@@ -520,20 +520,20 @@
 		"%sPriority: %d\r\n"
 		"%sUniqueid: %s\r\n"
 		"%sLinkedid: %s\r\n",
-		prefix, snapshot->name,
+		prefix, snapshot->base->name,
 		prefix, snapshot->state,
 		prefix, ast_state2str(snapshot->state),
-		prefix, S_OR(snapshot->caller_number, "<unknown>"),
+		prefix, S_OR(snapshot->caller->number, "<unknown>"),
 		prefix, S_OR(caller_name, "<unknown>"),
-		prefix, S_OR(snapshot->connected_number, "<unknown>"),
+		prefix, S_OR(snapshot->connected->number, "<unknown>"),
 		prefix, S_OR(connected_name, "<unknown>"),
-		prefix, snapshot->language,
-		prefix, snapshot->accountcode,
-		prefix, snapshot->context,
-		prefix, snapshot->exten,
-		prefix, snapshot->priority,
-		prefix, snapshot->uniqueid,
-		prefix, snapshot->linkedid);
+		prefix, snapshot->base->language,
+		prefix, snapshot->base->accountcode,
+		prefix, snapshot->dialplan->context,
+		prefix, snapshot->dialplan->exten,
+		prefix, snapshot->dialplan->priority,
+		prefix, snapshot->base->uniqueid,
+		prefix, snapshot->peer->linkedid);
 
 	ast_free(caller_name);
 	ast_free(connected_name);
@@ -594,8 +594,8 @@
 			EVENT_FLAG_CALL, "Hangup",
 			"Cause: %d\r\n"
 			"Cause-txt: %s\r\n",
-			new_snapshot->hangupcause,
-			ast_cause2str(new_snapshot->hangupcause));
+			new_snapshot->hangup->cause,
+			ast_cause2str(new_snapshot->hangup->cause));
 	}
 
 	if (old_snapshot->state != new_snapshot->state) {
@@ -612,7 +612,7 @@
 	struct ast_channel_snapshot *new_snapshot)
 {
 	/* Empty application is not valid for a Newexten event */
-	if (ast_strlen_zero(new_snapshot->appl)) {
+	if (ast_strlen_zero(new_snapshot->dialplan->appl)) {
 		return NULL;
 	}
 
@@ -632,9 +632,9 @@
 		"Extension: %s\r\n"
 		"Application: %s\r\n"
 		"AppData: %s\r\n",
-		new_snapshot->exten,
-		new_snapshot->appl,
-		new_snapshot->data);
+		new_snapshot->dialplan->exten,
+		new_snapshot->dialplan->appl,
+		new_snapshot->dialplan->data);
 }
 
 static struct ast_manager_event_blob *channel_new_callerid(
@@ -654,14 +654,14 @@
 	}
 
 	if (!(callerid = ast_escape_c_alloc(
-		      ast_describe_caller_presentation(new_snapshot->caller_pres)))) {
+		      ast_describe_caller_presentation(new_snapshot->caller->pres)))) {
 		return NULL;
 	}
 
 	res = ast_manager_event_blob_create(
 		EVENT_FLAG_CALL, "NewCallerid",
 		"CID-CallingPres: %d (%s)\r\n",
-		new_snapshot->caller_pres,
+		new_snapshot->caller->pres,
 		callerid);
 
 	ast_free(callerid);
@@ -693,13 +693,13 @@
 		return NULL;
 	}
 
-	if (!strcmp(old_snapshot->accountcode, new_snapshot->accountcode)) {
+	if (!strcmp(old_snapshot->base->accountcode, new_snapshot->base->accountcode)) {
 		return NULL;
 	}
 
 	return ast_manager_event_blob_create(
 		EVENT_FLAG_CALL, "NewAccountCode",
-		"OldAccountCode: %s\r\n", old_snapshot->accountcode);
+		"OldAccountCode: %s\r\n", old_snapshot->base->accountcode);
 }
 
 channel_snapshot_monitor channel_monitors[] = {
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 59b9685..42bf6bc 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -1069,7 +1069,7 @@
 	case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
 		ast_str_append(&variable_data, 0, "DestType: Threeway\r\n");
 		ast_str_append(&variable_data, 0, "DestBridgeUniqueid: %s\r\n", transfer_msg->dest.threeway.bridge_snapshot->uniqueid);
-		ast_str_append(&variable_data, 0, "DestTransfererChannel: %s\r\n", transfer_msg->dest.threeway.channel_snapshot->name);
+		ast_str_append(&variable_data, 0, "DestTransfererChannel: %s\r\n", transfer_msg->dest.threeway.channel_snapshot->base->name);
 		break;
 	case AST_ATTENDED_TRANSFER_DEST_FAIL:
 		ast_str_append(&variable_data, 0, "DestType: Fail\r\n");
@@ -1189,7 +1189,7 @@
 {
 	transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_THREEWAY;
 
-	if (!strcmp(ast_channel_uniqueid(survivor_channel), transfer_msg->to_transferee.channel_snapshot->uniqueid)) {
+	if (!strcmp(ast_channel_uniqueid(survivor_channel), transfer_msg->to_transferee.channel_snapshot->base->uniqueid)) {
 		transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transferee.channel_snapshot;
 	} else {
 		transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transfer_target.channel_snapshot;
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index ec8d70c..d39fb08 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -149,7 +149,7 @@
 		key = obj;
 		break;
 	case OBJ_SEARCH_OBJECT:
-		key = object->name;
+		key = object->base->name;
 		break;
 	default:
 		ast_assert(0);
@@ -171,12 +171,12 @@
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		right_key = object_right->name;
+		right_key = object_right->base->name;
 	case OBJ_SEARCH_KEY:
-		cmp = strcasecmp(object_left->name, right_key);
+		cmp = strcasecmp(object_left->base->name, right_key);
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
-		cmp = strncasecmp(object_left->name, right_key, strlen(right_key));
+		cmp = strncasecmp(object_left->base->name, right_key, strlen(right_key));
 		break;
 	default:
 		cmp = 0;
@@ -202,7 +202,7 @@
 		key = obj;
 		break;
 	case OBJ_SEARCH_OBJECT:
-		key = object->uniqueid;
+		key = object->base->uniqueid;
 		break;
 	default:
 		ast_assert(0);
@@ -224,12 +224,12 @@
 
 	switch (flags & OBJ_SEARCH_MASK) {
 	case OBJ_SEARCH_OBJECT:
-		right_key = object_right->uniqueid;
+		right_key = object_right->base->uniqueid;
 	case OBJ_SEARCH_KEY:
-		cmp = strcasecmp(object_left->uniqueid, right_key);
+		cmp = strcasecmp(object_left->base->uniqueid, right_key);
 		break;
 	case OBJ_SEARCH_PARTIAL_KEY:
-		cmp = strncasecmp(object_left->uniqueid, right_key, strlen(right_key));
+		cmp = strncasecmp(object_left->base->uniqueid, right_key, strlen(right_key));
 		break;
 	default:
 		cmp = 0;
@@ -245,36 +245,180 @@
 {
 	struct ast_channel_snapshot *snapshot = obj;
 
-	ast_string_field_free_memory(snapshot);
+	ao2_cleanup(snapshot->base);
+	ao2_cleanup(snapshot->peer);
+	ao2_cleanup(snapshot->caller);
+	ao2_cleanup(snapshot->connected);
+	ao2_cleanup(snapshot->bridge);
+	ao2_cleanup(snapshot->dialplan);
+	ao2_cleanup(snapshot->hangup);
 	ao2_cleanup(snapshot->manager_vars);
 	ao2_cleanup(snapshot->ari_vars);
 }
 
-struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *chan)
+static void channel_snapshot_base_dtor(void *obj)
 {
-	struct ast_channel_snapshot *snapshot;
-	struct ast_bridge *bridge;
+	struct ast_channel_snapshot_base *snapshot = obj;
 
-	/* no snapshots for dummy channels */
-	if (!ast_channel_tech(chan)) {
+	ast_string_field_free_memory(snapshot);
+}
+
+static struct ast_channel_snapshot_base *channel_snapshot_base_create(struct ast_channel *chan)
+{
+	struct ast_channel_snapshot_base *snapshot;
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot), channel_snapshot_base_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
 		return NULL;
 	}
 
-	snapshot = ao2_alloc_options(sizeof(*snapshot), channel_snapshot_dtor,
-		AO2_ALLOC_OPT_LOCK_NOLOCK);
-	if (!snapshot || ast_string_field_init(snapshot, 1024)) {
-		ao2_cleanup(snapshot);
+	if (ast_string_field_init(snapshot, 256)) {
+		ao2_ref(snapshot, -1);
 		return NULL;
 	}
 
 	ast_string_field_set(snapshot, name, ast_channel_name(chan));
 	ast_string_field_set(snapshot, type, ast_channel_tech(chan)->type);
 	ast_string_field_set(snapshot, accountcode, ast_channel_accountcode(chan));
-	ast_string_field_set(snapshot, peeraccount, ast_channel_peeraccount(chan));
 	ast_string_field_set(snapshot, userfield, ast_channel_userfield(chan));
 	ast_string_field_set(snapshot, uniqueid, ast_channel_uniqueid(chan));
-	ast_string_field_set(snapshot, linkedid, ast_channel_linkedid(chan));
-	ast_string_field_set(snapshot, hangupsource, ast_channel_hangupsource(chan));
+	ast_string_field_set(snapshot, language, ast_channel_language(chan));
+
+	snapshot->creationtime = ast_channel_creationtime(chan);
+	snapshot->tech_properties = ast_channel_tech(chan)->properties;
+
+	return snapshot;
+}
+
+static struct ast_channel_snapshot_peer *channel_snapshot_peer_create(struct ast_channel *chan)
+{
+	const char *linkedid = S_OR(ast_channel_linkedid(chan), "");
+	const char *peeraccount = S_OR(ast_channel_peeraccount(chan), "");
+	size_t linkedid_len = strlen(linkedid) + 1;
+	size_t peeraccount_len = strlen(peeraccount) + 1;
+	struct ast_channel_snapshot_peer *snapshot;
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot) + linkedid_len + peeraccount_len, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	strcpy(snapshot->account, peeraccount); /* Safe */
+	snapshot->linkedid = snapshot->account + peeraccount_len;
+	strcpy(snapshot->linkedid, linkedid); /* Safe */
+
+	return snapshot;
+}
+
+static void channel_snapshot_caller_dtor(void *obj)
+{
+	struct ast_channel_snapshot_caller *snapshot = obj;
+
+	ast_string_field_free_memory(snapshot);
+}
+
+static struct ast_channel_snapshot_caller *channel_snapshot_caller_create(struct ast_channel *chan)
+{
+	struct ast_channel_snapshot_caller *snapshot;
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot), channel_snapshot_caller_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(snapshot, 256)) {
+		ao2_ref(snapshot, -1);
+		return NULL;
+	}
+
+	ast_string_field_set(snapshot, name,
+		S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""));
+	ast_string_field_set(snapshot, number,
+		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""));
+	ast_string_field_set(snapshot, subaddr,
+		S_COR(ast_channel_caller(chan)->id.subaddress.valid, ast_channel_caller(chan)->id.subaddress.str, ""));
+	ast_string_field_set(snapshot, ani,
+		S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, ""));
+
+	ast_string_field_set(snapshot, rdnis,
+		S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, ""));
+
+	ast_string_field_set(snapshot, dnid,
+		S_OR(ast_channel_dialed(chan)->number.str, ""));
+	ast_string_field_set(snapshot, dialed_subaddr,
+		S_COR(ast_channel_dialed(chan)->subaddress.valid, ast_channel_dialed(chan)->subaddress.str, ""));
+
+	snapshot->pres = ast_party_id_presentation(&ast_channel_caller(chan)->id);
+
+	return snapshot;
+}
+
+static struct ast_channel_snapshot_connected *channel_snapshot_connected_create(struct ast_channel *chan)
+{
+	const char *name = S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, "");
+	const char *number = S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, "");
+	size_t name_len = strlen(name) + 1;
+	size_t number_len = strlen(number) + 1;
+	struct ast_channel_snapshot_connected *snapshot;
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot) + name_len + number_len, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	strcpy(snapshot->name, name); /* Safe */
+	snapshot->number = snapshot->name + name_len;
+	strcpy(snapshot->number, number); /* Safe */
+
+	return snapshot;
+}
+
+static struct ast_channel_snapshot_bridge *channel_snapshot_bridge_create(struct ast_channel *chan)
+{
+	const char *uniqueid = "";
+	struct ast_bridge *bridge;
+	struct ast_channel_snapshot_bridge *snapshot;
+
+	bridge = ast_channel_get_bridge(chan);
+	if (bridge && !ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
+		uniqueid = bridge->uniqueid;
+	}
+	ao2_cleanup(bridge);
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot) + strlen(uniqueid) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	strcpy(snapshot->id, uniqueid); /* Safe */
+
+	return snapshot;
+}
+
+static void channel_snapshot_dialplan_dtor(void *obj)
+{
+	struct ast_channel_snapshot_dialplan *snapshot = obj;
+
+	ast_string_field_free_memory(snapshot);
+}
+
+static struct ast_channel_snapshot_dialplan *channel_snapshot_dialplan_create(struct ast_channel *chan)
+{
+	struct ast_channel_snapshot_dialplan *snapshot;
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot), channel_snapshot_dialplan_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(snapshot, 256)) {
+		ao2_ref(snapshot, -1);
+		return NULL;
+	}
+
 	if (ast_channel_appl(chan)) {
 		ast_string_field_set(snapshot, appl, ast_channel_appl(chan));
 	}
@@ -283,50 +427,130 @@
 	}
 	ast_string_field_set(snapshot, context, ast_channel_context(chan));
 	ast_string_field_set(snapshot, exten, ast_channel_exten(chan));
+	snapshot->priority = ast_channel_priority(chan);
 
-	ast_string_field_set(snapshot, caller_name,
-		S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""));
-	ast_string_field_set(snapshot, caller_number,
-		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""));
-	ast_string_field_set(snapshot, caller_subaddr,
-		S_COR(ast_channel_caller(chan)->id.subaddress.valid, ast_channel_caller(chan)->id.subaddress.str, ""));
-	ast_string_field_set(snapshot, caller_ani,
-		S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, ""));
+	return snapshot;
+}
 
-	ast_string_field_set(snapshot, caller_rdnis,
-		S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, ""));
+static struct ast_channel_snapshot_hangup *channel_snapshot_hangup_create(struct ast_channel *chan)
+{
+	const char *hangupsource = S_OR(ast_channel_hangupsource(chan), "");
+	struct ast_channel_snapshot_hangup *snapshot;
 
-	ast_string_field_set(snapshot, caller_dnid,
-		S_OR(ast_channel_dialed(chan)->number.str, ""));
-	ast_string_field_set(snapshot, dialed_subaddr,
-		S_COR(ast_channel_dialed(chan)->subaddress.valid, ast_channel_dialed(chan)->subaddress.str, ""));
-
-	ast_string_field_set(snapshot, connected_name,
-		S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, ""));
-	ast_string_field_set(snapshot, connected_number,
-		S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, ""));
-
-	ast_string_field_set(snapshot, language, ast_channel_language(chan));
-
-	if ((bridge = ast_channel_get_bridge(chan))) {
-		if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
-			ast_string_field_set(snapshot, bridgeid, bridge->uniqueid);
-		}
-		ao2_cleanup(bridge);
+	snapshot = ao2_alloc_options(sizeof(*snapshot) + strlen(hangupsource) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
 	}
 
-	snapshot->creationtime = ast_channel_creationtime(chan);
+	snapshot->cause = ast_channel_hangupcause(chan);
+	strcpy(snapshot->source, hangupsource); /* Safe */
+
+	return snapshot;
+}
+
+struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *chan)
+{
+	struct ast_channel_snapshot *old_snapshot;
+	struct ast_channel_snapshot *snapshot;
+
+	/* no snapshots for dummy channels */
+	if (!ast_channel_tech(chan)) {
+		return NULL;
+	}
+
+	snapshot = ao2_alloc_options(sizeof(*snapshot), channel_snapshot_dtor,
+		AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	old_snapshot = ast_channel_snapshot(chan);
+
+	/* Channels automatically have all segments invalidated on them initially so a check for an old
+	 * snapshot existing before usage is not done here, as it can not happen. If the stored snapshot
+	 * on the channel is updated as a result of this then all segments marked as invalidated will be
+	 * cleared.
+	 */
+	if (ast_test_flag(ast_channel_snapshot_segment_flags(chan), AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE)) {
+		/* The base information has changed so update our snapshot */
+		snapshot->base = channel_snapshot_base_create(chan);
+		if (!snapshot->base) {
+			ao2_ref(snapshot, -1);
+			return NULL;
+		}
+	} else {
+		snapshot->base = ao2_bump(old_snapshot->base);
+	}
+
+	if (ast_test_flag(ast_channel_snapshot_segment_flags(chan), AST_CHANNEL_SNAPSHOT_INVALIDATE_PEER)) {
+		/* The peer information has changed so update our snapshot */
+		snapshot->peer = channel_snapshot_peer_create(chan);
+		if (!snapshot->peer) {
+			ao2_ref(snapshot, -1);
+			return NULL;
+		}
+	} else {
+		snapshot->peer = ao2_bump(old_snapshot->peer);
+	}
+
+	/* Unfortunately both caller and connected information do not have an enforced contract with
+	 * the channel API. This has allowed consumers to directly get the caller or connected structure
+	 * and manipulate it. Until such time as there is an enforced contract (which is being tracked under
+	 * ASTERISK-28164) they are each regenerated every time a channel snapshot is created.
+	 */
+	snapshot->caller = channel_snapshot_caller_create(chan);
+	if (!snapshot->caller) {
+		ao2_ref(snapshot, -1);
+		return NULL;
+	}
+
+	snapshot->connected = channel_snapshot_connected_create(chan);
+	if (!snapshot->connected) {
+		ao2_ref(snapshot, -1);
+		return NULL;
+	}
+
+	if (ast_test_flag(ast_channel_snapshot_segment_flags(chan), AST_CHANNEL_SNAPSHOT_INVALIDATE_BRIDGE)) {
+		/* The bridge has changed so update our snapshot */
+		snapshot->bridge = channel_snapshot_bridge_create(chan);
+		if (!snapshot->bridge) {
+			ao2_ref(snapshot, -1);
+			return NULL;
+		}
+	} else {
+		snapshot->bridge = ao2_bump(old_snapshot->bridge);
+	}
+
+	if (ast_test_flag(ast_channel_snapshot_segment_flags(chan), AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN)) {
+		/* The dialplan information has changed so update our snapshot */
+		snapshot->dialplan = channel_snapshot_dialplan_create(chan);
+		if (!snapshot->dialplan) {
+			ao2_ref(snapshot, -1);
+			return NULL;
+		}
+	} else {
+		snapshot->dialplan = ao2_bump(old_snapshot->dialplan);
+	}
+
+	if (ast_test_flag(ast_channel_snapshot_segment_flags(chan), AST_CHANNEL_SNAPSHOT_INVALIDATE_HANGUP)) {
+		/* The hangup information has changed so update our snapshot */
+		snapshot->hangup = channel_snapshot_hangup_create(chan);
+		if (!snapshot->hangup) {
+			ao2_ref(snapshot, -1);
+			return NULL;
+		}
+	} else {
+		snapshot->hangup = ao2_bump(old_snapshot->hangup);
+	}
+
 	snapshot->state = ast_channel_state(chan);
-	snapshot->priority = ast_channel_priority(chan);
 	snapshot->amaflags = ast_channel_amaflags(chan);
-	snapshot->hangupcause = ast_channel_hangupcause(chan);
 	ast_copy_flags(&snapshot->flags, ast_channel_flags(chan), 0xFFFFFFFF);
-	snapshot->caller_pres = ast_party_id_presentation(&ast_channel_caller(chan)->id);
 	ast_set_flag(&snapshot->softhangup_flags, ast_channel_softhangup_internal_flag(chan));
 
+	/* These have to be recreated as they may have changed, unfortunately */
 	snapshot->manager_vars = ast_channel_get_manager_vars(chan);
 	snapshot->ari_vars = ast_channel_get_ari_vars(chan);
-	snapshot->tech_properties = ast_channel_tech(chan)->properties;
 
 	return snapshot;
 }
@@ -822,6 +1046,12 @@
 	ast_channel_publish_snapshot(chan);
 }
 
+void ast_channel_snapshot_invalidate_segment(struct ast_channel *chan,
+	enum ast_channel_snapshot_segment_invalidation segment)
+{
+	ast_set_flag(ast_channel_snapshot_segment_flags(chan), segment);
+}
+
 void ast_channel_publish_snapshot(struct ast_channel *chan)
 {
 	struct ast_channel_snapshot_update *update;
@@ -840,6 +1070,14 @@
 		return;
 	}
 
+	/* If an old snapshot exists and is the same as this newly created one don't bother
+	 * raising a message as it hasn't changed.
+	 */
+	if (update->old_snapshot && !memcmp(update->old_snapshot, update->new_snapshot, sizeof(struct ast_channel_snapshot))) {
+		ao2_ref(update, -1);
+		return;
+	}
+
 	message = stasis_message_create(ast_channel_snapshot_type(), update);
 	/* In the success path message holds a reference to update so it will be valid
 	 * for the lifetime of this function until the end.
@@ -869,6 +1107,11 @@
 
 	ast_channel_snapshot_set(chan, update->new_snapshot);
 
+	/* As this is now the new snapshot any existing invalidated segments have been
+	 * created fresh and are up to date.
+	 */
+	ast_clear_flag(ast_channel_snapshot_segment_flags(chan), AST_FLAGS_ALL);
+
 	ast_assert(ast_channel_topic(chan) != NULL);
 	stasis_publish(ast_channel_topic(chan), message);
 	ao2_ref(message, -1);
@@ -1028,20 +1271,20 @@
 		"  s: o, s: o, s: s,"
 		"  s: o, s: o, s: s }",
 		/* First line */
-		"id", snapshot->uniqueid,
-		"name", snapshot->name,
+		"id", snapshot->base->uniqueid,
+		"name", snapshot->base->name,
 		"state", ast_state2str(snapshot->state),
 		/* Second line */
 		"caller", ast_json_name_number(
-			snapshot->caller_name, snapshot->caller_number),
+			snapshot->caller->name, snapshot->caller->number),
 		"connected", ast_json_name_number(
-			snapshot->connected_name, snapshot->connected_number),
-		"accountcode", snapshot->accountcode,
+			snapshot->connected->name, snapshot->connected->number),
+		"accountcode", snapshot->base->accountcode,
 		/* Third line */
 		"dialplan", ast_json_dialplan_cep(
-			snapshot->context, snapshot->exten, snapshot->priority),
-		"creationtime", ast_json_timeval(snapshot->creationtime, NULL),
-		"language", snapshot->language);
+			snapshot->dialplan->context, snapshot->dialplan->exten, snapshot->dialplan->priority),
+		"creationtime", ast_json_timeval(snapshot->base->creationtime, NULL),
+		"language", snapshot->base->language);
 
 	if (snapshot->ari_vars && !AST_LIST_EMPTY(snapshot->ari_vars)) {
 		ast_json_object_set(json_chan, "channelvars", ast_json_channel_vars(snapshot->ari_vars));
@@ -1061,14 +1304,14 @@
 	 * application is set. Since empty application is invalid, we treat
 	 * setting the application from nothing as a CEP change.
 	 */
-	if (ast_strlen_zero(old_snapshot->appl) &&
-	    !ast_strlen_zero(new_snapshot->appl)) {
+	if (ast_strlen_zero(old_snapshot->dialplan->appl) &&
+	    !ast_strlen_zero(new_snapshot->dialplan->appl)) {
 		return 0;
 	}
 
-	return old_snapshot->priority == new_snapshot->priority &&
-		strcmp(old_snapshot->context, new_snapshot->context) == 0 &&
-		strcmp(old_snapshot->exten, new_snapshot->exten) == 0;
+	return old_snapshot->dialplan->priority == new_snapshot->dialplan->priority &&
+		strcmp(old_snapshot->dialplan->context, new_snapshot->dialplan->context) == 0 &&
+		strcmp(old_snapshot->dialplan->exten, new_snapshot->dialplan->exten) == 0;
 }
 
 int ast_channel_snapshot_caller_id_equal(
@@ -1077,8 +1320,8 @@
 {
 	ast_assert(old_snapshot != NULL);
 	ast_assert(new_snapshot != NULL);
-	return strcmp(old_snapshot->caller_number, new_snapshot->caller_number) == 0 &&
-		strcmp(old_snapshot->caller_name, new_snapshot->caller_name) == 0;
+	return strcmp(old_snapshot->caller->number, new_snapshot->caller->number) == 0 &&
+		strcmp(old_snapshot->caller->name, new_snapshot->caller->name) == 0;
 }
 
 int ast_channel_snapshot_connected_line_equal(
@@ -1087,8 +1330,8 @@
 {
 	ast_assert(old_snapshot != NULL);
 	ast_assert(new_snapshot != NULL);
-	return strcmp(old_snapshot->connected_number, new_snapshot->connected_number) == 0 &&
-		strcmp(old_snapshot->connected_name, new_snapshot->connected_name) == 0;
+	return strcmp(old_snapshot->connected->number, new_snapshot->connected->number) == 0 &&
+		strcmp(old_snapshot->connected->name, new_snapshot->connected->name) == 0;
 }
 
 static struct ast_json *channel_blob_to_json(
diff --git a/pbx/pbx_realtime.c b/pbx/pbx_realtime.c
index 75e6654..970bb3c 100644
--- a/pbx/pbx_realtime.c
+++ b/pbx/pbx_realtime.c
@@ -344,6 +344,11 @@
 						 term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
 				if (ast_channel_snapshot_type()) {
 					ast_channel_lock(chan);
+					/* Force a new dialplan segment that will be unique to use so we can update it with the
+					 * information we want. In the future when a channel snapshot is published this will
+					 * occur again and unset this flag.
+					 */
+					ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_DIALPLAN);
 					snapshot = ast_channel_snapshot_create(chan);
 					ast_channel_unlock(chan);
 				}
@@ -351,8 +356,8 @@
 					/* pbx_exec sets application name and data, but we don't want to log
 					 * every exec. Just update the snapshot here instead.
 					 */
-					ast_string_field_set(snapshot, appl, app);
-					ast_string_field_set(snapshot, data, !ast_strlen_zero(appdata) ? appdata : "(NULL)");
+					ast_string_field_set(snapshot->dialplan, appl, app);
+					ast_string_field_set(snapshot->dialplan, data, !ast_strlen_zero(appdata) ? appdata : "(NULL)");
 					msg = stasis_message_create(ast_channel_snapshot_type(), snapshot);
 					if (msg) {
 						stasis_publish(ast_channel_topic(chan), msg);
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index e5fa41c..3c0eeb7 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -377,7 +377,7 @@
 		return -1;
 	}
 
-	language = S_OR(args_lang, snapshot->language);
+	language = S_OR(args_lang, snapshot->base->language);
 
 	playback = stasis_app_control_play_uri(control, args_media, args_media_count,
 		language, bridge->uniqueid, STASIS_PLAYBACK_TARGET_BRIDGE, args_skipms,
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index f96192b..311358e 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -170,8 +170,8 @@
 	}
 
 	if (ast_strlen_zero(args->context)) {
-		context = snapshot->context;
-		exten = S_OR(args->extension, snapshot->exten);
+		context = snapshot->dialplan->context;
+		exten = S_OR(args->extension, snapshot->dialplan->exten);
 	} else {
 		context = args->context;
 		exten = S_OR(args->extension, "s");
@@ -203,7 +203,7 @@
 		ipri = args->priority;
 	} else if (ast_strlen_zero(args->context) && ast_strlen_zero(args->extension)) {
 		/* Special case. No exten, context, or priority provided, then move on to the next priority */
-		ipri = snapshot->priority + 1;
+		ipri = snapshot->dialplan->priority + 1;
 	} else {
 		ipri = 1;
 	}
@@ -263,10 +263,10 @@
 		return;
 	}
 
-	if (strncasecmp(chan_snapshot->type, tech, tech_len)) {
+	if (strncasecmp(chan_snapshot->base->type, tech, tech_len)) {
 		ast_ari_response_error(response, 422, "Unprocessable Entity",
 			"Endpoint technology '%s' does not match channel technology '%s'",
-			tech, chan_snapshot->type);
+			tech, chan_snapshot->base->type);
 		return;
 	}
 
@@ -628,7 +628,7 @@
 		return;
 	}
 
-	language = S_OR(args_lang, snapshot->language);
+	language = S_OR(args_lang, snapshot->base->language);
 
 	playback = stasis_app_control_play_uri(control, args_media, args_media_count, language,
 		args_channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args_skipms, args_offsetms, args_playback_id);
diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c
index f9b3e85..46dbedf 100644
--- a/res/parking/parking_applications.c
+++ b/res/parking/parking_applications.c
@@ -819,12 +819,12 @@
 	snprintf(buf, sizeof(buf), "%d", parkingspace);
 	oh.vars = ast_variable_new("_PARKEDAT", buf, "");
 
-	inherit_channel_vars_from_id(&oh, parkee_snapshot->uniqueid);
+	inherit_channel_vars_from_id(&oh, parkee_snapshot->base->uniqueid);
 
 	dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
 		&outstate,
-		parkee_snapshot->caller_number,
-		parkee_snapshot->caller_name,
+		parkee_snapshot->caller->number,
+		parkee_snapshot->caller->name,
 		&oh);
 
 	ast_variables_destroy(oh.vars);
@@ -877,7 +877,7 @@
 		return;
 	}
 
-	if (strcmp(payload->parkee->uniqueid, pa_data->parkee_uuid)) {
+	if (strcmp(payload->parkee->base->uniqueid, pa_data->parkee_uuid)) {
 		/* We are only concerned with the parkee we are subscribed for. */
 		return;
 	}
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index 1d3b9e4..d619471 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -111,7 +111,7 @@
 	RAII_VAR(struct ast_channel *, parker, NULL, ast_channel_cleanup);
 	RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
 
-	if (strcmp(parkee_to_act_on, parkee_snapshot->uniqueid)) {
+	if (strcmp(parkee_to_act_on, parkee_snapshot->base->uniqueid)) {
 		return;
 	}
 
diff --git a/res/res_chan_stats.c b/res/res_chan_stats.c
index bed95a0..e32498b 100644
--- a/res/res_chan_stats.c
+++ b/res/res_chan_stats.c
@@ -110,7 +110,7 @@
 		int64_t age;
 
 		age = ast_tvdiff_ms(*stasis_message_timestamp(message),
-			update->new_snapshot->creationtime);
+			update->new_snapshot->base->creationtime);
 		ast_statsd_log("channels.calltime", AST_STATSD_TIMER, age);
 
 		/* And decrement the channel count */
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 41195be..d88024a 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1351,7 +1351,7 @@
 {
 	const struct ast_channel_snapshot *snapshot = object;
 	struct ast_str **buf = arg;
-	ast_str_append(buf, 0, "%s,", snapshot->name);
+	ast_str_append(buf, 0, "%s,", snapshot->base->name);
 	return 0;
 }
 
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 3dfaabc..9b75146 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -179,7 +179,7 @@
 	}
 
 	enter_blob = stasis_message_data(message);
-	if (strcmp(enter_blob->channel->uniqueid, progress->transferee)) {
+	if (strcmp(enter_blob->channel->base->uniqueid, progress->transferee)) {
 		/* Don't care */
 		return;
 	}
diff --git a/res/res_stasis.c b/res/res_stasis.c
index 704d779..43833e1 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -1019,7 +1019,7 @@
 	}
 
 	if (replace_channel_snapshot) {
-		app_unsubscribe_channel_id(app, replace_channel_snapshot->uniqueid);
+		app_unsubscribe_channel_id(app, replace_channel_snapshot->base->uniqueid);
 	}
 	stasis_publish(ast_app_get_topic(app), msg);
 	ao2_ref(msg, -1);
@@ -2051,7 +2051,7 @@
 /* \brief Sanitization callback for channel snapshots */
 static int channel_snapshot_sanitizer(const struct ast_channel_snapshot *snapshot)
 {
-	if (!snapshot || !(snapshot->tech_properties & AST_CHAN_TP_INTERNAL)) {
+	if (!snapshot || !(snapshot->base->tech_properties & AST_CHAN_TP_INTERNAL)) {
 		return 0;
 	}
 	return 1;
diff --git a/res/stasis/app.c b/res/stasis/app.c
index b4f3bc6..78f5765 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -315,7 +315,7 @@
 		return;
 	}
 
-	chan = ast_channel_get_by_name(snapshot->uniqueid);
+	chan = ast_channel_get_by_name(snapshot->base->uniqueid);
 	if (!chan) {
 		return;
 	}
@@ -391,8 +391,8 @@
 	return ast_json_pack("{s: s, s: o, s: i, s: s, s: o}",
 		"type", "ChannelDestroyed",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"cause", snapshot->hangupcause,
-		"cause_txt", ast_cause2str(snapshot->hangupcause),
+		"cause", snapshot->hangup->cause,
+		"cause_txt", ast_cause2str(snapshot->hangup->cause),
 		"channel", json_channel);
 }
 
@@ -436,7 +436,7 @@
 	}
 
 	/* Empty application is not valid for a Newexten event */
-	if (ast_strlen_zero(new_snapshot->appl)) {
+	if (ast_strlen_zero(new_snapshot->dialplan->appl)) {
 		return NULL;
 	}
 
@@ -452,8 +452,8 @@
 	return ast_json_pack("{s: s, s: o, s: s, s: s, s: o}",
 		"type", "ChannelDialplan",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"dialplan_app", new_snapshot->appl,
-		"dialplan_app_data", AST_JSON_UTF8_VALIDATE(new_snapshot->data),
+		"dialplan_app", new_snapshot->dialplan->appl,
+		"dialplan_app_data", AST_JSON_UTF8_VALIDATE(new_snapshot->dialplan->data),
 		"channel", json_channel);
 }
 
@@ -481,9 +481,9 @@
 	return ast_json_pack("{s: s, s: o, s: i, s: s, s: o}",
 		"type", "ChannelCallerId",
 		"timestamp", ast_json_timeval(*tv, NULL),
-		"caller_presentation", new_snapshot->caller_pres,
+		"caller_presentation", new_snapshot->caller->pres,
 		"caller_presentation_txt", ast_describe_caller_presentation(
-			new_snapshot->caller_pres),
+			new_snapshot->caller->pres),
 		"channel", json_channel);
 }
 
@@ -541,7 +541,7 @@
 	}
 
 	if (ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {
-		unsubscribe(app, "channel", update->new_snapshot->uniqueid, 1);
+		unsubscribe(app, "channel", update->new_snapshot->base->uniqueid, 1);
 	}
 }
 
@@ -768,7 +768,7 @@
 	struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
 	struct ast_bridge_snapshot *bridge = transfer_msg->bridge;
 
-	if (bridge_app_subscribed(app, transfer_msg->transferer->uniqueid) ||
+	if (bridge_app_subscribed(app, transfer_msg->transferer->base->uniqueid) ||
 		(bridge && bridge_app_subscribed_involved(app, bridge))) {
 		stasis_publish(app->topic, message);
 	}
@@ -781,9 +781,9 @@
 	struct ast_attended_transfer_message *transfer_msg = stasis_message_data(message);
 	int subscribed = 0;
 
-	subscribed = bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->uniqueid);
+	subscribed = bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->base->uniqueid);
 	if (!subscribed) {
-		subscribed = bridge_app_subscribed(app, transfer_msg->to_transfer_target.channel_snapshot->uniqueid);
+		subscribed = bridge_app_subscribed(app, transfer_msg->to_transfer_target.channel_snapshot->base->uniqueid);
 	}
 	if (!subscribed && transfer_msg->to_transferee.bridge_snapshot) {
 		subscribed = bridge_app_subscribed_involved(app, transfer_msg->to_transferee.bridge_snapshot);
@@ -798,16 +798,16 @@
 			subscribed = bridge_app_subscribed(app, transfer_msg->dest.bridge);
 			break;
 		case AST_ATTENDED_TRANSFER_DEST_LINK:
-			subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[0]->uniqueid);
+			subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[0]->base->uniqueid);
 			if (!subscribed) {
-				subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[1]->uniqueid);
+				subscribed = bridge_app_subscribed(app, transfer_msg->dest.links[1]->base->uniqueid);
 			}
 			break;
 		break;
 		case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
 			subscribed = bridge_app_subscribed_involved(app, transfer_msg->dest.threeway.bridge_snapshot);
 			if (!subscribed) {
-				subscribed = bridge_app_subscribed(app, transfer_msg->dest.threeway.channel_snapshot->uniqueid);
+				subscribed = bridge_app_subscribed(app, transfer_msg->dest.threeway.channel_snapshot->base->uniqueid);
 			}
 			break;
 		default:
diff --git a/tests/test_cel.c b/tests/test_cel.c
index c1e7340..6c8bd61 100644
--- a/tests/test_cel.c
+++ b/tests/test_cel.c
@@ -329,7 +329,7 @@
 			ao2_cleanup);
 
 		/* Don't add the channel for which this message is being generated */
-		if (!strcmp(current_chan, chan->uniqueid)) {
+		if (!strcmp(current_chan, chan->base->uniqueid)) {
 			continue;
 		}
 
@@ -338,7 +338,7 @@
 			continue;
 		}
 
-		ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
+		ast_str_append(&peer_str, 0, "%s,", current_snapshot->base->name);
 	}
 	ao2_iterator_destroy(&i);
 
@@ -1668,8 +1668,8 @@
 	stasis_publish(ast_channel_topic(chan_alice), local_opt_begin);
 	stasis_publish(ast_channel_topic(chan_alice), local_opt_end);
 
-	extra = ast_json_pack("{s: s, s: s}", "local_two", bob_snapshot->name,
-		"local_two_uniqueid", bob_snapshot->uniqueid);
+	extra = ast_json_pack("{s: s, s: s}", "local_two", bob_snapshot->base->name,
+		"local_two_uniqueid", bob_snapshot->base->uniqueid);
 	ast_test_validate(test, extra != NULL);
 
 	APPEND_EVENT_SNAPSHOT(alice_snapshot, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
diff --git a/tests/test_stasis_channels.c b/tests/test_stasis_channels.c
index f73d882..f8d2a03 100644
--- a/tests/test_stasis_channels.c
+++ b/tests/test_stasis_channels.c
@@ -220,12 +220,12 @@
 	/* Test for single match */
 	snapshot = ast_multi_channel_blob_get_channel(blob, "Caller");
 	ast_test_validate(test, NULL != snapshot);
-	ast_test_validate(test, 0 == strcmp("TEST/Alice", snapshot->name));
+	ast_test_validate(test, 0 == strcmp("TEST/Alice", snapshot->base->name));
 
 	/* Test for single match, multiple possibilities */
 	snapshot = ast_multi_channel_blob_get_channel(blob, "Peer");
 	ast_test_validate(test, NULL != snapshot);
-	ast_test_validate(test, 0 != strcmp("TEST/Alice", snapshot->name));
+	ast_test_validate(test, 0 != strcmp("TEST/Alice", snapshot->base->name));
 
 	/* Multi-match */
 	matches = ast_multi_channel_blob_get_channels(blob, "Peer");

-- 
To view, visit https://gerrit.asterisk.org/10622
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I5d7ef3df963a88ac47bc187d73c5225c315f8423
Gerrit-Change-Number: 10622
Gerrit-PatchSet: 10
Gerrit-Owner: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Corey Farrell <git at cfware.com>
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Jenkins2 (1000185)
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20181126/1e1bddc1/attachment-0001.html>


More information about the asterisk-code-review mailing list