Wed Aug 15 01:25:00 2007

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

enum chanspy_flags

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 };

enum chanspy_states

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 1371 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.

01372 {
01373    /* Link the owner channel to the spy */
01374    spy->chan = chan;
01375 
01376    if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
01377       ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
01378          spy->type, chan->name);
01379       return -1;
01380    }
01381 
01382    if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
01383       ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
01384          ast_getformatname(spy->read_queue.format));
01385       return -1;
01386    }
01387 
01388    if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
01389       ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
01390          ast_getformatname(spy->write_queue.format));
01391       return -1;
01392    }
01393 
01394    if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
01395        ((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
01396         (spy->write_queue.format != AST_FORMAT_SLINEAR))) {
01397       ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
01398          ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
01399       return -1;
01400    }
01401 
01402    if (!chan->spies) {
01403       if (!(chan->spies = ast_calloc(1, sizeof(*chan->spies)))) {
01404          return -1;
01405       }
01406 
01407       AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
01408       AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
01409    } else {
01410       AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
01411    }
01412 
01413    if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
01414       ast_cond_init(&spy->trigger, NULL);
01415       ast_set_flag(spy, CHANSPY_TRIGGER_READ);
01416       ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
01417    }
01418 
01419    if (option_debug)
01420       ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
01421          spy->type, chan->name);
01422 
01423    return 0;
01424 }

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 1501 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.

01502 {
01503    struct ast_frame *f = NULL;
01504 
01505    if (spy->status == CHANSPY_DONE)
01506       return;
01507 
01508    /* Switch status to done in case we get called twice */
01509    spy->status = CHANSPY_DONE;
01510 
01511    /* Drop any frames in the queue */
01512    while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
01513       ast_frfree(f);
01514    while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
01515       ast_frfree(f);
01516 
01517    /* Destroy the condition if in use */
01518    if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
01519       ast_cond_destroy(&spy->trigger);
01520 
01521    /* Destroy our mutex since it is no longer in use */
01522    ast_mutex_destroy(&spy->lock);
01523 
01524    return;
01525 }

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 4637 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.

04638 {
04639    struct ast_frame *result;
04640    /* buffers are allocated to hold SLINEAR, which is the largest format */
04641         short read_buf[samples];
04642         short write_buf[samples];
04643    struct ast_frame *read_frame;
04644    struct ast_frame *write_frame;
04645    int need_dup;
04646    struct ast_frame stack_read_frame = { .frametype = AST_FRAME_VOICE,
04647                      .subclass = spy->read_queue.format,
04648                      .data = read_buf,
04649                      .samples = samples,
04650                      .datalen = ast_codec_get_len(spy->read_queue.format, samples),
04651    };
04652    struct ast_frame stack_write_frame = { .frametype = AST_FRAME_VOICE,
04653                       .subclass = spy->write_queue.format,
04654                       .data = write_buf,
04655                       .samples = samples,
04656                       .datalen = ast_codec_get_len(spy->write_queue.format, samples),
04657    };
04658 
04659    /* if a flush has been requested, dump everything in whichever queue is larger */
04660    if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
04661       if (spy->read_queue.samples > spy->write_queue.samples) {
04662          if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
04663             AST_LIST_TRAVERSE(&spy->read_queue.list, result, frame_list)
04664                ast_frame_adjust_volume(result, spy->read_vol_adjustment);
04665          }
04666          result = AST_LIST_FIRST(&spy->read_queue.list);
04667          AST_LIST_HEAD_SET_NOLOCK(&spy->read_queue.list, NULL);
04668          spy->read_queue.samples = 0;
04669       } else {
04670          if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
04671             AST_LIST_TRAVERSE(&spy->write_queue.list, result, frame_list)
04672                ast_frame_adjust_volume(result, spy->write_vol_adjustment);
04673          }
04674          result = AST_LIST_FIRST(&spy->write_queue.list);
04675          AST_LIST_HEAD_SET_NOLOCK(&spy->write_queue.list, NULL);
04676          spy->write_queue.samples = 0;
04677       }
04678       ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
04679       return result;
04680    }
04681 
04682    if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
04683       return NULL;
04684 
04685    /* short-circuit if both head frames have exactly what we want */
04686    if ((AST_LIST_FIRST(&spy->read_queue.list)->samples == samples) &&
04687        (AST_LIST_FIRST(&spy->write_queue.list)->samples == samples)) {
04688       read_frame = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list);
04689       write_frame = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list);
04690 
04691       spy->read_queue.samples -= samples;
04692       spy->write_queue.samples -= samples;
04693 
04694       need_dup = 0;
04695    } else {
04696       copy_data_from_queue(&spy->read_queue, read_buf, samples);
04697       copy_data_from_queue(&spy->write_queue, write_buf, samples);
04698 
04699       read_frame = &stack_read_frame;
04700       write_frame = &stack_write_frame;
04701       need_dup = 1;
04702    }
04703    
04704    if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
04705       ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
04706 
04707    if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
04708       ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
04709 
04710    if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
04711       ast_frame_slinear_sum(read_frame, write_frame);
04712 
04713       if (need_dup)
04714          result = ast_frdup(read_frame);
04715       else {
04716          result = read_frame;
04717          ast_frfree(write_frame);
04718       }
04719    } else {
04720       if (need_dup) {
04721          result = ast_frdup(read_frame);
04722          AST_LIST_NEXT(result, frame_list) = ast_frdup(write_frame);
04723       } else {
04724          result = read_frame;
04725          AST_LIST_NEXT(result, frame_list) = write_frame;
04726       }
04727    }
04728 
04729    return result;
04730 }

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 1491 of file channel.c.

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

01492 {
01493    if (!chan->spies)
01494       return;
01495 
01496    AST_LIST_REMOVE(&chan->spies->list, spy, list);
01497    spy_detach(spy, chan);
01498    spy_cleanup(chan);
01499 }

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 1462 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.

01463 {
01464    struct ast_channel_spy *spy = NULL;
01465    
01466    if (!chan->spies)
01467       return;
01468 
01469    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
01470       if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
01471          AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
01472          spy_detach(spy, chan);
01473       }
01474    }
01475    AST_LIST_TRAVERSE_SAFE_END
01476    spy_cleanup(chan);
01477 }

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 1479 of file channel.c.

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

01480 {
01481    struct timeval tv;
01482    struct timespec ts;
01483 
01484    tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
01485    ts.tv_sec = tv.tv_sec;
01486    ts.tv_nsec = tv.tv_usec * 1000;
01487 
01488    ast_cond_timedwait(&spy->trigger, &spy->lock, &ts);
01489 }


Generated on Wed Aug 15 01:25:00 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3