Fri Sep 25 19:28:33 2009

Asterisk developer's documentation


chanspy.h File Reference

Asterisk PBX channel spy definitions. More...

#include "asterisk/linkedlists.h"

Include dependency graph for chanspy.h:

This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ast_channel_spy
struct  ast_channel_spy_queue

Enumerations

enum  chanspy_flags {
  CHANSPY_MIXAUDIO = (1 << 0), CHANSPY_READ_VOLADJUST = (1 << 1), CHANSPY_WRITE_VOLADJUST = (1 << 2), CHANSPY_FORMAT_AUDIO = (1 << 3),
  CHANSPY_TRIGGER_MODE = (3 << 4), CHANSPY_TRIGGER_READ = (1 << 4), CHANSPY_TRIGGER_WRITE = (2 << 4), CHANSPY_TRIGGER_NONE = (3 << 4),
  CHANSPY_TRIGGER_FLUSH = (1 << 6)
}
enum  chanspy_states { CHANSPY_NEW = 0, CHANSPY_RUNNING = 1, CHANSPY_DONE = 2, CHANSPY_STOP = 3 }

Functions

int ast_channel_spy_add (struct ast_channel *chan, struct ast_channel_spy *spy)
 Adds a spy to a channel, to begin receiving copies of the channel's audio frames.
void ast_channel_spy_free (struct ast_channel_spy *spy)
 Free a spy.
struct ast_frameast_channel_spy_read_frame (struct ast_channel_spy *spy, unsigned int samples)
 Read one (or more) frames of audio from a channel being spied upon.
void ast_channel_spy_remove (struct ast_channel *chan, struct ast_channel_spy *spy)
 Remove a spy from a channel.
void ast_channel_spy_stop_by_type (struct ast_channel *chan, const char *type)
 Find all spies of a particular type on a channel and stop them.
void ast_channel_spy_trigger_wait (struct ast_channel_spy *spy)
 Efficiently wait until audio is available for a spy, or an exception occurs.


Detailed Description

Asterisk PBX channel spy definitions.

Definition in file chanspy.h.


Enumeration Type Documentation

Enumerator:
CHANSPY_MIXAUDIO 
CHANSPY_READ_VOLADJUST 
CHANSPY_WRITE_VOLADJUST 
CHANSPY_FORMAT_AUDIO 
CHANSPY_TRIGGER_MODE 
CHANSPY_TRIGGER_READ 
CHANSPY_TRIGGER_WRITE 
CHANSPY_TRIGGER_NONE 
CHANSPY_TRIGGER_FLUSH 

Definition at line 39 of file chanspy.h.

00039                    {
00040    CHANSPY_MIXAUDIO = (1 << 0),
00041    CHANSPY_READ_VOLADJUST = (1 << 1),
00042    CHANSPY_WRITE_VOLADJUST = (1 << 2),
00043    CHANSPY_FORMAT_AUDIO = (1 << 3),
00044    CHANSPY_TRIGGER_MODE = (3 << 4),
00045    CHANSPY_TRIGGER_READ = (1 << 4),
00046    CHANSPY_TRIGGER_WRITE = (2 << 4),
00047    CHANSPY_TRIGGER_NONE = (3 << 4),
00048    CHANSPY_TRIGGER_FLUSH = (1 << 6),
00049 };

Enumerator:
CHANSPY_NEW  spy not yet operating
CHANSPY_RUNNING  normal operation, spy is still operating
CHANSPY_DONE  spy is stopped and already removed from channel
CHANSPY_STOP  spy requested to stop, still attached to channel

Definition at line 32 of file chanspy.h.

00032                     {
00033    CHANSPY_NEW = 0,     /*!< spy not yet operating */
00034    CHANSPY_RUNNING = 1,    /*!< normal operation, spy is still operating */
00035    CHANSPY_DONE = 2,    /*!< spy is stopped and already removed from channel */
00036    CHANSPY_STOP = 3,    /*!< spy requested to stop, still attached to channel */
00037 };


