Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2005, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \author Mark Spencer <markster@digium.com>
/*** MODULEINFO
<support_level>core</support_level>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
Kevin P. Fleming
committed
#include "asterisk/_private.h"
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/frame.h"
#include "asterisk/format_cache.h"
Kevin P. Fleming
committed
#include "asterisk/channel.h"
#include "asterisk/cli.h"
#include "asterisk/term.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
#include "asterisk/linkedlists.h"
#include "asterisk/translate.h"
#if !defined(LOW_MEMORY)
static void frame_cache_cleanup(void *data);
/*! \brief A per-thread cache of frame headers */
Russell Bryant
committed
AST_THREADSTORAGE_CUSTOM(frame_cache, NULL, frame_cache_cleanup);
* \brief Maximum ast_frame cache size
*
* In most cases where the frame header cache will be useful, the size
* of the cache will stay very small. However, it is not always the case that
* the same thread that allocates the frame will be the one freeing them, so
* sometimes a thread will never have any frames in its cache, or the cache
* will never be pulled from. For the latter case, we limit the maximum size.
*/
#define FRAME_CACHE_MAX_SIZE 10
/*! \brief This is just so ast_frames, a list head struct for holding a list of
* ast_frame structures, is defined. */
AST_LIST_HEAD_NOLOCK(ast_frames, ast_frame);
struct ast_frame_cache {
struct ast_frames list;
size_t size;
};
struct ast_frame ast_null_frame = { AST_FRAME_NULL, };
static struct ast_frame *ast_frame_header_new(void)
{
#if !defined(LOW_MEMORY)
struct ast_frame_cache *frames;
if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
if ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list))) {
size_t mallocd_len = f->mallocd_hdr_len;
memset(f, 0, sizeof(*f));
f->mallocd_hdr_len = mallocd_len;
f->mallocd = AST_MALLOCD_HDR;
frames->size--;
return f;
}
if (!(f = ast_calloc_cache(1, sizeof(*f))))
#else
if (!(f = ast_calloc(1, sizeof(*f))))
return NULL;
#endif
#if !defined(LOW_MEMORY)
static void frame_cache_cleanup(void *data)
{
struct ast_frame_cache *frames = data;
struct ast_frame *f;
while ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list)))
Tilghman Lesher
committed
ast_free(f);
Tilghman Lesher
committed
ast_free(frames);
static void __frame_free(struct ast_frame *fr, int cache)
#if !defined(LOW_MEMORY)
Russell Bryant
committed
if (cache && fr->mallocd == AST_MALLOCD_HDR) {
/* Cool, only the header is malloc'd, let's just cache those for now
* to keep things simple... */
struct ast_frame_cache *frames;
if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) &&
(frames->size < FRAME_CACHE_MAX_SIZE)) {
if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) ||
(fr->frametype == AST_FRAME_IMAGE)) {
ao2_cleanup(fr->subclass.format);
}
AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list);
frames->size++;
return;
}
}
Michiel van Baak
committed
ast_free(fr->data.ptr - fr->offset);
}
if (fr->mallocd & AST_MALLOCD_SRC) {
if (fr->src)
ast_free((void *) fr->src);
if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) ||
(fr->frametype == AST_FRAME_IMAGE)) {
ao2_cleanup(fr->subclass.format);
}
Tilghman Lesher
committed
ast_free(fr);
} else {
fr->mallocd = 0;
void ast_frame_free(struct ast_frame *frame, int cache)
{
struct ast_frame *next;
for (next = AST_LIST_NEXT(frame, frame_list);
frame;
frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) {
__frame_free(frame, cache);
}
}
void ast_frame_dtor(struct ast_frame *f)
{
if (f) {
ast_frfree(f);
}
}
/*!
* \brief 'isolates' a frame by duplicating non-malloc'ed components
* (header, src, data).
* On return all components are malloc'ed
*/
struct ast_frame *ast_frisolate(struct ast_frame *fr)
{
struct ast_frame *out;
void *newdata;
/* if none of the existing frame is malloc'd, let ast_frdup() do it
since it is more efficient
*/
if (fr->mallocd == 0) {
return ast_frdup(fr);
}
/* if everything is already malloc'd, we are done */
if ((fr->mallocd & (AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA)) ==
(AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA)) {
return fr;
}
if (!(fr->mallocd & AST_MALLOCD_HDR)) {
/* Allocate a new header if needed */
if (!(out = ast_frame_header_new())) {
if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) ||
(fr->frametype == AST_FRAME_IMAGE)) {
out->subclass.format = ao2_bump(fr->subclass.format);
} else {
memcpy(&out->subclass, &fr->subclass, sizeof(out->subclass));
}
Russell Bryant
committed
/* Copy the timing data */
ast_copy_flags(out, fr, AST_FLAGS_ALL);
if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) {
Russell Bryant
committed
out->ts = fr->ts;
out->len = fr->len;
out->seqno = fr->seqno;
}
if (!(fr->mallocd & AST_MALLOCD_SRC) && fr->src) {
if (!(out->src = ast_strdup(fr->src))) {
if (out != fr) {
ast_free(out);
Kevin P. Fleming
committed
}
Kevin P. Fleming
committed
}
fr->src = NULL;
fr->mallocd &= ~AST_MALLOCD_SRC;
}
if (!fr->datalen) {
out->data.uint32 = fr->data.uint32;
out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC;
return out;
}
if (!(newdata = ast_malloc(fr->datalen + AST_FRIENDLY_OFFSET))) {
Tilghman Lesher
committed
ast_free((void *) out->src);
Tilghman Lesher
committed
ast_free(out);
newdata += AST_FRIENDLY_OFFSET;
out->offset = AST_FRIENDLY_OFFSET;
out->datalen = fr->datalen;
Michiel van Baak
committed
memcpy(newdata, fr->data.ptr, fr->datalen);
out->data.ptr = newdata;
} else {
out->data = fr->data;
memset(&fr->data, 0, sizeof(fr->data));
fr->mallocd &= ~AST_MALLOCD_DATA;
out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
Kevin P. Fleming
committed
struct ast_frame *ast_frdup(const struct ast_frame *f)
int len, srclen = 0;
#if !defined(LOW_MEMORY)
struct ast_frame_cache *frames;
#endif
len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
/* If we have a source, add space for it */
/*
* XXX Watch out here - if we receive a src which is not terminated
* properly, we can be easily attacked. Should limit the size we deal with.
*/
if (f->src)
srclen = strlen(f->src);
if (srclen > 0)
len += srclen + 1;
#if !defined(LOW_MEMORY)
if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
AST_LIST_TRAVERSE_SAFE_BEGIN(&frames->list, out, frame_list) {
if (out->mallocd_hdr_len >= len) {
size_t mallocd_len = out->mallocd_hdr_len;
AST_LIST_REMOVE_CURRENT(frame_list);
memset(out, 0, sizeof(*out));
out->mallocd_hdr_len = mallocd_len;
buf = out;
frames->size--;
break;
}
}
if (!(buf = ast_calloc_cache(1, len)))
return NULL;
out = buf;
out->mallocd_hdr_len = len;
}
if ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO) ||
(f->frametype == AST_FRAME_IMAGE)) {
out->subclass.format = ao2_bump(f->subclass.format);
} else {
memcpy(&out->subclass, &f->subclass, sizeof(out->subclass));
}
out->datalen = f->datalen;
out->samples = f->samples;
out->delivery = f->delivery;
Kevin P. Fleming
committed
/* Even though this new frame was allocated from the heap, we can't mark it
* with AST_MALLOCD_HDR, AST_MALLOCD_DATA and AST_MALLOCD_SRC, because that
* would cause ast_frfree() to attempt to individually free each of those
* under the assumption that they were separately allocated. Since this frame
* was allocated in a single allocation, we'll only mark it as if the header
* was heap-allocated; this will result in the entire frame being properly freed.
*/
out->mallocd = AST_MALLOCD_HDR;
out->offset = AST_FRIENDLY_OFFSET;
if (out->datalen) {
Michiel van Baak
committed
out->data.ptr = buf + sizeof(*out) + AST_FRIENDLY_OFFSET;
memcpy(out->data.ptr, f->data.ptr, out->datalen);
} else {
out->data.uint32 = f->data.uint32;
}
if (srclen > 0) {
/* This may seem a little strange, but it's to avoid a gcc (4.2.4) compiler warning */
char *src;
out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
/* Must have space since we allocated for it */
}
ast_copy_flags(out, f, AST_FLAGS_ALL);
out->ts = f->ts;
out->len = f->len;
out->seqno = f->seqno;
void ast_swapcopy_samples(void *dst, const void *src, int samples)
Kevin P. Fleming
committed
{
int i;
unsigned short *dst_s = dst;
const unsigned short *src_s = src;
Kevin P. Fleming
committed
Kevin P. Fleming
committed
dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8);
}
void ast_frame_subclass2str(struct ast_frame *f, char *subclass, size_t slen, char *moreinfo, size_t mlen)
if (slen > 1) {
subclass[0] = f->subclass.integer;
subclass[1] = '\0';
}
if (slen > 1) {
subclass[0] = f->subclass.integer;
subclass[1] = '\0';
}
switch (f->subclass.integer) {
ast_copy_string(subclass, "Hangup", slen);
ast_copy_string(subclass, "Ring", slen);
ast_copy_string(subclass, "Ringing", slen);
ast_copy_string(subclass, "Answer", slen);
ast_copy_string(subclass, "Busy", slen);
break;
case AST_CONTROL_TAKEOFFHOOK:
ast_copy_string(subclass, "Take Off Hook", slen);
ast_copy_string(subclass, "Line Off Hook", slen);
break;
case AST_CONTROL_CONGESTION:
ast_copy_string(subclass, "Congestion", slen);
ast_copy_string(subclass, "Flash", slen);
ast_copy_string(subclass, "Wink", slen);
ast_copy_string(subclass, "Option", slen);
break;
case AST_CONTROL_RADIO_KEY:
ast_copy_string(subclass, "Key Radio", slen);
break;
case AST_CONTROL_RADIO_UNKEY:
ast_copy_string(subclass, "Unkey Radio", slen);
ast_copy_string(subclass, "Hold", slen);
ast_copy_string(subclass, "Unhold", slen);
case AST_CONTROL_T38_PARAMETERS: {
char *message = "Unknown";
Kevin P. Fleming
committed
if (f->datalen != sizeof(struct ast_control_t38_parameters)) {
Joshua Colp
committed
message = "Invalid";
} else {
struct ast_control_t38_parameters *parameters = f->data.ptr;
enum ast_control_t38 state = parameters->request_response;
if (state == AST_T38_REQUEST_NEGOTIATE)
message = "Negotiation Requested";
else if (state == AST_T38_REQUEST_TERMINATE)
message = "Negotiation Request Terminated";
else if (state == AST_T38_NEGOTIATED)
message = "Negotiated";
else if (state == AST_T38_TERMINATED)
message = "Terminated";
else if (state == AST_T38_REFUSED)
message = "Refused";
}
snprintf(subclass, slen, "T38_Parameters/%s", message);
Joshua Colp
committed
break;
Mark Spencer
committed
case -1:
ast_copy_string(subclass, "Stop generators", slen);
Mark Spencer
committed
break;
snprintf(subclass, slen, "Unknown control '%d'", f->subclass.integer);
Mark Spencer
committed
break;
ast_copy_string(subclass, "N/A", slen);
break;
case AST_FRAME_IAX:
/* Should never happen */
snprintf(subclass, slen, "IAX Frametype %d", f->subclass.integer);
Richard Mudgett
committed
case AST_FRAME_BRIDGE_ACTION:
/* Should never happen */
snprintf(subclass, slen, "Bridge Frametype %d", f->subclass.integer);
break;
Mark Michelson
committed
case AST_FRAME_BRIDGE_ACTION_SYNC:
/* Should never happen */
snprintf(subclass, slen, "Synchronous Bridge Frametype %d", f->subclass.integer);
break;
ast_copy_string(subclass, "N/A", slen);
if (moreinfo) {
ast_copy_string(moreinfo, f->data.ptr, mlen);
}
snprintf(subclass, slen, "Image format %s\n", ast_format_get_name(f->subclass.format));
switch (f->subclass.integer) {
ast_copy_string(subclass, "URL", slen);
if (moreinfo) {
ast_copy_string(moreinfo, f->data.ptr, mlen);
}
ast_copy_string(subclass, "Data", slen);
ast_copy_string(subclass, "Begin", slen);
ast_copy_string(subclass, "End", slen);
ast_copy_string(subclass, "Load Complete", slen);
ast_copy_string(subclass, "No Support", slen);
ast_copy_string(subclass, "Link URL", slen);
if (moreinfo) {
ast_copy_string(moreinfo, f->data.ptr, mlen);
}
ast_copy_string(subclass, "Unlink", slen);
ast_copy_string(subclass, "Link Reject", slen);
snprintf(subclass, slen, "Unknown HTML frame '%d'\n", f->subclass.integer);
Joshua Colp
committed
case AST_FRAME_MODEM:
switch (f->subclass.integer) {
Joshua Colp
committed
case AST_MODEM_T38:
ast_copy_string(subclass, "T.38", slen);
Joshua Colp
committed
break;
case AST_MODEM_V150:
ast_copy_string(subclass, "V.150", slen);
Joshua Colp
committed
break;
default:
snprintf(subclass, slen, "Unknown MODEM frame '%d'\n", f->subclass.integer);
Joshua Colp
committed
break;
}
break;
ast_copy_string(subclass, "Unknown Subclass", slen);
}
void ast_frame_type2str(enum ast_frame_type frame_type, char *ftype, size_t len)
{
switch (frame_type) {
case AST_FRAME_DTMF_BEGIN:
ast_copy_string(ftype, "DTMF Begin", len);
break;
case AST_FRAME_DTMF_END:
ast_copy_string(ftype, "DTMF End", len);
break;
case AST_FRAME_CONTROL:
ast_copy_string(ftype, "Control", len);
break;
case AST_FRAME_NULL:
ast_copy_string(ftype, "Null Frame", len);
break;
case AST_FRAME_IAX:
/* Should never happen */
ast_copy_string(ftype, "IAX Specific", len);
break;
Richard Mudgett
committed
case AST_FRAME_BRIDGE_ACTION:
/* Should never happen */
ast_copy_string(ftype, "Bridge Specific", len);
Mark Michelson
committed
break;
case AST_FRAME_BRIDGE_ACTION_SYNC:
/* Should never happen */
ast_copy_string(ftype, "Bridge Specific", len);
Richard Mudgett
committed
break;
case AST_FRAME_TEXT:
ast_copy_string(ftype, "Text", len);
break;
case AST_FRAME_IMAGE:
ast_copy_string(ftype, "Image", len);
break;
case AST_FRAME_HTML:
ast_copy_string(ftype, "HTML", len);
break;
case AST_FRAME_MODEM:
ast_copy_string(ftype, "Modem", len);
break;
case AST_FRAME_VOICE:
ast_copy_string(ftype, "Voice", len);
break;
case AST_FRAME_VIDEO:
ast_copy_string(ftype, "Video", len);
break;
default:
snprintf(ftype, len, "Unknown Frametype '%u'", frame_type);
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
}
}
/*! Dump a frame for debugging purposes */
void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
{
const char noname[] = "unknown";
char ftype[40] = "Unknown Frametype";
char cft[80];
char subclass[40] = "Unknown Subclass";
char csub[80];
char moreinfo[40] = "";
char cn[60];
char cp[40];
char cmn[40];
if (!name) {
name = noname;
}
if (!f) {
ast_verb(-1, "%s [ %s (NULL) ] [%s]\n",
term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
return;
}
/* XXX We should probably print one each of voice and video when the format changes XXX */
if (f->frametype == AST_FRAME_VOICE) {
return;
}
if (f->frametype == AST_FRAME_VIDEO) {
return;
}
ast_frame_type2str(f->frametype, ftype, sizeof(ftype));
ast_frame_subclass2str(f, subclass, sizeof(subclass), moreinfo, sizeof(moreinfo));
ast_verb(-1, "%s [ TYPE: %s (%u) SUBCLASS: %s (%d) '%s' ] [%s]\n",
term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
ast_verb(-1, "%s [ TYPE: %s (%u) SUBCLASS: %s (%d) ] [%s]\n",
term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
{
int count;
Michiel van Baak
committed
short *fdata = f->data.ptr;
Kevin P. Fleming
committed
short adjust_value = abs(adjustment);
if ((f->frametype != AST_FRAME_VOICE) || !(ast_format_cache_is_slinear(f->subclass.format))) {
return -1;
David Vossel
committed
}
David Vossel
committed
if (!adjustment) {
Kevin P. Fleming
committed
return 0;
David Vossel
committed
}
Kevin P. Fleming
committed
for (count = 0; count < f->samples; count++) {
if (adjustment > 0) {
Kevin P. Fleming
committed
ast_slinear_saturated_multiply(&fdata[count], &adjust_value);
} else if (adjustment < 0) {
Kevin P. Fleming
committed
ast_slinear_saturated_divide(&fdata[count], &adjust_value);
}
}
return 0;
}
int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
{
int count;
short *data1, *data2;
if ((f1->frametype != AST_FRAME_VOICE) || (ast_format_cmp(f1->subclass.format, ast_format_slin) != AST_FORMAT_CMP_NOT_EQUAL))
return -1;
if ((f2->frametype != AST_FRAME_VOICE) || (ast_format_cmp(f2->subclass.format, ast_format_slin) != AST_FORMAT_CMP_NOT_EQUAL))
return -1;
if (f1->samples != f2->samples)
return -1;
Michiel van Baak
committed
for (count = 0, data1 = f1->data.ptr, data2 = f2->data.ptr;
count < f1->samples;
count++, data1++, data2++)
Kevin P. Fleming
committed
ast_slinear_saturated_add(data1, data2);
return 0;
}
int ast_frame_clear(struct ast_frame *frame)
{
struct ast_frame *next;
for (next = AST_LIST_NEXT(frame, frame_list);
frame;
frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) {
memset(frame->data.ptr, 0, frame->datalen);
}
return 0;
}