Browse Source

TT#79601 support media/DTMF playback to all parties

Change-Id: I534ba39c800e0b075c1502808a56b887baf3e323
(cherry picked from commit c1d29a41a7)
changes/53/39353/1
Richard Fuchs 6 years ago
parent
commit
dfa67f19c2
3 changed files with 84 additions and 51 deletions
  1. +5
    -3
      README.md
  2. +77
    -47
      daemon/call_interfaces.c
  3. +2
    -1
      utils/rtpengine-ng-client

+ 5
- 3
README.md View File

@ -1478,7 +1478,8 @@ and stopped independently of each other.
Only available if compiled with transcoding support. The message must contain the key `call-id` and one Only available if compiled with transcoding support. The message must contain the key `call-id` and one
of the participant selection keys described under the `block DTMF` message (such as `from-tag`, of the participant selection keys described under the `block DTMF` message (such as `from-tag`,
`address`, or `label`).
`address`, or `label`). Alternatively, the `all` flag can be set to play the media to all involved
call parties.
Starts playback of a provided media file to the selected call participant. The format of the media file Starts playback of a provided media file to the selected call participant. The format of the media file
can be anything that is supported by *ffmpeg*, for example a `.wav` or `.mp3` file. It will automatically can be anything that is supported by *ffmpeg*, for example a `.wav` or `.mp3` file. It will automatically
@ -1518,8 +1519,9 @@ The same participant selection keys as for the `play media` message can and must
------------------- -------------------
Instructs *rtpengine* to inject a DTMF tone or event into a running audio stream. A call participant must Instructs *rtpengine* to inject a DTMF tone or event into a running audio stream. A call participant must
be selected in the same way as described under the `block DTMF` message above. The selected call participant
is the one generating the DTMF event, not the one receiving it.
be selected in the same way as described under the `play media` message above (including the possibility
of using the `all` flag). The selected call participant is the one generating the DTMF event, not the
one receiving it.
The dictionary key `code` must be present in the message, indicating the DTMF event to be generated. It can The dictionary key `code` must be present in the message, indicating the DTMF event to be generated. It can
be either an integer with values 0-15, or a string containing a single character be either an integer with values 0-15, or a string containing a single character


+ 77
- 47
daemon/call_interfaces.c View File

