diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c index fafe03aa58..6d77aa5efd 100644 --- a/res/ari/resource_bridges.c +++ b/res/ari/resource_bridges.c @@ -311,7 +311,8 @@ static void *bridge_channel_control_thread(void *data) return NULL; } -static struct ast_channel *prepare_bridge_media_channel(const char *type) +static struct ast_channel *prepare_bridge_media_channel(const char *type, + struct ast_format *channel_format) { RAII_VAR(struct ast_format_cap *, cap, NULL, ao2_cleanup); struct ast_channel *chan; @@ -321,7 +322,8 @@ static struct ast_channel *prepare_bridge_media_channel(const char *type) return NULL; } - ast_format_cap_append(cap, ast_format_slin, 0); + /* This bumps the format's refcount */ + ast_format_cap_append(cap, channel_format, 0); chan = ast_request(type, cap, NULL, NULL, "ARI", NULL); if (!chan) { @@ -407,6 +409,7 @@ static int ari_bridges_play_helper(const char **args_media, static void ari_bridges_play_new(const char **args_media, size_t args_media_count, + const char *args_format, const char *args_lang, int args_offset_ms, int args_skipms, @@ -424,14 +427,64 @@ static void ari_bridges_play_new(const char **args_media, struct stasis_topic *bridge_topic; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; + struct ast_format *channel_format = NULL; struct ast_frame prog = { .frametype = AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_PROGRESS, }; + /* + * Determine the format for the playback channel. + * If a format was specified, use that if it's valid. + * Otherwise, if the bridge is empty, use slin. + * If the bridge has one channel, use that channel's raw write format. + * If the bridge has multiple channels, use the slin format that + * will handle the highest sample rate of the raw write format of all the channels. + */ + if (!ast_strlen_zero(args_format)) { + channel_format = ast_format_cache_get(args_format); + if (!channel_format) { + ast_ari_response_error( + response, 422, "Unprocessable Entity", + "specified announcer_format is unknown on this system"); + return; + } + /* + * ast_format_cache_get() bumps the refcount but the other calls + * to retrieve formats don't so we'll drop this reference. + * It'll be bumped again in the prepare_bridge_media_channel() call below. + */ + ao2_ref(channel_format, -1); + } else { + ast_bridge_lock(bridge); + if (bridge->num_channels == 0) { + channel_format = ast_format_slin; + } else if (bridge->num_channels == 1) { + struct ast_bridge_channel *bc = NULL; + bc = AST_LIST_FIRST(&bridge->channels); + if (bc) { + channel_format = ast_channel_rawwriteformat(bc->chan); + } + } else { + struct ast_bridge_channel *bc = NULL; + unsigned int max_sample_rate = 0; + AST_LIST_TRAVERSE(&bridge->channels, bc, entry) { + struct ast_format *fmt = ast_channel_rawwriteformat(bc->chan); + max_sample_rate = MAX(ast_format_get_sample_rate(fmt), max_sample_rate); + } + channel_format = ast_format_cache_get_slin_by_rate(max_sample_rate); + } + ast_bridge_unlock(bridge); + } - if (!(play_channel = prepare_bridge_media_channel("Announcer"))) { + if (!channel_format) { + channel_format = ast_format_slin; + } + + play_channel = prepare_bridge_media_channel("Announcer", channel_format); + ao2_cleanup(channel_format); + if (!play_channel) { ast_ari_response_error( response, 500, "Internal Error", "Could not create playback channel"); return; @@ -578,6 +631,7 @@ static void ari_bridges_handle_play( const char *args_bridge_id, const char **args_media, size_t args_media_count, + const char *args_format, const char *args_lang, int args_offset_ms, int args_skipms, @@ -608,7 +662,7 @@ static void ari_bridges_handle_play( return; } - ari_bridges_play_new(args_media, args_media_count, args_lang, args_offset_ms, + ari_bridges_play_new(args_media, args_media_count, args_format, args_lang, args_offset_ms, args_skipms, args_playback_id, response, bridge); } @@ -620,6 +674,7 @@ void ast_ari_bridges_play(struct ast_variable *headers, ari_bridges_handle_play(args->bridge_id, args->media, args->media_count, + args->announcer_format, args->lang, args->offsetms, args->skipms, @@ -634,6 +689,7 @@ void ast_ari_bridges_play_with_id(struct ast_variable *headers, ari_bridges_handle_play(args->bridge_id, args->media, args->media_count, + args->announcer_format, args->lang, args->offsetms, args->skipms, @@ -660,6 +716,8 @@ void ast_ari_bridges_record(struct ast_variable *headers, size_t uri_name_maxlen; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; + struct ast_format *file_format = NULL; + struct ast_format *channel_format = NULL; ast_assert(response != NULL); @@ -667,7 +725,34 @@ void ast_ari_bridges_record(struct ast_variable *headers, return; } - if (!(record_channel = prepare_bridge_media_channel("Recorder"))) { + file_format = ast_get_format_for_file_ext(args->format); + if (!file_format) { + ast_ari_response_error( + response, 422, "Unprocessable Entity", + "specified format is unknown on this system"); + return; + } + + if (!ast_strlen_zero(args->recorder_format)) { + channel_format = ast_format_cache_get(args->recorder_format); + if (!channel_format) { + ast_ari_response_error( + response, 422, "Unprocessable Entity", + "specified recorder_format is unknown on this system"); + return; + } + /* + * ast_format_cache_get() bumps the refcount but the other calls + * to retrieve formats don't so we'll drop this reference. + * It'll be bumped again in the prepare_bridge_media_channel() call below. + */ + ao2_ref(channel_format, -1); + + } else { + channel_format = file_format; + } + + if (!(record_channel = prepare_bridge_media_channel("Recorder", channel_format))) { ast_ari_response_error( response, 500, "Internal Server Error", "Failed to create recording channel"); return; @@ -728,13 +813,6 @@ void ast_ari_bridges_record(struct ast_variable *headers, return; } - if (!ast_get_format_for_file_ext(options->format)) { - ast_ari_response_error( - response, 422, "Unprocessable Entity", - "specified format is unknown on this system"); - return; - } - recording = stasis_app_control_record(control, options); if (recording == NULL) { switch(errno) { diff --git a/res/ari/resource_bridges.h b/res/ari/resource_bridges.h index d494d89008..068ac28d60 100644 --- a/res/ari/resource_bridges.h +++ b/res/ari/resource_bridges.h @@ -285,6 +285,8 @@ struct ast_ari_bridges_play_args { size_t media_count; /*! Parsing context for media. */ char *media_parse; + /*! Format of the 'Anouncer' channel attached to the bridge. Defaults to the format of the channel in the bridge with the highest sampe rate. */ + const char *announcer_format; /*! For sounds, selects language for sound. */ const char *lang; /*! Number of milliseconds to skip before playing. Only applies to the first URI if multiple media URIs are specified. */ @@ -327,6 +329,8 @@ struct ast_ari_bridges_play_with_id_args { size_t media_count; /*! Parsing context for media. */ char *media_parse; + /*! Format of the 'Anouncer' channel attached to the bridge. Defaults to the format of the channel in the bridge with the highest sampe rate. */ + const char *announcer_format; /*! For sounds, selects language for sound. */ const char *lang; /*! Number of milliseconds to skip before playing. Only applies to the first URI if multiple media URIs are specified. */ @@ -363,6 +367,8 @@ struct ast_ari_bridges_record_args { const char *name; /*! Format to encode audio in */ const char *format; + /*! Format of the 'Recorder' channel attached to the bridge. Defaults to the same format as the 'format' parameter. */ + const char *recorder_format; /*! Maximum duration of the recording, in seconds. 0 for no limit. */ int max_duration_seconds; /*! Maximum duration of silence, in seconds. 0 for no limit. */ diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c index ba649a6ded..465f6e947a 100644 --- a/res/res_ari_bridges.c +++ b/res/res_ari_bridges.c @@ -1044,6 +1044,10 @@ int ast_ari_bridges_play_parse_body( args->media[0] = ast_json_string_get(field); } } + field = ast_json_object_get(body, "announcer_format"); + if (field) { + args->announcer_format = ast_json_string_get(field); + } field = ast_json_object_get(body, "lang"); if (field) { args->lang = ast_json_string_get(field); @@ -1128,6 +1132,9 @@ static void ast_ari_bridges_play_cb( args.media[j] = (vals[j]); } } else + if (strcmp(i->name, "announcer_format") == 0) { + args.announcer_format = (i->value); + } else if (strcmp(i->name, "lang") == 0) { args.lang = (i->value); } else @@ -1164,6 +1171,7 @@ static void ast_ari_bridges_play_cb( case 501: /* Not Implemented */ case 404: /* Bridge not found */ case 409: /* Bridge not in a Stasis application */ + case 422: /* The format specified is unknown on this system */ is_valid = 1; break; default: @@ -1223,6 +1231,10 @@ int ast_ari_bridges_play_with_id_parse_body( args->media[0] = ast_json_string_get(field); } } + field = ast_json_object_get(body, "announcer_format"); + if (field) { + args->announcer_format = ast_json_string_get(field); + } field = ast_json_object_get(body, "lang"); if (field) { args->lang = ast_json_string_get(field); @@ -1303,6 +1315,9 @@ static void ast_ari_bridges_play_with_id_cb( args.media[j] = (vals[j]); } } else + if (strcmp(i->name, "announcer_format") == 0) { + args.announcer_format = (i->value); + } else if (strcmp(i->name, "lang") == 0) { args.lang = (i->value); } else @@ -1339,6 +1354,7 @@ static void ast_ari_bridges_play_with_id_cb( case 501: /* Not Implemented */ case 404: /* Bridge not found */ case 409: /* Bridge not in a Stasis application */ + case 422: /* The format specified is unknown on this system */ is_valid = 1; break; default: @@ -1377,6 +1393,10 @@ int ast_ari_bridges_record_parse_body( if (field) { args->format = ast_json_string_get(field); } + field = ast_json_object_get(body, "recorder_format"); + if (field) { + args->recorder_format = ast_json_string_get(field); + } field = ast_json_object_get(body, "maxDurationSeconds"); if (field) { args->max_duration_seconds = ast_json_integer_get(field); @@ -1428,6 +1448,9 @@ static void ast_ari_bridges_record_cb( if (strcmp(i->name, "format") == 0) { args.format = (i->value); } else + if (strcmp(i->name, "recorder_format") == 0) { + args.recorder_format = (i->value); + } else if (strcmp(i->name, "maxDurationSeconds") == 0) { args.max_duration_seconds = atoi(i->value); } else diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json index 4cd15b9794..ae5ee56fec 100644 --- a/rest-api/api-docs/bridges.json +++ b/rest-api/api-docs/bridges.json @@ -490,6 +490,14 @@ "allowMultiple": true, "dataType": "string" }, + { + "name": "announcer_format", + "description": "Format of the 'Anouncer' channel attached to the bridge. Defaults to the format of the channel in the bridge with the highest sampe rate.", + "paramType": "query", + "required": false, + "allowMultiple": false, + "dataType": "string" + }, { "name": "lang", "description": "For sounds, selects language for sound.", @@ -510,7 +518,6 @@ "valueType": "RANGE", "min": 0 } - }, { "name": "skipms", @@ -542,6 +549,10 @@ { "code": 409, "reason": "Bridge not in a Stasis application" + }, + { + "code": 422, + "reason": "The format specified is unknown on this system" } ] } @@ -585,6 +596,14 @@ "allowMultiple": true, "dataType": "string" }, + { + "name": "announcer_format", + "description": "Format of the 'Anouncer' channel attached to the bridge. Defaults to the format of the channel in the bridge with the highest sampe rate.", + "paramType": "query", + "required": false, + "allowMultiple": false, + "dataType": "string" + }, { "name": "lang", "description": "For sounds, selects language for sound.", @@ -628,6 +647,10 @@ { "code": 409, "reason": "Bridge not in a Stasis application" + }, + { + "code": 422, + "reason": "The format specified is unknown on this system" } ] @@ -672,6 +695,14 @@ "allowMultiple": false, "dataType": "string" }, + { + "name": "recorder_format", + "description": "Format of the 'Recorder' channel attached to the bridge. Defaults to the same format as the 'format' parameter.", + "paramType": "query", + "required": false, + "allowMultiple": false, + "dataType": "string" + }, { "name": "maxDurationSeconds", "description": "Maximum duration of the recording, in seconds. 0 for no limit.",