Commit e8a975195b for asterisk.org
commit e8a975195b35a208a1e0b874df15658e09959f7d
Author: Daniel McLean <daniel.mclean@biamp.com>
Date: Wed Oct 28 11:03:10 2026 +1000
chan_rtp: Add a payload type option to UnicastRTP and externalMedia.
A UnicastRTP channel has no SDP, so it sends with the payload type
Asterisk assigns to its format and on receive only knows the static
payload types. Media sent to it with a dynamic payload type is
dropped, so it cannot receive formats that have no static payload
type, such as slin16 and slin48.
The new `p(<payload type>)` option takes a payload type in the dynamic
range (96-127), which the channel then both sends with and accepts.
ARI externalMedia passes it on from a new payload_type parameter,
valid only for the rtp encapsulation. Without it nothing changes.
Assisted-by: Claude Opus 5.5
Resolves: #2268
UserNote: UnicastRTP channels have a new `p(<payload type>)` option to
send and receive media with a payload type in the dynamic range
(96-127).
DeveloperNote: A "payload_type" parameter has been added to the
channels/externalMedia ARI endpoint. For the rtp encapsulation it sets
the payload type, in the dynamic range 96-127, that the channel sends
and receives media with.
diff --git a/channels/chan_rtp.c b/channels/chan_rtp.c
index 01b57d7aad..9373f34efa 100644
--- a/channels/chan_rtp.c
+++ b/channels/chan_rtp.c
@@ -41,6 +41,7 @@
#include "asterisk/app.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/causes.h"
+#include "asterisk/conversions.h"
#include "asterisk/format_cache.h"
#include "asterisk/multicast_rtp.h"
#include "asterisk/dns_core.h"
@@ -253,11 +254,13 @@ enum {
OPT_RTP_CODEC = (1 << 0),
OPT_RTP_ENGINE = (1 << 1),
OPT_RTP_GLUE = (1 << 2),
+ OPT_RTP_PAYLOAD = (1 << 3),
};
enum {
OPT_ARG_RTP_CODEC,
OPT_ARG_RTP_ENGINE,
+ OPT_ARG_RTP_PAYLOAD,
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE
};
@@ -269,6 +272,8 @@ AST_APP_OPTIONS(unicast_rtp_options, BEGIN_OPTIONS
AST_APP_OPTION_ARG('e', OPT_RTP_ENGINE, OPT_ARG_RTP_ENGINE),
/*! Provide RTP glue for the channel */
AST_APP_OPTION('g', OPT_RTP_GLUE),
+ /*! Set the dynamic payload type (96-127) to send and receive with */
+ AST_APP_OPTION_ARG('p', OPT_RTP_PAYLOAD, OPT_ARG_RTP_PAYLOAD),
END_OPTIONS );
static const struct ast_datastore_info chan_rtp_datastore_info = {
@@ -285,6 +290,7 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
struct ast_channel *chan;
struct ast_format_cap *caps = NULL;
struct ast_format *fmt = NULL;
+ int payload = -1;
const char *engine_name;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(destination);
@@ -348,6 +354,15 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
}
}
+ if (ast_test_flag(&opts, OPT_RTP_PAYLOAD)
+ && (ast_str_to_int(opt_args[OPT_ARG_RTP_PAYLOAD], &payload)
+ || payload < AST_RTP_PT_FIRST_DYNAMIC || payload >= AST_RTP_MAX_PT)) {
+ ast_log(LOG_ERROR, "Payload type '%s' for '%s' is not in the dynamic range %d-%d\n",
+ opt_args[OPT_ARG_RTP_PAYLOAD], args.destination,
+ AST_RTP_PT_FIRST_DYNAMIC, AST_RTP_MAX_PT - 1);
+ goto failure;
+ }
+
caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!caps) {
goto failure;
@@ -370,6 +385,16 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
goto failure;
}
+ if (payload >= 0) {
+ struct ast_rtp_codecs *codecs = ast_rtp_instance_get_codecs(instance);
+
+ if (ast_rtp_codecs_payload_replace_format(codecs, payload, fmt)
+ || ast_rtp_codecs_payload_set_rx(codecs, payload, fmt) < 0) {
+ ast_rtp_instance_destroy(instance);
+ goto failure;
+ }
+ }
+
chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,
requestor, 0, "UnicastRTP/%s-%p", args.destination, instance);
if (!chan) {
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index c9d4a417fc..b11e4325c6 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -2419,10 +2419,16 @@ static int external_media_rtp_udp(struct ast_ari_channels_external_media_args *a
char *endpoint;
struct ast_channel *chan;
struct varshead *vars;
+ char payload_type[16] = "";
- if (ast_asprintf(&endpoint, "UnicastRTP/%s/c(%s)",
+ if (args->payload_type) {
+ snprintf(payload_type, sizeof(payload_type), "p(%d)", args->payload_type);
+ }
+
+ if (ast_asprintf(&endpoint, "UnicastRTP/%s/c(%s)%s",
args->external_host,
- args->format) == -1) {
+ args->format,
+ payload_type) == -1) {
return 1;
}
@@ -2681,6 +2687,17 @@ void ast_ari_channels_external_media(struct ast_variable *headers,
}
}
+ if (args->payload_type) {
+ if (strcasecmp(args->encapsulation, "rtp")) {
+ ast_ari_response_error(response, 400, "Bad Request", "payload_type is only valid for the rtp encapsulation");
+ return;
+ }
+ if (args->payload_type < AST_RTP_PT_FIRST_DYNAMIC || args->payload_type >= AST_RTP_MAX_PT) {
+ ast_ari_response_error(response, 400, "Bad Request", "payload_type must be in the dynamic range 96-127");
+ return;
+ }
+ }
+
if (strcasecmp(args->encapsulation, "rtp") == 0 && strcasecmp(args->transport, "udp") == 0) {
if (external_media_rtp_udp(args, variables, report_event_variables, response)) {
ast_ari_response_error(
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 2be83a0e7e..15a8e9f34f 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -921,6 +921,8 @@ struct ast_ari_channels_external_media_args {
const char *data;
/*! Transport-specific data. For websocket this is appended to the dialstring. */
const char *transport_data;
+ /*! RTP payload type to send and receive media with, in the dynamic range 96-127. Only valid for the rtp encapsulation. */
+ int payload_type;
};
/*!
* \brief Body parsing function for /channels/externalMedia.
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index d07631df46..ec5eccfb22 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -3209,6 +3209,10 @@ int ast_ari_channels_external_media_parse_body(
if (field) {
args->transport_data = ast_json_string_get(field);
}
+ field = ast_json_object_get(body, "payload_type");
+ if (field) {
+ args->payload_type = ast_json_integer_get(field);
+ }
return 0;
}
@@ -3264,6 +3268,9 @@ static void ast_ari_channels_external_media_cb(
if (strcmp(i->name, "transport_data") == 0) {
args.transport_data = (i->value);
} else
+ if (strcmp(i->name, "payload_type") == 0) {
+ args.payload_type = atoi(i->value);
+ } else
{}
}
args.variables = body;
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index f1b711d36a..82a9830a51 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -2137,6 +2137,19 @@
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "payload_type",
+ "description": "RTP payload type to send and receive media with, in the dynamic range 96-127. Only valid for the rtp encapsulation.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "int",
+ "allowableValues": {
+ "valueType": "RANGE",
+ "min": 96,
+ "max": 127
+ }
}
],
"errorResponses": [