Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:48:41 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_frame * | ast_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 978 of file channel.c.

References 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, calloc, 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, ast_channel::name, 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().

    00979 {
    00980    /* Link the owner channel to the spy */
    00981    spy->chan = chan;
    00982
    00983    if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
    00984       ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
    00985          spy->type, chan->name);
    00986       return -1;
    00987    }
    00988
    00989    if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
    00990       ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
    00991          ast_getformatname(spy->read_queue.format));
    00992       return -1;
    00993    }
    00994
    00995    if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
    00996       ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
    00997          ast_getformatname(spy->write_queue.format));
    00998       return -1;
    00999    }
    01000
    01001    if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
    01002        ((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
    01003         (spy->write_queue.format != AST_FORMAT_SLINEAR))) {
    01004       ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
    01005          ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
    01006       return -1;
    01007    }
    01008
    01009    if (!chan->spies) {
    01010       if (!(chan->spies = calloc(1, sizeof(*chan->spies)))) {
    01011          ast_log(LOG_WARNING, "Memory allocation failure\n");
    01012          return -1;
    01013       }
    01014
    01015       AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
    01016       AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
    01017    } else {
    01018       AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
    01019    }
    01020
    01021    if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
    01022       ast_cond_init(&spy->trigger, NULL);
    01023       ast_set_flag(spy, CHANSPY_TRIGGER_READ);
    01024       ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
    01025    }
    01026
    01027    ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
    01028       spy->type, chan->name);
    01029
    01030    return 0;
    01031 }

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

References ast_cond_destroy(), ast_frfree(), ast_mutex_destroy(), ast_test_flag, CHANSPY_DONE, CHANSPY_TRIGGER_MODE, CHANSPY_TRIGGER_NONE, ast_channel_spy_queue::head, ast_channel_spy::lock, ast_frame::next, 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().

    01107 {
    01108    struct ast_frame *f = NULL;
    01109
    01110    if (spy->status == CHANSPY_DONE)
    01111       return;
    01112
    01113    /* Switch status to done in case we get called twice */
    01114    spy->status = CHANSPY_DONE;
    01115
    01116    /* Drop any frames in the queue */
    01117    for (f = spy->write_queue.head; f; f = spy->write_queue.head) {
    01118       spy->write_queue.head = f->next;
    01119       ast_frfree(f);
    01120    }
    01121    for (f = spy->read_queue.head; f; f= spy->read_queue.head) {
    01122       spy->read_queue.head = f->next;
    01123       ast_frfree(f);
    01124    }
    01125
    01126    /* Destroy the condition if in use */
    01127    if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
    01128       ast_cond_destroy(&spy->trigger);
    01129
    01130    /* Destroy our mutex since it is no longer in use */
    01131    ast_mutex_destroy(&spy->lock);
    01132
    01133    return;
    01134 }

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 4014 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_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_queue::head, ast_frame::next, ast_channel_spy::read_queue, ast_channel_spy::read_vol_adjustment, result, ast_frame::samples, ast_channel_spy_queue::samples, ast_channel_spy::write_queue, and ast_channel_spy::write_vol_adjustment.

