aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfile <file@f38db490-d61c-443f-a65b-d21fe96a405b>2006-09-03 23:30:37 +0000
committerfile <file@f38db490-d61c-443f-a65b-d21fe96a405b>2006-09-03 23:30:37 +0000
commitc345657f48f506680d121ac479fe46af663d2ba3 (patch)
treeb469e85436e1552e41576dfd5a3045a35456e1bc
parent96aba6e67cfa62a6b6052d74e154f02434ecf929 (diff)
Make the difference clear about what the responsibilities of the core and a spy are when it comes to spying on a channel. The core is responsible for adding a spy to a channel, feeding frames into the spy, removing the spy from the channel, and notifying the spy that is has been detached. The spy is responsible for reading frames in, and cleaning itself up. Each side will not try to do the other's job.
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@41959 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--apps/app_chanspy.c31
-rw-r--r--apps/app_mixmonitor.c28
-rw-r--r--include/asterisk/chanspy.h9
-rw-r--r--main/channel.c109
4 files changed, 94 insertions, 83 deletions
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index f433c0d1a..56052a4a4 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -211,21 +211,6 @@ static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, s
return res;
}
-static void stop_spying(struct ast_channel_spy *spy)
-{
- /* If our status has changed to DONE, then the channel we're spying on is gone....
- DON'T TOUCH IT!!! RUN AWAY!!! */
- if (spy->status == CHANSPY_DONE)
- return;
-
- if (!spy->chan)
- return;
-
- ast_channel_lock(spy->chan);
- ast_channel_spy_remove(spy->chan, spy);
- ast_channel_unlock(spy->chan);
-};
-
/* Map 'volume' levels from -4 through +4 into
decibel (dB) settings for channel drivers
*/
@@ -327,10 +312,11 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
*/
while ((res = ast_waitfor(chan, -1) > -1) &&
csth.spy.status == CHANSPY_RUNNING &&
- !ast_check_hangup(chan) &&
csth.spy.chan) {
- if (!(f = ast_read(chan)))
+ if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
+ running = -1;
break;
+ }
if (ast_test_flag(flags, OPTION_WHISPER) &&
(f->frametype == AST_FRAME_VOICE)) {
@@ -381,13 +367,16 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
else
ast_deactivate_generator(chan);
- stop_spying(&csth.spy);
+ /* If a channel still exists on our spy structure then we need to remove ourselves */
+ if (csth.spy.chan) {
+ csth.spy.status = CHANSPY_DONE;
+ ast_channel_spy_remove(csth.spy.chan, &csth.spy);
+ }
+ ast_channel_spy_free(&csth.spy);
if (option_verbose >= 2)
ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
- ast_mutex_destroy(&csth.spy.lock);
-
return running;
}
@@ -532,6 +521,8 @@ static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
peer = NULL;
}
}
+ if (res == -1)
+ break;
}
ast_clear_flag(chan, AST_FLAG_SPYING);
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index aa7602f65..5038776a3 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -122,23 +122,6 @@ AST_APP_OPTIONS(mixmonitor_opts, {
AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
});
-static void stopmon(struct ast_channel_spy *spy)
-{
- struct ast_channel *chan = spy->chan;
-
- /* If our status has changed to DONE, then the channel we're spying on is gone....
- DON'T TOUCH IT!!! RUN AWAY!!! */
- if (spy->status == CHANSPY_DONE)
- return;
-
- if (!chan)
- return;
-
- ast_channel_lock(chan);
- ast_channel_spy_remove(chan, spy);
- ast_channel_unlock(chan);
-}
-
static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
{
struct ast_channel *peer;
@@ -176,9 +159,8 @@ static void *mixmonitor_thread(void *obj)
ast_channel_spy_trigger_wait(&mixmonitor->spy);
- if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING) {
+ if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
break;
- }
while (1) {
if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
@@ -194,15 +176,15 @@ static void *mixmonitor_thread(void *obj)
next = AST_LIST_NEXT(f, frame_list);
if (write)
ast_writestream(mixmonitor->fs, f);
- ast_frfree(f);
+ ast_frame_free(f, 0);
}
}
}
ast_mutex_unlock(&mixmonitor->spy.lock);
-
- stopmon(&mixmonitor->spy);
+ ast_channel_spy_free(&mixmonitor->spy);
+
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
@@ -211,8 +193,6 @@ static void *mixmonitor_thread(void *obj)
ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
ast_safe_system(mixmonitor->post_process);
}
-
- ast_mutex_destroy(&mixmonitor->spy.lock);
ast_closestream(mixmonitor->fs);
diff --git a/include/asterisk/chanspy.h b/include/asterisk/chanspy.h
index dd8d79edf..8550210d0 100644
--- a/include/asterisk/chanspy.h
+++ b/include/asterisk/chanspy.h
@@ -95,6 +95,15 @@ int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy);
void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy);
/*!
+ \brief Free a spy.
+ \param spy The spy to free
+ \return nothing
+
+ Note: This function MUST NOT be called with the spy locked.
+*/
+void ast_channel_spy_free(struct ast_channel_spy *spy);
+
+/*!
\brief Find all spies of a particular type on a channel and stop them.
\param chan The channel to operate on
\param type A character string identifying the type of spies to be stopped
diff --git a/main/channel.c b/main/channel.c
index 18e8baa23..dec1f86ba 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1203,22 +1203,61 @@ int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
return 0;
}
+/* Clean up a channel's spy information */
+static void spy_cleanup(struct ast_channel *chan)
+{
+ if (!AST_LIST_EMPTY(&chan->spies->list))
+ return;
+ if (chan->spies->read_translator.path)
+ ast_translator_free_path(chan->spies->read_translator.path);
+ if (chan->spies->write_translator.path)
+ ast_translator_free_path(chan->spies->write_translator.path);
+ free(chan->spies);
+ chan->spies = NULL;
+ return;
+}
+
+/* Detach a spy from it's channel */
+static void spy_detach(struct ast_channel_spy *spy, struct ast_channel *chan)
+{
+ ast_mutex_lock(&spy->lock);
+
+ /* We only need to poke them if they aren't already done */
+ if (spy->status != CHANSPY_DONE) {
+ /* Indicate to the spy to stop */
+ spy->status = CHANSPY_STOP;
+ spy->chan = NULL;
+ /* Poke the spy if needed */
+ if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
+ ast_cond_signal(&spy->trigger);
+ }
+
+ /* Print it out while we still have a lock so the structure can't go away (if signalled above) */
+ ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n", spy->type, chan->name);
+
+ ast_mutex_unlock(&spy->lock);
+
+ return;
+}
+
void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
{
- struct ast_channel_spy *spy;
+ struct ast_channel_spy *spy = NULL;
if (!chan->spies)
return;
- AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
ast_mutex_lock(&spy->lock);
if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
- spy->status = CHANSPY_STOP;
- if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
- ast_cond_signal(&spy->trigger);
- }
- ast_mutex_unlock(&spy->lock);
+ ast_mutex_unlock(&spy->lock);
+ AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
+ spy_detach(spy, chan);
+ } else
+ ast_mutex_unlock(&spy->lock);
}
+ AST_LIST_TRAVERSE_SAFE_END
+ spy_cleanup(chan);
}
void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
@@ -1235,62 +1274,54 @@ void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
{
- struct ast_frame *f;
-
if (!chan->spies)
return;
AST_LIST_REMOVE(&chan->spies->list, spy, list);
+ spy_detach(spy, chan);
+ spy_cleanup(chan);
+}
- ast_mutex_lock(&spy->lock);
+void ast_channel_spy_free(struct ast_channel_spy *spy)
+{
+ struct ast_frame *f = NULL;
+
+ if (spy->status == CHANSPY_DONE)
+ return;
- spy->chan = NULL;
+ /* Switch status to done in case we get called twice */
+ spy->status = CHANSPY_DONE;
- while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
- ast_frfree(f);
-
+ /* Drop any frames in the queue */
while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
ast_frfree(f);
+ while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
+ ast_frfree(f);
+ /* Destroy the condition if in use */
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_destroy(&spy->trigger);
- ast_mutex_unlock(&spy->lock);
+ /* Destroy our mutex since it is no longer in use */
+ ast_mutex_destroy(&spy->lock);
- ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n",
- spy->type, chan->name);
-
- if (AST_LIST_EMPTY(&chan->spies->list)) {
- if (chan->spies->read_translator.path)
- ast_translator_free_path(chan->spies->read_translator.path);
- if (chan->spies->write_translator.path)
- ast_translator_free_path(chan->spies->write_translator.path);
- free(chan->spies);
- chan->spies = NULL;
- }
+ return;
}
static void detach_spies(struct ast_channel *chan)
{
- struct ast_channel_spy *spy;
+ struct ast_channel_spy *spy = NULL;
if (!chan->spies)
return;
- /* Marking the spies as done is sufficient. Chanspy or spy users will get the picture. */
- AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
- ast_mutex_lock(&spy->lock);
- spy->chan = NULL;
- if (spy->status == CHANSPY_RUNNING)
- spy->status = CHANSPY_DONE;
- if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
- ast_cond_signal(&spy->trigger);
- ast_mutex_unlock(&spy->lock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
+ AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
+ spy_detach(spy, chan);
}
+ AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list)
- ast_channel_spy_remove(chan, spy);
- AST_LIST_TRAVERSE_SAFE_END;
+ spy_cleanup(chan);
}
/*! \brief Softly hangup a channel, don't lock */