Function Documentation

int ast_channel_spy_add ( struct ast_channel chan,
struct ast_channel_spy spy 
)

Adds a spy to a channel, to begin receiving copies of the channel's audio frames.

Parameters:
chan The channel to add the spy to.
spy A pointer to ast_channel_spy structure describing how the spy is to be used.
Returns:
0 for success, non-zero for failure
Note: This function performs no locking; you must hold the channel's lock before calling this function.

Definition at line 1415 of file channel.c.

References ast_calloc, ast_clear_flag, ast_cond_init(), AST_FORMAT_SLINEAR, ast_getformatname(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_HEAD, AST_LIST_INSERT_TAIL, ast_log(), ast_set_flag, ast_test_flag, ast_channel_spy::chan, CHANSPY_FORMAT_AUDIO, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_TRIGGER_MODE, CHANSPY_TRIGGER_NONE, CHANSPY_TRIGGER_READ, CHANSPY_TRIGGER_WRITE, CHANSPY_WRITE_VOLADJUST, ast_channel_spy_queue::format, LOG_DEBUG, LOG_WARNING, option_debug, ast_channel_spy::read_queue, ast_channel::spies, ast_channel_spy::trigger, ast_channel_spy::type, and ast_channel_spy::write_queue.

Referenced by start_spying(), and startmon().

01416 {
01417    /* Link the owner channel to the spy */
01418    spy->chan = chan;
01419 
01420    if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
01421       ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
01422          spy->type, chan->name);
01423       return -1;
01424    }
01425 
01426    if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
01427       ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
01428          ast_getformatname(spy->read_queue.format));
01429       return -1;
01430    }
01431 
01432    if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
01433       ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
01434          ast_getformatname(spy->write_queue.format));
01435       return -1;
01436    }
01437 
01438    if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
01439        ((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
01440         (spy->write_queue.format != AST_FORMAT_SLINEAR))) {
01441       ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
01442          ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
01443       return -1;
01444    }
01445 
01446    if (!chan->spies) {
01447       if (!(chan->spies = ast_calloc(1, sizeof(*chan->spies)))) {
01448          return -1;
01449       }
01450 
01451       AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
01452       AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
01453    } else {
01454       AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
01455    }
01456 
01457    if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
01458       ast_cond_init(&spy->trigger, NULL);
01459       ast_set_flag(spy, CHANSPY_TRIGGER_READ);
01460       ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
01461    }
01462 
01463    if (option_debug)
01464       ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
01465          spy->type, chan->name);
01466 
01467    return 0;
01468 }

void ast_channel_spy_free ( struct ast_channel_spy spy  ) 

Free a spy.

Parameters:
spy The spy to free
Returns:
nothing
Note: This function MUST NOT be called with the spy locked.

Definition at line 1543 of file channel.c.

References ast_cond_destroy(), ast_frfree, AST_LIST_REMOVE_HEAD, ast_mutex_destroy(), ast_test_flag, CHANSPY_DONE, CHANSPY_TRIGGER_MODE, CHANSPY_TRIGGER_NONE, f, ast_channel_spy::lock, ast_channel_spy::read_queue, ast_channel_spy::status, ast_channel_spy::trigger, and ast_channel_spy::write_queue.

Referenced by channel_spy(), and mixmonitor_thread().

01544 {
01545    struct ast_frame *f = NULL;
01546 
01547    if (spy->status == CHANSPY_DONE)
01548       return;
01549 
01550    /* Switch status to done in case we get called twice */
01551    spy->status = CHANSPY_DONE;
01552 
01553    /* Drop any frames in the queue */
01554    while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
01555       ast_frfree(f);
01556    while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
01557       ast_frfree(f);
01558 
01559    /* Destroy the condition if in use */
01560    if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
01561       ast_cond_destroy(&spy->trigger);
01562 
01563    /* Destroy our mutex since it is no longer in use */
01564    ast_mutex_destroy(&spy->lock);
01565 
01566    return;
01567 }