Referenced by mixmonitor_thread(), and spy_generate().

    04015 {
    04016    struct ast_frame *result;
    04017    /* buffers are allocated to hold SLINEAR, which is the largest format */
    04018         short read_buf[samples];
    04019         short write_buf[samples];
    04020    struct ast_frame *read_frame;
    04021    struct ast_frame *write_frame;
    04022    int need_dup;
    04023    struct ast_frame stack_read_frame = { .frametype = AST_FRAME_VOICE,
    04024                      .subclass = spy->read_queue.format,
    04025                      .data = read_buf,
    04026                      .samples = samples,
    04027                      .datalen = ast_codec_get_len(spy->read_queue.format, samples),
    04028    };
    04029    struct ast_frame stack_write_frame = { .frametype = AST_FRAME_VOICE,
    04030                       .subclass = spy->write_queue.format,
    04031                       .data = write_buf,
    04032                       .samples = samples,
    04033                       .datalen = ast_codec_get_len(spy->write_queue.format, samples),
    04034    };
    04035
    04036    /* if a flush has been requested, dump everything in whichever queue is larger */
    04037    if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
    04038       if (spy->read_queue.samples > spy->write_queue.samples) {
    04039          if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
    04040             for (result = spy->read_queue.head; result; result = result->next)
    04041                ast_frame_adjust_volume(result, spy->read_vol_adjustment);
    04042          }
    04043          result = spy->read_queue.head;
    04044          spy->read_queue.head = NULL;
    04045          spy->read_queue.samples = 0;
    04046          ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
    04047          return result;
    04048       } else {
    04049          if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
    04050             for (result = spy->write_queue.head; result; result = result->next)
    04051                ast_frame_adjust_volume(result, spy->write_vol_adjustment);
    04052          }
    04053          result = spy->write_queue.head;
    04054          spy->write_queue.head = NULL;
    04055          spy->write_queue.samples = 0;
    04056          ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
    04057          return result;
    04058       }
    04059    }
    04060
    04061    if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
    04062       return NULL;
    04063
    04064    /* short-circuit if both head frames have exactly what we want */
    04065    if ((spy->read_queue.head->samples == samples) &&
    04066        (spy->write_queue.head->samples == samples)) {
    04067       read_frame = spy->read_queue.head;
    04068       spy->read_queue.head = read_frame->next;
    04069       read_frame->next = NULL;
    04070
    04071       write_frame = spy->write_queue.head;
    04072       spy->write_queue.head = write_frame->next;
    04073       write_frame->next = NULL;
    04074
    04075       spy->read_queue.samples -= samples;
    04076       spy->write_queue.samples -= samples;
    04077
    04078       need_dup = 0;
    04079    } else {
    04080       copy_data_from_queue(&spy->read_queue, read_buf, samples);
    04081       copy_data_from_queue(&spy->write_queue, write_buf, samples);
    04082
    04083       read_frame = &stack_read_frame;
    04084       write_frame = &stack_write_frame;
    04085       need_dup = 1;
    04086    }
    04087
    04088    if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
    04089       ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
    04090
    04091    if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
    04092       ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
    04093
    04094    if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
    04095       ast_frame_slinear_sum(read_frame, write_frame);
    04096
    04097       if (need_dup)
    04098          result = ast_frdup(read_frame);
    04099       else {
    04100          result = read_frame;
    04101          ast_frfree(write_frame);
    04102       }
    04103    } else {
    04104       if (need_dup) {
    04105          result = ast_frdup(read_frame);
    04106          result->next = ast_frdup(write_frame);
    04107       } else {
    04108          result = read_frame;
    04109          result->next = write_frame;
    04110       }
    04111    }
    04112
    04113    return result;
    04114 }

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

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

Referenced by channel_spy().

    01096 {
    01097    if (!chan->spies)
    01098       return;
    01099
    01100    AST_LIST_REMOVE(&chan->spies->list, spy, list);
    01101
    01102    spy_detach(spy, chan);
    01103    spy_cleanup(chan);
    01104 }

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 1066 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().

    01067 {
    01068    struct ast_channel_spy *spy = NULL;
    01069
    01070    if (!chan->spies)
    01071       return;
    01072
    01073    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
    01074       if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
    01075          AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
    01076          spy_detach(spy, chan);
    01077       }
    01078    }
    01079    AST_LIST_TRAVERSE_SAFE_END
    01080    spy_cleanup(chan);
    01081 }

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

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

Referenced by mixmonitor_thread().

    01084 {
    01085    struct timeval tv;
    01086    struct timespec ts;
    01087
    01088    tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
    01089    ts.tv_sec = tv.tv_sec;
    01090    ts.tv_nsec = tv.tv_usec * 1000;
    01091
    01092    ast_cond_timedwait(&spy->trigger, &spy->lock, &ts);
    01093 }