@ -1504,6 +1504,9 @@ static const char *media_block_match(struct call **call, struct call_monologue *
return "Unknown call-id"; return "Unknown call-id";
// directional? // directional?
if (flags->all) // explicitly non-directional, so skip the rest
return NULL;
if (flags->label.s) { if (flags->label.s) {
*monologue = g_hash_table_lookup((*call)->labels, &flags->label); *monologue = g_hash_table_lookup((*call)->labels, &flags->label);
if (!*monologue) if (!*monologue)
@ -1749,14 +1752,23 @@ out:
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
static const char *play_media_select_party(struct call **call, struct call_monologue **monologue,
static const char *play_media_select_party(struct call **call, GQueue *monologues,
bencode_item_t *input) bencode_item_t *input)
{ {
const char *err = media_block_match(call, monologue, NULL, input);
struct call_monologue *monologue;
struct sdp_ng_flags flags;
g_queue_init(monologues);
const char *err = media_block_match(call, &monologue, &flags, input);
if (err) if (err)
return err; return err;
if (!*monologue)
if (flags.all)
g_queue_append(monologues, &(*call)->monologues);
else if (!monologue)
return "No participant party specified"; return "No participant party specified";
else
g_queue_push_tail(monologues, monologue);
return NULL; return NULL;
} }
#endif #endif
@ -1766,42 +1778,47 @@ const char *call_play_media_ng(bencode_item_t *input, bencode_item_t *output) {
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
str str; str str;
struct call *call; struct call *call;
struct call_monologue *monologue;
GQueue monologues;
const char *err = NULL; const char *err = NULL;
long long db_id; long long db_id;
err = play_media_select_party(&call, &monologue, input);
err = play_media_select_party(&call, &monologues, input);
if (err) if (err)
goto out; goto out;
if (!monologue->player)
monologue->player = media_player_new(monologue);
for (GList *l = monologues.head; l; l = l->next) {
struct call_monologue *monologue = l->data;
err = "No media file specified";
if (bencode_dictionary_get_str(input, "file", &str)) {
err = "Failed to start media playback from file";
if (media_player_play_file(monologue->player, &str))
goto out;
}
else if (bencode_dictionary_get_str(input, "blob", &str)) {
err = "Failed to start media playback from blob";
if (media_player_play_blob(monologue->player, &str))
goto out;
}
else if ((db_id = bencode_dictionary_get_int_str(input, "db-id", 0)) > 0) {
err = "Failed to start media playback from database";
if (media_player_play_db(monologue->player, db_id))
if (!monologue->player)
monologue->player = media_player_new(monologue);
err = "No media file specified";
if (bencode_dictionary_get_str(input, "file", &str)) {
err = "Failed to start media playback from file";
if (media_player_play_file(monologue->player, &str))
goto out;
}
else if (bencode_dictionary_get_str(input, "blob", &str)) {
err = "Failed to start media playback from blob";
if (media_player_play_blob(monologue->player, &str))
goto out;
}
else if ((db_id = bencode_dictionary_get_int_str(input, "db-id", 0)) > 0) {
err = "Failed to start media playback from database";
if (media_player_play_db(monologue->player, db_id))
goto out;
}
else
goto out; goto out;
}
else
goto out;
if (monologue->player->duration)
bencode_dictionary_add_integer(output, "duration", monologue->player->duration);
if (l == monologues.head && monologue->player->duration)
bencode_dictionary_add_integer(output, "duration", monologue->player->duration);
}
err = NULL; err = NULL;
out: out:
g_queue_clear(&monologues);
if (call) { if (call) {
rwlock_unlock_w(&call->master_lock); rwlock_unlock_w(&call->master_lock);
obj_put(call); obj_put(call);
@ -1816,21 +1833,27 @@ out:
const char *call_stop_media_ng(bencode_item_t *input, bencode_item_t *output) { const char *call_stop_media_ng(bencode_item_t *input, bencode_item_t *output) {
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
struct call *call; struct call *call;
struct call_monologue *monologue;
GQueue monologues;
const char *err = NULL; const char *err = NULL;
err = play_media_select_party(&call, &monologue, input);
err = play_media_select_party(&call, &monologues, input);
if (err) if (err)
goto out; goto out;
if (!monologue->player)
return "Not currently playing media";
for (GList *l = monologues.head; l; l = l->next) {
struct call_monologue *monologue = l->data;
media_player_stop(monologue->player);
err = "Not currently playing media";
if (!monologue->player)
goto out;
media_player_stop(monologue->player);
}
err = NULL; err = NULL;
out: out:
g_queue_clear(&monologues);
if (call) { if (call) {
rwlock_unlock_w(&call->master_lock); rwlock_unlock_w(&call->master_lock);
obj_put(call); obj_put(call);
@ -1845,11 +1868,11 @@ out:
const char *call_play_dtmf_ng(bencode_item_t *input, bencode_item_t *output) { const char *call_play_dtmf_ng(bencode_item_t *input, bencode_item_t *output) {
#ifdef WITH_TRANSCODING #ifdef WITH_TRANSCODING
struct call *call; struct call *call;
struct call_monologue *monologue;
GQueue monologues;
str str; str str;
const char *err = NULL; const char *err = NULL;
err = play_media_select_party(&call, &monologue, input);
err = play_media_select_party(&call, &monologues, input);
if (err) if (err)
goto out; goto out;
@ -1899,25 +1922,32 @@ const char *call_play_dtmf_ng(bencode_item_t *input, bencode_item_t *output) {
if (volume > 0) if (volume > 0)
volume *= -1; volume *= -1;
// find a usable output media
struct call_media *media;
for (GList *l = monologue->medias.head; l; l = l->next) {
media = l->data;
if (media->type_id != MT_AUDIO)
continue;
if (!media->dtmf_injector)
continue;
goto found;
}
for (GList *l = monologues.head; l; l = l->next) {
struct call_monologue *monologue = l->data;
err = "Monologue has no media capable of DTMF injection";
// XXX fall back to generating a secondary stream
goto out;
// find a usable output media
struct call_media *media;
for (GList *l = monologue->medias.head; l; l = l->next) {
media = l->data;
if (media->type_id != MT_AUDIO)
continue;
if (!media->dtmf_injector)
continue;
goto found;
}
err = "Monologue has no media capable of DTMF injection";
// XXX fall back to generating a secondary stream
goto out;
found:; found:;
err = dtmf_inject(media, code, volume, duration, pause);
err = dtmf_inject(media, code, volume, duration, pause);
if (err)
break;
}
out: out:
g_queue_clear(&monologues);
if (call) { if (call) {
rwlock_unlock_w(&call->master_lock); rwlock_unlock_w(&call->master_lock);
obj_put(call); obj_put(call);


+ 2
- 1
utils/rtpengine-ng-client View File

@ -71,13 +71,14 @@ GetOptions(
'blob=s' => \$options{'blob'}, 'blob=s' => \$options{'blob'},
'blob-file=s' => \$options{'blob-file'}, 'blob-file=s' => \$options{'blob-file'},
'db-id=i' => \$options{'db-id'}, 'db-id=i' => \$options{'db-id'},
'code=s' => \$options{'code'},
) or die; ) or die;
my $cmd = shift(@ARGV) or die; my $cmd = shift(@ARGV) or die;
my %packet = (command => $cmd); my %packet = (command => $cmd);
for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id')) {
for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code')) {
defined($options{$x}) and $packet{$x} = \$options{$x}; defined($options{$x}) and $packet{$x} = \$options{$x};
} }
for my $x (split(/,/, 'TOS,delete-delay')) { for my $x (split(/,/, 'TOS,delete-delay')) {


Loading…
Cancel
Save