struct ast_frame* ast_channel_spy_read_frame ( struct ast_channel_spy spy,
unsigned int  samples 
) [read]

Read one (or more) frames of audio from a channel being spied upon.

Parameters:
spy The spy to operate on
samples The number of audio samples to read
Returns:
NULL for failure, one ast_frame pointer, or a chain of ast_frame pointers
This function can return multiple frames if the spy structure needs to be 'flushed' due to mismatched queue lengths, or if the spy structure is configured to return unmixed audio (in which case each call to this function will return a frame of audio from each side of channel).

Note: This function performs no locking; you must hold the spy's lock before calling this function. You must not hold the channel's lock at the same time.

Definition at line 4771 of file channel.c.

References ast_clear_flag, ast_codec_get_len(), ast_frame_adjust_volume(), ast_frame_slinear_sum(), AST_FRAME_VOICE, ast_frdup(), ast_frfree, AST_LIST_FIRST, AST_LIST_HEAD_SET_NOLOCK, AST_LIST_NEXT, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_test_flag, CHANSPY_MIXAUDIO, CHANSPY_READ_VOLADJUST, CHANSPY_TRIGGER_FLUSH, CHANSPY_WRITE_VOLADJUST, copy_data_from_queue(), ast_channel_spy_queue::format, ast_frame::frametype, ast_channel_spy::read_queue, ast_channel_spy::read_vol_adjustment, ast_channel_spy_queue::samples, ast_channel_spy::write_queue, and ast_channel_spy::write_vol_adjustment.

Referenced by mixmonitor_thread(), and spy_generate().

04772 {
04773    struct ast_frame *result;
04774    /* buffers are allocated to hold SLINEAR, which is the largest format */
04775         short read_buf[samples];
04776         short write_buf[samples];
04777    struct ast_frame *read_frame;
04778    struct ast_frame *write_frame;
04779    int need_dup;
04780    struct ast_frame stack_read_frame = { .frametype = AST_FRAME_VOICE,
04781                      .subclass = spy->read_queue.format,
04782                      .data = read_buf,
04783                      .samples = samples,
04784                      .datalen = ast_codec_get_len(spy->read_queue.format, samples),
04785    };
04786    struct ast_frame stack_write_frame = { .frametype = AST_FRAME_VOICE,
04787                       .subclass = spy->write_queue.format,
04788                       .data = write_buf,
04789                       .samples = samples,
04790                       .datalen = ast_codec_get_len(spy->write_queue.format, samples),
04791    };
04792 
04793    /* if a flush has been requested, dump everything in whichever queue is larger */
04794    if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
04795       if (spy->read_queue.samples > spy->write_queue.samples) {
04796          if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
04797             AST_LIST_TRAVERSE(&spy->read_queue.list, result, frame_list)
04798                ast_frame_adjust_volume(result, spy->read_vol_adjustment);
04799          }
04800          result = AST_LIST_FIRST(&spy->read_queue.list);
04801          AST_LIST_HEAD_SET_NOLOCK(&spy->read_queue.list, NULL);
04802          spy->read_queue.samples = 0;
04803       } else {
04804          if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
04805             AST_LIST_TRAVERSE(&spy->write_queue.list, result, frame_list)
04806                ast_frame_adjust_volume(result, spy->write_vol_adjustment);
04807          }
04808          result = AST_LIST_FIRST(&spy->write_queue.list);
04809          AST_LIST_HEAD_SET_NOLOCK(&spy->write_queue.list, NULL);
04810          spy->write_queue.samples = 0;
04811       }
04812       ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
04813       return result;
04814    }
04815 
04816    if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
04817       return NULL;
04818 
04819    /* short-circuit if both head frames have exactly what we want */
04820    if ((AST_LIST_FIRST(&spy->read_queue.list)->samples == samples) &&
04821        (AST_LIST_FIRST(&spy->write_queue.list)->samples == samples)) {
04822       read_frame = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list);
04823       write_frame = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list);
04824 
04825       spy->read_queue.samples -= samples;
04826       spy->write_queue.samples -= samples;
04827 
04828       need_dup = 0;
04829    } else {
04830       copy_data_from_queue(&spy->read_queue, read_buf, samples);
04831       copy_data_from_queue(&spy->write_queue, write_buf, samples);
04832 
04833       read_frame = &stack_read_frame;
04834       write_frame = &stack_write_frame;
04835       need_dup = 1;
04836    }
04837    
04838    if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
04839       ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
04840 
04841    if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
04842       ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
04843 
04844    if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
04845       ast_frame_slinear_sum(read_frame, write_frame);
04846 
04847       if (need_dup)
04848          result = ast_frdup(read_frame);
04849       else {
04850          result = read_frame;
04851          ast_frfree(write_frame);
04852       }
04853    } else {
04854       if (need_dup) {
04855          result = ast_frdup(read_frame);
04856          AST_LIST_NEXT(result, frame_list) = ast_frdup(write_frame);
04857       } else {
04858          result = read_frame;
04859          AST_LIST_NEXT(result, frame_list) = write_frame;
04860       }
04861    }
04862 
04863    return result;
04864 }

