Compare commits

...

5 Commits

Author SHA1 Message Date
Naveen Albert
45646be97a func_scramble: Add example to XML documentation.
The previous lack of an example made it ambiguous if the arguments went
inside the function arguments or were part of the right-hand value.

Resolves: #1485
2025-09-30 15:09:43 +00:00
Naveen Albert
a96d7fcfaf sig_analog: Eliminate potential timeout with Last Number Redial.
If Last Number Redial is used to redial, ensure that we do not wait
for further digits. This was possible if the number that was last
dialed is a prefix of another possible dialplan match. Since all we
did is copy the number into the extension buffer, if other matches
are now possible, there would thus be a timeout before the call went
through. We now complete redialed calls immediaetly in all cases.

Resolves: #1483
2025-09-30 15:09:14 +00:00
George Joseph
559ea45ddd ARI: The bridges play and record APIs now handle sample rates > 8K correctly.
The bridge play and record APIs were forcing the Announcer/Recorder channel
to slin8 which meant that if you played or recorded audio with a sample
rate > 8K, it was downsampled to 8K limiting the bandwidth.

* The /bridges/play REST APIs have a new "announcer_format" parameter that
  allows the caller to explicitly set the format on the "Announcer" channel
  through which the audio is played into the bridge.  If not specified, the
  default depends on how many channels are currently in the bridge.  If
  a single channel is in the bridge, then the Announcer channel's format
  will be set to the same as that channel's.  If multiple channels are in the
  bridge, the channels will be scanned to find the one with the highest
  sample rate and the Announcer channel's format will be set to the slin
  format that has an equal to or greater than sample rate.

* The /bridges/record REST API has a new "recorder_format" parameter that
  allows the caller to explicitly set the format on the "Recorder" channel
  from which audio is retrieved to write to the file.  If not specified,
  the Recorder channel's format will be set to the format that was requested
  to save the audio in.

Resolves: #1479

DeveloperNote: The ARI /bridges/play and /bridges/record REST APIs have new
parameters that allow the caller to specify the format to be used on the
"Announcer" and "Recorder" channels respecitvely.
2025-09-30 13:59:34 +00:00
Max Grobecker
2c97fd3ea4 res_pjsip_geolocation: Add support for Geolocation loc-src parameter
This adds support for the Geolocation 'loc-src' parameter to res_pjsip_geolocation.
The already existing config option 'location_source` in res_geolocation is documented to add a 'loc-src' parameter containing a user-defined FQDN to the 'Geolocation:' header,
but that option had no effect as it was not implemented by res_pjsip_geolocation.

If the `location_source` configuration option is not set or invalid, that parameter will not be added (this is already checked by res_geolocation).

This commits adds already documented functionality.
2025-09-30 13:53:45 +00:00
Joshua C. Colp
ece6ed9459 sorcery: Move from threadpool to taskpool.
This change moves observer invocation from the use of
a threadpool to a taskpool. The taskpool options have also
been adjusted to ensure that at least one taskprocessor
remains available at all times.
2025-09-30 13:50:37 +00:00
8 changed files with 175 additions and 26 deletions

View File

@@ -2421,6 +2421,7 @@ static void *__analog_ss_thread(void *data)
}
while (len < AST_MAX_EXTENSION-1) {
int is_exten_parking = 0;
int is_lastnumredial = 0;
/* Read digit unless it's supposed to be immediate, in which case the
only answer is 's' */
@@ -2455,6 +2456,9 @@ static void *__analog_ss_thread(void *data)
analog_lock_private(p);
ast_copy_string(exten, p->lastexten, sizeof(exten));
analog_unlock_private(p);
/* If Last Number Redial was used, even if the user might normally be able to dial further
* digits for the digits dialed, we should complete the call immediately without delay. */
is_lastnumredial = 1;
} else {
ast_verb(3, "Last Number Redial not possible on channel %d (no saved number)\n", p->channel);
res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
@@ -2464,7 +2468,7 @@ static void *__analog_ss_thread(void *data)
}
}
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !is_exten_parking) {
if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
if (!res || is_lastnumredial || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
if (getforward) {
/* Record this as the forwarding extension */
analog_lock_private(p);

View File

@@ -52,6 +52,9 @@
This is not intended to be used for securely scrambling
audio. It merely renders obfuscates audio on a channel
to render it unintelligible, as a privacy enhancement.</para>
<example title="Scramble speech in both directions">
same => n,Set(SCRAMBLE()=both)
</example>
</description>
<see-also>
<ref type="application">ChanSpy</ref>

View File

@@ -39,7 +39,7 @@
#include "asterisk/netsock2.h"
#include "asterisk/module.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/threadpool.h"
#include "asterisk/taskpool.h"
#include "asterisk/json.h"
#include "asterisk/vector.h"
#include "asterisk/cli.h"
@@ -83,8 +83,8 @@
#define NOTIFY_WIZARD_OBSERVERS(container, callback, ...) \
NOTIFY_GENERIC_OBSERVERS(container, sorcery_wizard_observer, callback, __VA_ARGS__)
/*! \brief Thread pool for observers */
static struct ast_threadpool *threadpool;
/*! \brief Taskpool for observers */
static struct ast_taskpool *taskpool;
/*! \brief Structure for an internal wizard instance */
struct ast_sorcery_internal_wizard {
@@ -402,8 +402,8 @@ static struct ast_cli_entry cli_commands[] = {
static void sorcery_cleanup(void)
{
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
ast_threadpool_shutdown(threadpool);
threadpool = NULL;
ast_taskpool_shutdown(taskpool);
taskpool = NULL;
ao2_cleanup(wizards);
wizards = NULL;
ao2_cleanup(observers);
@@ -443,19 +443,20 @@ static void parse_general_options(void)
int ast_sorcery_init(void)
{
struct ast_threadpool_options options = {
.version = AST_THREADPOOL_OPTIONS_VERSION,
struct ast_taskpool_options options = {
.version = AST_TASKPOOL_OPTIONS_VERSION,
.auto_increment = 1,
.max_size = 0,
.idle_timeout = 60,
.initial_size = 0,
.initial_size = 1,
.minimum_size = 1,
};
ast_assert(wizards == NULL);
parse_general_options();
threadpool = ast_threadpool_create("sorcery", NULL, &options);
if (!threadpool) {
taskpool = ast_taskpool_create("sorcery", &options);
if (!taskpool) {
return -1;
}
@@ -807,7 +808,7 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
/* Create name with seq number appended. */
ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "sorcery/%s", type);
if (!(object_type->serializer = ast_threadpool_serializer(tps_name, threadpool))) {
if (!(object_type->serializer = ast_taskpool_serializer(tps_name, taskpool))) {
ao2_ref(object_type, -1);
return NULL;
}

View File

@@ -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) {

View File

@@ -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. */

View File

@@ -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

View File

@@ -602,10 +602,13 @@ static void handle_outgoing_request(struct ast_sip_session *session, struct pjsi
uri = ast_strdupa(ast_str_buffer(buf));
ast_str_reset(buf);
ast_str_set(&buf, 0, "<%s>", uri);
if (!ast_strlen_zero(final_eprofile->location_source)) {
ast_str_append(&buf, 0, ";loc-src=%s", final_eprofile->location_source);
}
uri = ast_strdupa(ast_str_buffer(buf));
ast_trace(4, "%s: Using URI '%s'\n", session_name, uri);
/* It's almost impossible for add header to fail but you never know */
geoloc_hdr = ast_sip_add_header2(tdata, "Geolocation", uri);
if (geoloc_hdr == NULL) {

View File

@@ -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.",