diff --git a/main/features.c b/main/features.c
index 1e578d2b9e27f938782e2ac557317958c4b8c860..ddbd04b4687971c463deb475e9f05fdfb9e14a8c 100644
--- a/main/features.c
+++ b/main/features.c
@@ -3161,6 +3161,42 @@ static void clear_dialed_interfaces(struct ast_channel *chan)
 	ast_channel_unlock(chan);
 }
 
+/*!
+ * \internal
+ * \brief Helper to add a builtin DTMF feature hook to the features struct.
+ * \since 12.0.0
+ *
+ * \param features Bridge features to setup.
+ * \param chan Get features from this channel.
+ * \param flags Feature flags on the channel.
+ * \param feature_flag Feature flag to test.
+ * \param feature_name features.conf name of feature.
+ * \param feature_bridge Bridge feature enum to get hook callback.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int builtin_features_helper(struct ast_bridge_features *features, struct ast_channel *chan,
+	struct ast_flags *flags, unsigned int feature_flag, const char *feature_name, enum ast_bridge_builtin_feature feature_bridge)
+{
+	char dtmf[AST_FEATURE_MAX_LEN];
+	int res;
+
+	res = 0;
+	if (ast_test_flag(flags, feature_flag)
+		&& !builtin_feature_get_exten(chan, feature_name, dtmf, sizeof(dtmf))
+		&& !ast_strlen_zero(dtmf)) {
+		res = ast_bridge_features_enable(features, feature_bridge, dtmf, NULL, NULL,
+			AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
+		if (res) {
+			ast_log(LOG_ERROR, "Channel %s: Requested DTMF feature %s not available.\n",
+				ast_channel_name(chan), feature_name);
+		}
+	}
+
+	return res;
+}
+
 /*!
  * \internal
  * \brief Setup bridge builtin features.
@@ -3175,7 +3211,6 @@ static void clear_dialed_interfaces(struct ast_channel *chan)
 static int setup_bridge_features_builtin(struct ast_bridge_features *features, struct ast_channel *chan)
 {
 	struct ast_flags *flags;
-	char dtmf[AST_FEATURE_MAX_LEN];
 	int res;
 
 	ast_channel_lock(chan);
@@ -3186,43 +3221,12 @@ static int setup_bridge_features_builtin(struct ast_bridge_features *features, s
 	}
 
 	res = 0;
-	if (ast_test_flag(flags, AST_FEATURE_REDIRECT)) {
-		/* Add atxfer and blind transfer. */
-		if (!builtin_feature_get_exten(chan, "blindxfer", dtmf, sizeof(dtmf))
-				&& !ast_strlen_zero(dtmf)) {
-			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_BLINDTRANSFER, dtmf,
-					NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
-		}
-		if (!builtin_feature_get_exten(chan, "atxfer", dtmf, sizeof(dtmf)) &&
-				!ast_strlen_zero(dtmf)) {
-			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, dtmf,
-					NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
-		}
-	}
-	if (ast_test_flag(flags, AST_FEATURE_DISCONNECT) &&
-			!builtin_feature_get_exten(chan, "disconnect", dtmf, sizeof(dtmf)) &&
-			!ast_strlen_zero(dtmf)) {
-		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_HANGUP, dtmf,
-				NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
-	}
-	if (ast_test_flag(flags, AST_FEATURE_PARKCALL) &&
-			!builtin_feature_get_exten(chan, "parkcall", dtmf, sizeof(dtmf)) &&
-			!ast_strlen_zero(dtmf)) {
-		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_PARKCALL, dtmf,
-				NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
-	}
-	if (ast_test_flag(flags, AST_FEATURE_AUTOMON) &&
-			!builtin_feature_get_exten(chan, "automon", dtmf, sizeof(dtmf)) &&
-			!ast_strlen_zero(dtmf)) {
-		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMON, dtmf,
-				NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
-	}
-	if (ast_test_flag(flags, AST_FEATURE_AUTOMIXMON) &&
-			!builtin_feature_get_exten(chan, "automixmon", dtmf, sizeof(dtmf)) &&
-			!ast_strlen_zero(dtmf)) {
-		res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMIXMON, dtmf,
-				NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
-	}
+	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "blindxfer", AST_BRIDGE_BUILTIN_BLINDTRANSFER);
+	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "atxfer", AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER);
+	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_DISCONNECT, "disconnect", AST_BRIDGE_BUILTIN_HANGUP);
+	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_PARKCALL, "parkcall", AST_BRIDGE_BUILTIN_PARKCALL);
+	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMON, "automon", AST_BRIDGE_BUILTIN_AUTOMON);
+	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMIXMON, "automixmon", AST_BRIDGE_BUILTIN_AUTOMIXMON);
 
 	return res ? -1 : 0;
 }