void ast_channel_spy_remove ( struct ast_channel chan,
struct ast_channel_spy spy 
)

Remove a spy from a channel.

Parameters:
chan The channel to remove the spy from
spy The spy to be removed
Returns:
nothing
Note: This function performs no locking; you must hold the channel's lock before calling this function.

Definition at line 1533 of file channel.c.

References AST_LIST_REMOVE, ast_channel::spies, spy_cleanup(), and spy_detach().

Referenced by channel_spy().

01534 {
01535    if (!chan->spies)
01536       return;
01537 
01538    AST_LIST_REMOVE(&chan->spies->list, spy, list);
01539    spy_detach(spy, chan);
01540    spy_cleanup(chan);
01541 }

void ast_channel_spy_stop_by_type ( struct ast_channel chan,
const char *  type 
)

Find all spies of a particular type on a channel and stop them.

Parameters:
chan The channel to operate on
type A character string identifying the type of spies to be stopped
Returns:
nothing
Note: This function performs no locking; you must hold the channel's lock before calling this function.

Definition at line 1504 of file channel.c.

References AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, CHANSPY_RUNNING, ast_channel::spies, spy_cleanup(), spy_detach(), ast_channel_spy::status, and ast_channel_spy::type.

Referenced by mixmonitor_cli(), and stop_mixmonitor_exec().

01505 {
01506    struct ast_channel_spy *spy = NULL;
01507    
01508    if (!chan->spies)
01509       return;
01510 
01511    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
01512       if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
01513          AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
01514          spy_detach(spy, chan);
01515       }
01516    }
01517    AST_LIST_TRAVERSE_SAFE_END
01518    spy_cleanup(chan);
01519 }

void ast_channel_spy_trigger_wait ( struct ast_channel_spy spy  ) 

Efficiently wait until audio is available for a spy, or an exception occurs.

Parameters:
spy The spy to wait on
Returns:
nothing
Note: The locking rules for this function are non-obvious... first, you must not hold the channel's lock when calling this function. Second, you must hold the spy's lock before making the function call; while the function runs the lock will be released, and when the trigger event occurs, the lock will be re-obtained. This means that when control returns to your code, you will again hold the spy's lock.

Definition at line 1521 of file channel.c.

References ast_cond_timedwait(), ast_tvadd(), ast_channel_spy::lock, and ast_channel_spy::trigger.

Referenced by mixmonitor_thread().

01522 {
01523    struct timeval tv;
01524    struct timespec ts;
01525 
01526    tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
01527    ts.tv_sec = tv.tv_sec;
01528    ts.tv_nsec = tv.tv_usec * 1000;
01529 
01530    ast_cond_timedwait(&spy->trigger, &spy->lock, &ts);
01531 }


Generated on Fri Sep 25 19:28:33 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5