summaryrefslogtreecommitdiff
path: root/res/res_stasis_playback.c
diff options
context:
space:
mode:
authorDavid M. Lee <dlee@digium.com>2013-08-13 15:27:32 +0000
committerDavid M. Lee <dlee@digium.com>2013-08-13 15:27:32 +0000
commit987fdfb444aef3eb60041887db60b54e4e382293 (patch)
tree944be36f63794903977a210f530845d230a23f05 /res/res_stasis_playback.c
parent94ee8f2e33f6d6e80c3fc6d64d04883fec9496f1 (diff)
ARI: allow other operations to happen while bridged
This patch changes ARI bridging to allow other channel operations to happen while the channel is bridged. ARI channel operations are designed to queue up and execute sequentially. This meant, though, that while a channel was bridged, any other channel operations would queue up and execute only after the channel left the bridge. This patch changes ARI bridging so that channel commands can execute while the channel is bridged. For most operations, things simply work as expected. The one thing that ended up being a bit odd is recording. The current recording implementation will fail when one attempts to record a channel that's in a bridge. Note that the bridge itself may be recording; it's recording a specific channel in the bridge that fails. While this is an annoying limitation, channel recording is still very useful for use cases such as voice mail, and bridge recording makes up much of the difference for other use cases. (closes issue ASTERISK-22084) Review: https://reviewboard.asterisk.org/r/2726/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396568 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_stasis_playback.c')
-rw-r--r--res/res_stasis_playback.c177
1 files changed, 142 insertions, 35 deletions
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index 483aff8c2..83730491c 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -34,6 +34,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/astobj2.h"
+#include "asterisk/bridge.h"
+#include "asterisk/bridge_internal.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
@@ -73,12 +75,56 @@ struct stasis_app_playback {
/*! Number of milliseconds to skip for forward/reverse operations */
int skipms;
+ /*! Set when playback has been completed */
+ int done;
+ /*! Condition for waiting on done to be set */
+ ast_cond_t done_cond;
/*! Number of milliseconds of media that has been played */
long playedms;
/*! Current playback state */
enum stasis_app_playback_state state;
};
+static void playback_dtor(void *obj)
+{
+ struct stasis_app_playback *playback = obj;
+
+ ast_string_field_free_memory(playback);
+ ast_cond_destroy(&playback->done_cond);
+}
+
+static struct stasis_app_playback *playback_create(
+ struct stasis_app_control *control)
+{
+ RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
+ char id[AST_UUID_STR_LEN];
+ int res;
+
+ if (!control) {
+ return NULL;
+ }
+
+ playback = ao2_alloc(sizeof(*playback), playback_dtor);
+ if (!playback || ast_string_field_init(playback, 128)) {
+ return NULL;
+ }
+
+ res = ast_cond_init(&playback->done_cond, NULL);
+ if (res != 0) {
+ ast_log(LOG_ERROR, "Error creating done condition: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ ast_uuid_generate_str(id, sizeof(id));
+ ast_string_field_set(playback, id, id);
+
+ playback->control = control;
+
+ ao2_ref(playback, +1);
+ return playback;
+}
+
static int playback_hash(const void *obj, int flags)
{
const struct stasis_app_playback *playback = obj;
@@ -144,12 +190,6 @@ static void playback_publish(struct stasis_app_playback *playback)
stasis_app_control_publish(playback->control, message);
}
-static void playback_cleanup(struct stasis_app_playback *playback)
-{
- ao2_unlink_flags(playbacks, playback,
- OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
-}
-
static int playback_first_update(struct stasis_app_playback *playback,
const char *uniqueid)
{
@@ -191,11 +231,21 @@ static void playback_final_update(struct stasis_app_playback *playback,
playback_publish(playback);
}
-static void *play_uri(struct stasis_app_control *control,
- struct ast_channel *chan, void *data)
+/*!
+ * \brief RAII_VAR function to mark a playback as done when leaving scope.
+ */
+static void mark_as_done(struct stasis_app_playback *playback)
{
- RAII_VAR(struct stasis_app_playback *, playback, NULL,
- playback_cleanup);
+ SCOPED_AO2LOCK(lock, playback);
+ playback->done = 1;
+ ast_cond_broadcast(&playback->done_cond);
+}
+
+static void play_on_channel(struct stasis_app_playback *playback,
+ struct ast_channel *chan)
+{
+ RAII_VAR(struct stasis_app_playback *, mark_when_done, playback,
+ mark_as_done);
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
RAII_VAR(char *, file, NULL, ast_free);
int res;
@@ -210,7 +260,6 @@ static void *play_uri(struct stasis_app_control *control,
const char *pause = NULL;
const char *restart = NULL;
- playback = data;
ast_assert(playback != NULL);
offsetms = playback->offsetms;
@@ -218,7 +267,7 @@ static void *play_uri(struct stasis_app_control *control,
res = playback_first_update(playback, ast_channel_uniqueid(chan));
if (res != 0) {
- return NULL;
+ return;
}
if (ast_channel_state(chan) != AST_STATE_UP) {
@@ -241,11 +290,11 @@ static void *play_uri(struct stasis_app_control *control,
} else {
/* Play URL */
ast_log(LOG_ERROR, "Unimplemented\n");
- return NULL;
+ return;
}
if (!file) {
- return NULL;
+ return;
}
res = ast_control_streamfile_lang(chan, file, fwd, rev, stop, pause,
@@ -254,14 +303,87 @@ static void *play_uri(struct stasis_app_control *control,
playback_final_update(playback, offsetms, res,
ast_channel_uniqueid(chan));
- return NULL;
+ return;
}
-static void playback_dtor(void *obj)
+/*!
+ * \brief Special case code to play while a channel is in a bridge.
+ *
+ * \param bridge_channel The channel's bridge_channel.
+ * \param playback_id Id of the playback to start.
+ */
+static void play_on_channel_in_bridge(struct ast_bridge_channel *bridge_channel,
+ const char *playback_id)
{
- struct stasis_app_playback *playback = obj;
+ RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
- ast_string_field_free_memory(playback);
+ playback = stasis_app_playback_find_by_id(playback_id);
+ if (!playback) {
+ ast_log(LOG_ERROR, "Couldn't find playback %s\n",
+ playback_id);
+ return;
+ }
+
+ play_on_channel(playback, bridge_channel->chan);
+}
+
+/*!
+ * \brief \ref RAII_VAR function to remove a playback from the global list when
+ * leaving scope.
+ */
+static void remove_from_playbacks(struct stasis_app_playback *playback)
+{
+ ao2_unlink_flags(playbacks, playback,
+ OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+}
+
+static void *play_uri(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ RAII_VAR(struct stasis_app_playback *, playback, NULL,
+ remove_from_playbacks);
+ struct ast_bridge *bridge;
+ int res;
+
+ playback = data;
+
+ if (!control) {
+ return NULL;
+ }
+
+ bridge = stasis_app_get_bridge(control);
+ if (bridge) {
+ struct ast_bridge_channel *bridge_chan;
+
+ /* Queue up playback on the bridge */
+ ast_bridge_lock(bridge);
+ bridge_chan = bridge_find_channel(bridge, chan);
+ if (bridge_chan) {
+ ast_bridge_channel_queue_playfile(
+ bridge_chan,
+ play_on_channel_in_bridge,
+ playback->id,
+ NULL); /* moh_class */
+ }
+ ast_bridge_unlock(bridge);
+
+ /* Wait for playback to complete */
+ ao2_lock(playback);
+ while (!playback->done) {
+ res = ast_cond_wait(&playback->done_cond,
+ ao2_object_get_lockaddr(playback));
+ if (res != 0) {
+ ast_log(LOG_ERROR,
+ "Error waiting for playback to complete: %s\n",
+ strerror(errno));
+ }
+ }
+ ao2_unlock(playback);
+ } else {
+ play_on_channel(playback, chan);
+ }
+
+ return NULL;
}
static void set_target_uri(
@@ -291,7 +413,6 @@ struct stasis_app_playback *stasis_app_control_play_uri(
int skipms, long offsetms)
{
RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
- char id[AST_UUID_STR_LEN];
if (skipms < 0 || offsetms < 0) {
return NULL;
@@ -300,21 +421,15 @@ struct stasis_app_playback *stasis_app_control_play_uri(
ast_debug(3, "%s: Sending play(%s) command\n",
stasis_app_control_get_channel_id(control), uri);
- playback = ao2_alloc(sizeof(*playback), playback_dtor);
- if (!playback || ast_string_field_init(playback, 128)) {
- return NULL;
- }
+ playback = playback_create(control);
if (skipms == 0) {
skipms = PLAYBACK_DEFAULT_SKIPMS;
}
- ast_uuid_generate_str(id, sizeof(id));
- ast_string_field_set(playback, id, id);
ast_string_field_set(playback, media, uri);
ast_string_field_set(playback, language, language);
set_target_uri(playback, target_type, target_id);
- playback->control = control;
playback->skipms = skipms;
playback->offsetms = offsetms;
ao2_link(playbacks, playback);
@@ -346,15 +461,7 @@ const char *stasis_app_playback_get_id(
struct stasis_app_playback *stasis_app_playback_find_by_id(const char *id)
{
- RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
-
- playback = ao2_find(playbacks, id, OBJ_KEY);
- if (playback == NULL) {
- return NULL;
- }
-
- ao2_ref(playback, +1);
- return playback;
+ return ao2_find(playbacks, id, OBJ_KEY);
}
struct ast_json *stasis_app_playback_to_json(