Sat Sep 16 05:47:46 2006

Asterisk developer's documentation


frame.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Frame manipulation routines
00022  * 
00023  */
00024 
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 #include <string.h>
00028 #include <errno.h>
00029 #include <stdio.h>
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 38547 $")
00034 
00035 #include "asterisk/lock.h"
00036 #include "asterisk/frame.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/options.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/cli.h"
00041 #include "asterisk/term.h"
00042 #include "asterisk/utils.h"
00043 
00044 #ifdef TRACE_FRAMES
00045 static int headers = 0;
00046 static struct ast_frame *headerlist = NULL;
00047 AST_MUTEX_DEFINE_STATIC(framelock);
00048 #endif
00049 
00050 #define SMOOTHER_SIZE 8000
00051 
00052 #define TYPE_HIGH  0x0
00053 #define TYPE_LOW   0x1
00054 #define TYPE_SILENCE  0x2
00055 #define TYPE_DONTSEND    0x3
00056 #define TYPE_MASK  0x3
00057 
00058 struct ast_format_list {
00059    int visible;   /*!< Can we see this entry */
00060    int bits;   /*!< bitmask value */
00061    char *name; /*!< short name */
00062    char *desc; /*!< Description */
00063 };
00064 
00065 struct ast_smoother {
00066    int size;
00067    int format;
00068    int readdata;
00069    int optimizablestream;
00070    int flags;
00071    float samplesperbyte;
00072    struct ast_frame f;
00073    struct timeval delivery;
00074    char data[SMOOTHER_SIZE];
00075    char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
00076    struct ast_frame *opt;
00077    int len;
00078 };
00079 
00080 /*! \brief Definition of supported media formats (codecs) */
00081 static struct ast_format_list AST_FORMAT_LIST[] = {
00082    { 1, AST_FORMAT_G723_1 , "g723" , "G.723.1"},   /*!< codec_g723_1.c */
00083    { 1, AST_FORMAT_GSM, "gsm" , "GSM"},      /*!< codec_gsm.c */
00084    { 1, AST_FORMAT_ULAW, "ulaw", "G.711 u-law" },  /*!< codec_ulaw.c */
00085    { 1, AST_FORMAT_ALAW, "alaw", "G.711 A-law" },  /*!< codec_alaw.c */
00086    { 1, AST_FORMAT_G726, "g726", "G.726" },  /*!< codec_g726.c */
00087    { 1, AST_FORMAT_ADPCM, "adpcm" , "ADPCM"},   /*!< codec_adpcm.c */
00088    { 1, AST_FORMAT_SLINEAR, "slin",  "16 bit Signed Linear PCM"}, /*!<  */
00089    { 1, AST_FORMAT_LPC10, "lpc10", "LPC10" },   /*!< codec_lpc10.c */
00090    { 1, AST_FORMAT_G729A, "g729", "G.729A" },   /*!< Binary commercial distribution */
00091    { 1, AST_FORMAT_SPEEX, "speex", "SpeeX" },   /*!< codec_speex.c */
00092    { 1, AST_FORMAT_ILBC, "ilbc", "iLBC"}, /*!< codec_ilbc.c */
00093    { 0, 0, "nothing", "undefined" },
00094    { 0, 0, "nothing", "undefined" },
00095    { 0, 0, "nothing", "undefined" },
00096    { 0, 0, "nothing", "undefined" },
00097    { 0, AST_FORMAT_MAX_AUDIO, "maxaudio", "Maximum audio format" },
00098    { 1, AST_FORMAT_JPEG, "jpeg", "JPEG image"}, /*!< See format_jpeg.c */
00099    { 1, AST_FORMAT_PNG, "png", "PNG image"}, /*!< Image format */
00100    { 1, AST_FORMAT_H261, "h261", "H.261 Video" },  /*!< Passthrough */
00101    { 1, AST_FORMAT_H263, "h263", "H.263 Video" },  /*!< Passthrough support, see format_h263.c */
00102    { 1, AST_FORMAT_H263_PLUS, "h263p", "H.263+ Video" }, /*!< See format_h263.c */
00103    { 0, 0, "nothing", "undefined" },
00104    { 0, 0, "nothing", "undefined" },
00105    { 0, 0, "nothing", "undefined" },
00106    { 0, 0, "nothing", "undefined" },
00107    { 0, AST_FORMAT_MAX_VIDEO, "maxvideo", "Maximum video format" },
00108 };
00109 
00110 void ast_smoother_reset(struct ast_smoother *s, int size)
00111 {
00112    memset(s, 0, sizeof(struct ast_smoother));
00113    s->size = size;
00114 }
00115 
00116 struct ast_smoother *ast_smoother_new(int size)
00117 {
00118    struct ast_smoother *s;
00119    if (size < 1)
00120       return NULL;
00121    s = malloc(sizeof(struct ast_smoother));
00122    if (s)
00123       ast_smoother_reset(s, size);
00124    return s;
00125 }
00126 
00127 int ast_smoother_get_flags(struct ast_smoother *s)
00128 {
00129    return s->flags;
00130 }
00131 
00132 void ast_smoother_set_flags(struct ast_smoother *s, int flags)
00133 {
00134    s->flags = flags;
00135 }
00136 
00137 int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap)
00138 {
00139    if (f->frametype != AST_FRAME_VOICE) {
00140       ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
00141       return -1;
00142    }
00143    if (!s->format) {
00144       s->format = f->subclass;
00145       s->samplesperbyte = (float)f->samples / (float)f->datalen;
00146    } else if (s->format != f->subclass) {
00147       ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
00148       return -1;
00149    }
00150    if (s->len + f->datalen > SMOOTHER_SIZE) {
00151       ast_log(LOG_WARNING, "Out of smoother space\n");
00152       return -1;
00153    }
00154    if (((f->datalen == s->size) || ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729)))
00155              && !s->opt && (f->offset >= AST_MIN_OFFSET)) {
00156       if (!s->len) {
00157          /* Optimize by sending the frame we just got
00158             on the next read, thus eliminating the douple
00159             copy */
00160          s->opt = f;
00161          return 0;
00162       } else {
00163          s->optimizablestream++;
00164          if (s->optimizablestream > 10) {
00165             /* For the past 10 rounds, we have input and output
00166                frames of the correct size for this smoother, yet
00167                we were unable to optimize because there was still
00168                some cruft left over.  Lets just drop the cruft so
00169                we can move to a fully optimized path */
00170             s->len = 0;
00171             s->opt = f;
00172             return 0;
00173          }
00174       }
00175    } else 
00176       s->optimizablestream = 0;
00177    if (s->flags & AST_SMOOTHER_FLAG_G729) {
00178       if (s->len % 10) {
00179          ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n");
00180          return 0;
00181       }
00182    }
00183    if (swap)
00184       ast_swapcopy_samples(s->data+s->len, f->data, f->samples);
00185    else
00186       memcpy(s->data + s->len, f->data, f->datalen);
00187    /* If either side is empty, reset the delivery time */
00188    if (!s->len || ast_tvzero(f->delivery) || ast_tvzero(s->delivery))   /* XXX really ? */
00189       s->delivery = f->delivery;
00190    s->len += f->datalen;
00191    return 0;
00192 }
00193 
00194 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
00195 {
00196    struct ast_frame *opt;
00197    int len;
00198    /* IF we have an optimization frame, send it */
00199    if (s->opt) {
00200       if (s->opt->offset < AST_FRIENDLY_OFFSET)
00201          ast_log(LOG_WARNING, "Returning a frame of inappropriate offset (%d).",
00202                      s->opt->offset);
00203       opt = s->opt;
00204       s->opt = NULL;
00205       return opt;
00206    }
00207 
00208    /* Make sure we have enough data */
00209    if (s->len < s->size) {
00210       /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */
00211       if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->size % 10)))
00212          return NULL;
00213    }
00214    len = s->size;
00215    if (len > s->len)
00216       len = s->len;
00217    /* Make frame */
00218    s->f.frametype = AST_FRAME_VOICE;
00219    s->f.subclass = s->format;
00220    s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
00221    s->f.offset = AST_FRIENDLY_OFFSET;
00222    s->f.datalen = len;
00223    /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */
00224    s->f.samples = len * s->samplesperbyte;   /* XXX rounding */
00225    s->f.delivery = s->delivery;
00226    /* Fill Data */
00227    memcpy(s->f.data, s->data, len);
00228    s->len -= len;
00229    /* Move remaining data to the front if applicable */
00230    if (s->len) {
00231       /* In principle this should all be fine because if we are sending
00232          G.729 VAD, the next timestamp will take over anyawy */
00233       memmove(s->data, s->data + len, s->len);
00234       if (!ast_tvzero(s->delivery)) {
00235          /* If we have delivery time, increment it, otherwise, leave it at 0 */
00236          s->delivery = ast_tvadd(s->delivery, ast_samp2tv(s->f.samples, 8000));
00237       }
00238    }
00239    /* Return frame */
00240    return &s->f;
00241 }
00242 
00243 void ast_smoother_free(struct ast_smoother *s)
00244 {
00245    free(s);
00246 }
00247 
00248 static struct ast_frame *ast_frame_header_new(void)
00249 {
00250    struct ast_frame *f;
00251    f = malloc(sizeof(struct ast_frame));
00252    if (f)
00253       memset(f, 0, sizeof(struct ast_frame));
00254 #ifdef TRACE_FRAMES
00255    if (f) {
00256       f->prev = NULL;
00257       ast_mutex_lock(&framelock);
00258       headers++;
00259       f->next = headerlist;
00260       if (headerlist)
00261          headerlist->prev = f;
00262       headerlist = f;
00263       ast_mutex_unlock(&framelock);
00264    }
00265 #endif   
00266    return f;
00267 }
00268 
00269 /*!
00270  * \todo Important: I should be made more efficient.  Frame headers should
00271  * most definitely be cached
00272  */
00273 void ast_frfree(struct ast_frame *fr)
00274 {
00275    if (fr->mallocd & AST_MALLOCD_DATA) {
00276       if (fr->data) 
00277          free(fr->data - fr->offset);
00278    }
00279    if (fr->mallocd & AST_MALLOCD_SRC) {
00280       if (fr->src)
00281          free((char *)fr->src);
00282    }
00283    if (fr->mallocd & AST_MALLOCD_HDR) {
00284 #ifdef TRACE_FRAMES
00285       ast_mutex_lock(&framelock);
00286       headers--;
00287       if (fr->next)
00288          fr->next->prev = fr->prev;
00289       if (fr->prev)
00290          fr->prev->next = fr->next;
00291       else
00292          headerlist = fr->next;
00293       ast_mutex_unlock(&framelock);
00294 #endif         
00295       free(fr);
00296    }
00297 }
00298 
00299 /*!
00300  * \brief 'isolates' a frame by duplicating non-malloc'ed components
00301  * (header, src, data).
00302  * On return all components are malloc'ed
00303  */
00304 struct ast_frame *ast_frisolate(struct ast_frame *fr)
00305 {
00306    struct ast_frame *out;
00307    void *newdata;
00308 
00309    if (!(fr->mallocd & AST_MALLOCD_HDR)) {
00310       /* Allocate a new header if needed */
00311       out = ast_frame_header_new();
00312       if (!out) {
00313          ast_log(LOG_WARNING, "Out of memory\n");
00314          return NULL;
00315       }
00316       out->frametype = fr->frametype;
00317       out->subclass = fr->subclass;
00318       out->datalen = fr->datalen;
00319       out->samples = fr->samples;
00320       out->offset = fr->offset;
00321       out->src = NULL;
00322       out->data = fr->data;
00323    } else
00324       out = fr;
00325    
00326    if (!(fr->mallocd & AST_MALLOCD_SRC)) {
00327       if (fr->src) {
00328          out->src = strdup(fr->src);
00329          if (!out->src) {
00330             if (out != fr)
00331                free(out);
00332             ast_log(LOG_WARNING, "Out of memory\n");
00333             return NULL;
00334          }
00335       }
00336    } else
00337       out->src = fr->src;
00338    
00339    if (!(fr->mallocd & AST_MALLOCD_DATA))  {
00340       newdata = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
00341       if (!newdata) {
00342          if (out->src != fr->src)
00343             free((void *) out->src);
00344          if (out != fr)
00345             free(out);
00346          ast_log(LOG_WARNING, "Out of memory\n");
00347          return NULL;
00348       }
00349       newdata += AST_FRIENDLY_OFFSET;
00350       out->offset = AST_FRIENDLY_OFFSET;
00351       out->datalen = fr->datalen;
00352       memcpy(newdata, fr->data, fr->datalen);
00353       out->data = newdata;
00354    }
00355 
00356    out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
00357    
00358    return out;
00359 }
00360 
00361 struct ast_frame *ast_frdup(struct ast_frame *f)
00362 {
00363    struct ast_frame *out;
00364    int len, srclen = 0;
00365    void *buf;
00366    /* Start with standard stuff */
00367    len = sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET + f->datalen;
00368    /* If we have a source, add space for it */
00369    /*
00370     * XXX Watch out here - if we receive a src which is not terminated
00371     * properly, we can be easily attacked. Should limit the size we deal with.
00372     */
00373    if (f->src)
00374       srclen = strlen(f->src);
00375    if (srclen > 0)
00376       len += srclen + 1;
00377    buf = calloc(1, len);
00378    if (!buf)
00379       return NULL;
00380    out = buf;
00381    /* Set us as having malloc'd header only, so it will eventually
00382       get freed. */
00383    out->frametype = f->frametype;
00384    out->subclass = f->subclass;
00385    out->datalen = f->datalen;
00386    out->samples = f->samples;
00387    out->delivery = f->delivery;
00388    out->mallocd = AST_MALLOCD_HDR;
00389    out->offset = AST_FRIENDLY_OFFSET;
00390    if (out->datalen) {
00391       out->data = buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
00392       memcpy(out->data, f->data, out->datalen); 
00393    }
00394    if (srclen > 0) {
00395       out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
00396       /* Must have space since we allocated for it */
00397       strcpy((char *)out->src, f->src);
00398    }
00399    return out;
00400 }
00401 
00402 #if 0
00403 /*
00404  * XXX
00405  * This function is badly broken - it does not handle correctly
00406  * partial reads on either header or body.
00407  * However is it never used anywhere so we leave it commented out
00408  */
00409 struct ast_frame *ast_fr_fdread(int fd)
00410 {
00411    char buf[65536];
00412    int res;
00413    int ttl = sizeof(struct ast_frame);
00414    struct ast_frame *f = (struct ast_frame *)buf;
00415    /* Read a frame directly from there.  They're always in the
00416       right format. */
00417    
00418    while(ttl) {
00419       res = read(fd, buf, ttl);
00420       if (res < 0) {
00421          ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
00422          return NULL;
00423       }
00424       ttl -= res;
00425    }
00426    
00427    /* read the frame header */
00428    f->mallocd = 0;
00429    /* Re-write data position */
00430    f->data = buf + sizeof(struct ast_frame);
00431    f->offset = 0;
00432    /* Forget about being mallocd */
00433    f->mallocd = 0;
00434    /* Re-write the source */
00435    f->src = (char *)__FUNCTION__;
00436    if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
00437       /* Really bad read */
00438       ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
00439       return NULL;
00440    }
00441    if (f->datalen) {
00442       if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
00443          /* Bad read */
00444          ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
00445          return NULL;
00446       }
00447    }
00448    if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00449       return NULL;
00450    }
00451    return ast_frisolate(f);
00452 }
00453 
00454 /* Some convenient routines for sending frames to/from stream or datagram
00455    sockets, pipes, etc (maybe even files) */
00456 
00457 /*
00458  * XXX this function is also partly broken because it does not handle
00459  * partial writes. We comment it out too, and also the unique
00460  * client it has, ast_fr_fdhangup()
00461  */
00462 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
00463 {
00464    /* Write the frame exactly */
00465    if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
00466       ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
00467       return -1;
00468    }
00469    if (write(fd, frame->data, frame->datalen) != frame->datalen) {
00470       ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
00471       return -1;
00472    }
00473    return 0;
00474 }
00475 
00476 int ast_fr_fdhangup(int fd)
00477 {
00478    struct ast_frame hangup = {
00479       AST_FRAME_CONTROL,
00480       AST_CONTROL_HANGUP
00481    };
00482    return ast_fr_fdwrite(fd, &hangup);
00483 }
00484 
00485 #endif /* unused functions */
00486 void ast_swapcopy_samples(void *dst, const void *src, int samples)
00487 {
00488    int i;
00489    unsigned short *dst_s = dst;
00490    const unsigned short *src_s = src;
00491 
00492    for (i=0; i<samples; i++)
00493       dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8);
00494 }
00495 
00496 
00497 struct ast_format_list *ast_get_format_list_index(int index) 
00498 {
00499    return &AST_FORMAT_LIST[index];
00500 }
00501 
00502 struct ast_format_list *ast_get_format_list(size_t *size) 
00503 {
00504    *size = (sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list));
00505    return AST_FORMAT_LIST;
00506 }
00507 
00508 char* ast_getformatname(int format)
00509 {
00510    int x = 0;
00511    char *ret = "unknown";
00512    for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) {
00513       if(AST_FORMAT_LIST[x].visible && AST_FORMAT_LIST[x].bits == format) {
00514          ret = AST_FORMAT_LIST[x].name;
00515          break;
00516       }
00517    }
00518    return ret;
00519 }
00520 
00521 char *ast_getformatname_multiple(char *buf, size_t size, int format) {
00522 
00523    int x = 0;
00524    unsigned len;
00525    char *end = buf;
00526    char *start = buf;
00527    if (!size) return buf;
00528    snprintf(end, size, "0x%x (", format);
00529    len = strlen(end);
00530    end += len;
00531    size -= len;
00532    start = end;
00533    for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) {
00534       if (AST_FORMAT_LIST[x].visible && (AST_FORMAT_LIST[x].bits & format)) {
00535          snprintf(end, size,"%s|",AST_FORMAT_LIST[x].name);
00536          len = strlen(end);
00537          end += len;
00538          size -= len;
00539       }
00540    }
00541    if (start == end)
00542       snprintf(start, size, "nothing)");
00543    else if (size > 1)
00544       *(end -1) = ')';
00545    return buf;
00546 }
00547 
00548 static struct ast_codec_alias_table {
00549    char *alias;
00550    char *realname;
00551 
00552 } ast_codec_alias_table[] = {
00553    {"slinear","slin"},
00554    {"g723.1","g723"},
00555 };
00556 
00557 static char *ast_expand_codec_alias(char *in) {
00558    int x = 0;
00559 
00560    for (x = 0; x < sizeof(ast_codec_alias_table) / sizeof(struct ast_codec_alias_table) ; x++) {
00561       if(!strcmp(in,ast_codec_alias_table[x].alias))
00562          return ast_codec_alias_table[x].realname;
00563    }
00564    return in;
00565 }
00566 
00567 int ast_getformatbyname(char *name)
00568 {
00569    int x = 0, all = 0, format = 0;
00570 
00571    all = strcasecmp(name, "all") ? 0 : 1;
00572    for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) {
00573       if(AST_FORMAT_LIST[x].visible && (all || 
00574                                 !strcasecmp(AST_FORMAT_LIST[x].name,name) ||
00575                                 !strcasecmp(AST_FORMAT_LIST[x].name,ast_expand_codec_alias(name)))) {
00576          format |= AST_FORMAT_LIST[x].bits;
00577          if(!all)
00578             break;
00579       }
00580    }
00581 
00582    return format;
00583 }
00584 
00585 char *ast_codec2str(int codec) {
00586    int x = 0;
00587    char *ret = "unknown";
00588    for (x = 0 ; x < sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list) ; x++) {
00589       if(AST_FORMAT_LIST[x].visible && AST_FORMAT_LIST[x].bits == codec) {
00590          ret = AST_FORMAT_LIST[x].desc;
00591          break;
00592       }
00593    }
00594    return ret;
00595 }
00596 
00597 static int show_codecs(int fd, int argc, char *argv[])
00598 {
00599    int i, found=0;
00600    char hex[25];
00601    
00602    if ((argc < 2) || (argc > 3))
00603       return RESULT_SHOWUSAGE;
00604 
00605    if (!option_dontwarn)
00606       ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n"
00607             "\tIt does not indicate anything about your configuration.\n");
00608 
00609    ast_cli(fd, "%11s %9s %10s   TYPE   %5s   %s\n","INT","BINARY","HEX","NAME","DESC");
00610    ast_cli(fd, "--------------------------------------------------------------------------------\n");
00611    if ((argc == 2) || (!strcasecmp(argv[1],"audio"))) {
00612       found = 1;
00613       for (i=0;i<11;i++) {
00614          snprintf(hex,25,"(0x%x)",1<<i);
00615          ast_cli(fd, "%11u (1 << %2d) %10s  audio   %5s   (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
00616       }
00617    }
00618 
00619    if ((argc == 2) || (!strcasecmp(argv[1],"image"))) {
00620       found = 1;
00621       for (i=16;i<18;i++) {
00622          snprintf(hex,25,"(0x%x)",1<<i);
00623          ast_cli(fd, "%11u (1 << %2d) %10s  image   %5s   (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
00624       }
00625    }
00626 
00627    if ((argc == 2) || (!strcasecmp(argv[1],"video"))) {
00628       found = 1;
00629       for (i=18;i<21;i++) {
00630          snprintf(hex,25,"(0x%x)",1<<i);
00631          ast_cli(fd, "%11u (1 << %2d) %10s  video   %5s   (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
00632       }
00633    }
00634 
00635    if (! found)
00636       return RESULT_SHOWUSAGE;
00637    else
00638       return RESULT_SUCCESS;
00639 }
00640 
00641 static char frame_show_codecs_usage[] =
00642 "Usage: show [audio|video|image] codecs\n"
00643 "       Displays codec mapping\n";
00644 
00645 static int show_codec_n(int fd, int argc, char *argv[])
00646 {
00647    int codec, i, found=0;
00648 
00649    if (argc != 3)
00650       return RESULT_SHOWUSAGE;
00651 
00652    if (sscanf(argv[2],"%d",&codec) != 1)
00653       return RESULT_SHOWUSAGE;
00654 
00655    for (i=0;i<32;i++)
00656       if (codec & (1 << i)) {
00657          found = 1;
00658          ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(1<<i));
00659       }
00660 
00661    if (! found)
00662       ast_cli(fd, "Codec %d not found\n", codec);
00663 
00664    return RESULT_SUCCESS;
00665 }
00666 
00667 static char frame_show_codec_n_usage[] =
00668 "Usage: show codec <number>\n"
00669 "       Displays codec mapping\n";
00670 
00671 /*! Dump a frame for debugging purposes */
00672 void ast_frame_dump(char *name, struct ast_frame *f, char *prefix)
00673 {
00674    char *n = "unknown";
00675    char ftype[40] = "Unknown Frametype";
00676    char cft[80];
00677    char subclass[40] = "Unknown Subclass";
00678    char csub[80];
00679    char moreinfo[40] = "";
00680    char cn[60];
00681    char cp[40];
00682    char cmn[40];
00683    if (name)
00684       n = name;
00685    if (!f) {
00686       ast_verbose("%s [ %s (NULL) ] [%s]\n", 
00687          term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
00688          term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)), 
00689          term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
00690       return;
00691    }
00692    /* XXX We should probably print one each of voice and video when the format changes XXX */
00693    if (f->frametype == AST_FRAME_VOICE)
00694       return;
00695    if (f->frametype == AST_FRAME_VIDEO)
00696       return;
00697    switch(f->frametype) {
00698    case AST_FRAME_DTMF:
00699       strcpy(ftype, "DTMF");
00700       subclass[0] = f->subclass;
00701       subclass[1] = '\0';
00702       break;
00703    case AST_FRAME_CONTROL:
00704       strcpy(ftype, "Control");
00705       switch(f->subclass) {
00706       case AST_CONTROL_HANGUP:
00707          strcpy(subclass, "Hangup");
00708          break;
00709       case AST_CONTROL_RING:
00710          strcpy(subclass, "Ring");
00711          break;
00712       case AST_CONTROL_RINGING:
00713          strcpy(subclass, "Ringing");
00714          break;
00715       case AST_CONTROL_ANSWER:
00716          strcpy(subclass, "Answer");
00717          break;
00718       case AST_CONTROL_BUSY:
00719          strcpy(subclass, "Busy");
00720          break;
00721       case AST_CONTROL_TAKEOFFHOOK:
00722          strcpy(subclass, "Take Off Hook");
00723          break;
00724       case AST_CONTROL_OFFHOOK:
00725          strcpy(subclass, "Line Off Hook");
00726          break;
00727       case AST_CONTROL_CONGESTION:
00728          strcpy(subclass, "Congestion");
00729          break;
00730       case AST_CONTROL_FLASH:
00731          strcpy(subclass, "Flash");
00732          break;
00733       case AST_CONTROL_WINK:
00734          strcpy(subclass, "Wink");
00735          break;
00736       case AST_CONTROL_OPTION:
00737          strcpy(subclass, "Option");
00738          break;
00739       case AST_CONTROL_RADIO_KEY:
00740          strcpy(subclass, "Key Radio");
00741          break;
00742       case AST_CONTROL_RADIO_UNKEY:
00743          strcpy(subclass, "Unkey Radio");
00744          break;
00745       case -1:
00746          strcpy(subclass, "Stop generators");
00747          break;
00748       default:
00749          snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
00750       }
00751       break;
00752    case AST_FRAME_NULL:
00753       strcpy(ftype, "Null Frame");
00754       strcpy(subclass, "N/A");
00755       break;
00756    case AST_FRAME_IAX:
00757       /* Should never happen */
00758       strcpy(ftype, "IAX Specific");
00759       snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
00760       break;
00761    case AST_FRAME_TEXT:
00762       strcpy(ftype, "Text");
00763       strcpy(subclass, "N/A");
00764       ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
00765       break;
00766    case AST_FRAME_IMAGE:
00767       strcpy(ftype, "Image");
00768       snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass));
00769       break;
00770    case AST_FRAME_HTML:
00771       strcpy(ftype, "HTML");
00772       switch(f->subclass) {
00773       case AST_HTML_URL:
00774          strcpy(subclass, "URL");
00775          ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
00776          break;
00777       case AST_HTML_DATA:
00778          strcpy(subclass, "Data");
00779          break;
00780       case AST_HTML_BEGIN:
00781          strcpy(subclass, "Begin");
00782          break;
00783       case AST_HTML_END:
00784          strcpy(subclass, "End");
00785          break;
00786       case AST_HTML_LDCOMPLETE:
00787          strcpy(subclass, "Load Complete");
00788          break;
00789       case AST_HTML_NOSUPPORT:
00790          strcpy(subclass, "No Support");
00791          break;
00792       case AST_HTML_LINKURL:
00793          strcpy(subclass, "Link URL");
00794          ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
00795          break;
00796       case AST_HTML_UNLINK:
00797          strcpy(subclass, "Unlink");
00798          break;
00799       case AST_HTML_LINKREJECT:
00800          strcpy(subclass, "Link Reject");
00801          break;
00802       default:
00803          snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
00804          break;
00805       }
00806       break;
00807    default:
00808       snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
00809    }
00810    if (!ast_strlen_zero(moreinfo))
00811       ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",  
00812          term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
00813          term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
00814          f->frametype, 
00815          term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
00816          f->subclass, 
00817          term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
00818          term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
00819    else
00820       ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",  
00821          term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
00822          term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
00823          f->frametype, 
00824          term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
00825          f->subclass, 
00826          term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
00827 
00828 }
00829 
00830 
00831 #ifdef TRACE_FRAMES
00832 static int show_frame_stats(int fd, int argc, char *argv[])
00833 {
00834    struct ast_frame *f;
00835    int x=1;
00836    if (argc != 3)
00837       return RESULT_SHOWUSAGE;
00838    ast_mutex_lock(&framelock);
00839    ast_cli(fd, "     Framer Statistics     \n");
00840    ast_cli(fd, "---------------------------\n");
00841    ast_cli(fd, "Total allocated headers: %d\n", headers);
00842    ast_cli(fd, "Queue Dump:\n");
00843    for (f=headerlist; f; f = f->next) {
00844       ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
00845    }
00846    ast_mutex_unlock(&framelock);
00847    return RESULT_SUCCESS;
00848 }
00849 
00850 static char frame_stats_usage[] =
00851 "Usage: show frame stats\n"
00852 "       Displays debugging statistics from framer\n";
00853 #endif
00854 
00855 /* Builtin Asterisk CLI-commands for debugging */
00856 static struct ast_cli_entry my_clis[] = {
00857 { { "show", "codecs", NULL }, show_codecs, "Shows codecs", frame_show_codecs_usage },
00858 { { "show", "audio", "codecs", NULL }, show_codecs, "Shows audio codecs", frame_show_codecs_usage },
00859 { { "show", "video", "codecs", NULL }, show_codecs, "Shows video codecs", frame_show_codecs_usage },
00860 { { "show", "image", "codecs", NULL }, show_codecs, "Shows image codecs", frame_show_codecs_usage },
00861 { { "show", "codec", NULL }, show_codec_n, "Shows a specific codec", frame_show_codec_n_usage },
00862 #ifdef TRACE_FRAMES
00863 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage },
00864 #endif
00865 };
00866 
00867 int init_framer(void)
00868 {
00869    ast_cli_register_multiple(my_clis, sizeof(my_clis)/sizeof(my_clis[0]) );
00870    return 0;   
00871 }
00872 
00873 void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right) 
00874 {
00875    int x = 0, differential = (int) 'A', mem = 0;
00876    char *from = NULL, *to = NULL;
00877 
00878    if(right) {
00879       from = pref->order;
00880       to = buf;
00881       mem = size;
00882    } else {
00883       to = pref->order;
00884       from = buf;
00885       mem = 32;
00886    }
00887 
00888    memset(to, 0, mem);
00889    for (x = 0; x < 32 ; x++) {
00890       if(!from[x])
00891          break;
00892       to[x] = right ? (from[x] + differential) : (from[x] - differential);
00893    }
00894 }
00895 
00896 int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) 
00897 {
00898    int x = 0, codec = 0; 
00899    size_t total_len = 0, slen = 0;
00900    char *formatname = 0;
00901    
00902    memset(buf,0,size);
00903    total_len = size;
00904    buf[0] = '(';
00905    total_len--;
00906    for(x = 0; x < 32 ; x++) {
00907       if(total_len <= 0)
00908          break;
00909       if(!(codec = ast_codec_pref_index(pref,x)))
00910          break;
00911       if((formatname = ast_getformatname(codec))) {
00912          slen = strlen(formatname);
00913          if(slen > total_len)
00914             break;
00915          strncat(buf,formatname,total_len);
00916          total_len -= slen;
00917       }
00918       if(total_len && x < 31 && ast_codec_pref_index(pref , x + 1)) {
00919          strncat(buf,"|",total_len);
00920          total_len--;
00921       }
00922    }
00923    if(total_len) {
00924       strncat(buf,")",total_len);
00925       total_len--;
00926    }
00927 
00928    return size - total_len;
00929 }
00930 
00931 int ast_codec_pref_index(struct ast_codec_pref *pref, int index) 
00932 {
00933    int slot = 0;
00934 
00935    
00936    if((index >= 0) && (index < sizeof(pref->order))) {
00937       slot = pref->order[index];
00938    }
00939 
00940    return slot ? AST_FORMAT_LIST[slot-1].bits : 0;
00941 }
00942 
00943 /*! \brief ast_codec_pref_remove: Remove codec from pref list ---*/
00944 void ast_codec_pref_remove(struct ast_codec_pref *pref, int format)
00945 {
00946    struct ast_codec_pref oldorder;
00947    int x=0, y=0;
00948    size_t size = 0;
00949    int slot = 0;
00950 
00951    if(!pref->order[0])
00952       return;
00953 
00954    size = sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list);
00955 
00956    memcpy(&oldorder,pref,sizeof(struct ast_codec_pref));
00957    memset(pref,0,sizeof(struct ast_codec_pref));
00958 
00959    for (x = 0; x < size; x++) {
00960       slot = oldorder.order[x];
00961       if(! slot)
00962          break;
00963       if(AST_FORMAT_LIST[slot-1].bits != format)
00964          pref->order[y++] = slot;
00965    }
00966    
00967 }
00968 
00969 /*! \brief ast_codec_pref_append: Append codec to list ---*/
00970 int ast_codec_pref_append(struct ast_codec_pref *pref, int format)
00971 {
00972    size_t size = 0;
00973    int x = 0, newindex = -1;
00974 
00975    ast_codec_pref_remove(pref, format);
00976    size = sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list);
00977 
00978    for (x = 0; x < size; x++) {
00979       if(AST_FORMAT_LIST[x].bits == format) {
00980          newindex = x + 1;
00981          break;
00982       }
00983    }
00984 
00985    if(newindex) {
00986       for (x = 0; x < size; x++) {
00987          if(!pref->order[x]) {
00988             pref->order[x] = newindex;
00989             break;
00990          }
00991       }
00992    }
00993 
00994    return x;
00995 }
00996 
00997 
00998 /*! \brief ast_codec_choose: Pick a codec ---*/
00999 int ast_codec_choose(struct ast_codec_pref *pref, int formats, int find_best)
01000 {
01001    size_t size = 0;
01002    int x = 0, ret = 0, slot = 0;
01003 
01004    size = sizeof(AST_FORMAT_LIST) / sizeof(struct ast_format_list);
01005    for (x = 0; x < size; x++) {
01006       slot = pref->order[x];
01007 
01008       if(!slot)
01009          break;
01010       if ( formats & AST_FORMAT_LIST[slot-1].bits ) {
01011          ret = AST_FORMAT_LIST[slot-1].bits;
01012          break;
01013       }
01014    }
01015    if(ret)
01016       return ret;
01017 
01018       return find_best ? ast_best_codec(formats) : 0;
01019 }
01020 
01021 void ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, const char *list, int allowing) 
01022 {
01023    char *parse;
01024    char *this;
01025    int format;
01026 
01027    parse = ast_strdupa(list);
01028    while ((this = strsep(&parse, ","))) {
01029       if (!(format = ast_getformatbyname(this))) {
01030          ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", this);
01031          continue;
01032       }
01033 
01034       if (mask) {
01035          if (allowing)
01036             *mask |= format;
01037          else
01038             *mask &= ~format;
01039       }
01040 
01041       if (pref) {
01042          if (strcasecmp(this, "all")) {
01043             if (allowing)
01044                ast_codec_pref_append(pref, format);
01045             else
01046                ast_codec_pref_remove(pref, format);
01047          } else if (!allowing) {
01048             memset(pref, 0, sizeof(*pref));
01049          }
01050       }
01051    }
01052 }
01053 
01054 static int g723_len(unsigned char buf)
01055 {
01056    switch(buf & TYPE_MASK) {
01057    case TYPE_DONTSEND:
01058       return 0;
01059       break;
01060    case TYPE_SILENCE:
01061       return 4;
01062       break;
01063    case TYPE_HIGH:
01064       return 24;
01065       break;
01066    case TYPE_LOW:
01067       return 20;
01068       break;
01069    default:
01070       ast_log(LOG_WARNING, "Badly encoded frame (%d)\n", buf & TYPE_MASK);
01071    }
01072    return -1;
01073 }
01074 
01075 static int g723_samples(unsigned char *buf, int maxlen)
01076 {
01077    int pos = 0;
01078    int samples = 0;
01079    int res;
01080    while(pos < maxlen) {
01081       res = g723_len(buf[pos]);
01082       if (res <= 0)
01083          break;
01084       samples += 240;
01085       pos += res;
01086    }
01087    return samples;
01088 }
01089 
01090 static unsigned char get_n_bits_at(unsigned char *data, int n, int bit)
01091 {
01092    int byte = bit / 8;       /* byte containing first bit */
01093    int rem = 8 - (bit % 8);  /* remaining bits in first byte */
01094    unsigned char ret = 0;
01095    
01096    if (n <= 0 || n > 8)
01097       return 0;
01098 
01099    if (rem < n) {
01100       ret = (data[byte] << (n - rem));
01101       ret |= (data[byte + 1] >> (8 - n + rem));
01102    } else {
01103       ret = (data[byte] >> (rem - n));
01104    }
01105 
01106    return (ret & (0xff >> (8 - n)));
01107 }
01108 
01109 static int speex_get_wb_sz_at(unsigned char *data, int len, int bit)
01110 {
01111    static int SpeexWBSubModeSz[] = {
01112       0, 36, 112, 192,
01113       352, 0, 0, 0 };
01114    int off = bit;
01115    unsigned char c;
01116 
01117    /* skip up to two wideband frames */
01118    if (((len * 8 - off) >= 5) && 
01119       get_n_bits_at(data, 1, off)) {
01120       c = get_n_bits_at(data, 3, off + 1);
01121       off += SpeexWBSubModeSz[c];
01122 
01123       if (((len * 8 - off) >= 5) && 
01124          get_n_bits_at(data, 1, off)) {
01125          c = get_n_bits_at(data, 3, off + 1);
01126          off += SpeexWBSubModeSz[c];
01127 
01128          if (((len * 8 - off) >= 5) && 
01129             get_n_bits_at(data, 1, off)) {
01130             ast_log(LOG_WARNING, "Encountered corrupt speex frame; too many wideband frames in a row.\n");
01131             return -1;
01132          }
01133       }
01134 
01135    }
01136    return off - bit;
01137 }
01138 
01139 static int speex_samples(unsigned char *data, int len)
01140 {
01141    static int SpeexSubModeSz[] = {
01142                5, 43, 119, 160,
01143       220, 300, 364, 492, 
01144       79, 0, 0, 0,
01145       0, 0, 0, 0 };
01146    static int SpeexInBandSz[] = { 
01147       1, 1, 4, 4,
01148       4, 4, 4, 4,
01149       8, 8, 16, 16,
01150       32, 32, 64, 64 };
01151    int bit = 0;
01152    int cnt = 0;
01153    int off = 0;
01154    unsigned char c;
01155 
01156    while ((len * 8 - bit) >= 5) {
01157       /* skip wideband frames */
01158       off = speex_get_wb_sz_at(data, len, bit);
01159       if (off < 0)  {
01160          ast_log(LOG_WARNING, "Had error while reading wideband frames for speex samples\n");
01161          break;
01162       }
01163       bit += off;
01164 
01165       if ((len * 8 - bit) < 5) {
01166          ast_log(LOG_WARNING, "Not enough bits remaining after wide band for speex samples.\n");
01167          break;
01168       }
01169 
01170       /* get control bits */
01171       c = get_n_bits_at(data, 5, bit);
01172       bit += 5;
01173 
01174       if (c == 15) { 
01175          /* terminator */
01176          break; 
01177       } else if (c == 14) {
01178          /* in-band signal; next 4 bits contain signal id */
01179          c = get_n_bits_at(data, 4, bit);
01180          bit += 4;
01181          bit += SpeexInBandSz[c];
01182       } else if (c == 13) {
01183          /* user in-band; next 5 bits contain msg len */
01184          c = get_n_bits_at(data, 5, bit);
01185          bit += 5;
01186          bit += c * 8;
01187       } else if (c > 8) {
01188          /* unknown */
01189          break;
01190       } else {
01191          /* skip number bits for submode (less the 5 control bits) */
01192          bit += SpeexSubModeSz[c] - 5;
01193          cnt += 160; /* new frame */
01194       }
01195    }
01196    return cnt;
01197 }
01198 
01199 int ast_codec_get_samples(struct ast_frame *f)
01200 {
01201    int samples=0;
01202    switch(f->subclass) {
01203    case AST_FORMAT_SPEEX:
01204       samples = speex_samples(f->data, f->datalen);
01205       break;
01206    case AST_FORMAT_G723_1:
01207                 samples = g723_samples(f->data, f->datalen);
01208       break;
01209    case AST_FORMAT_ILBC:
01210       samples = 240 * (f->datalen / 50);
01211       break;
01212    case AST_FORMAT_GSM:
01213       samples = 160 * (f->datalen / 33);
01214       break;
01215    case AST_FORMAT_G729A:
01216       samples = f->datalen * 8;
01217       break;
01218    case AST_FORMAT_SLINEAR:
01219       samples = f->datalen / 2;
01220       break;
01221    case AST_FORMAT_LPC10:
01222                 /* assumes that the RTP packet contains one LPC10 frame */
01223       samples = 22 * 8;
01224       samples += (((char *)(f->data))[7] & 0x1) * 8;
01225       break;
01226    case AST_FORMAT_ULAW:
01227    case AST_FORMAT_ALAW:
01228       samples = f->datalen;
01229       break;
01230    case AST_FORMAT_ADPCM:
01231    case AST_FORMAT_G726:
01232       samples = f->datalen * 2;
01233       break;
01234    default:
01235       ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(f->subclass));
01236    }
01237    return samples;
01238 }
01239 
01240 int ast_codec_get_len(int format, int samples)
01241 {
01242    int len = 0;
01243 
01244    /* XXX Still need speex, g723, and lpc10 XXX */ 
01245    switch(format) {
01246    case AST_FORMAT_ILBC:
01247       len = (samples / 240) * 50;
01248       break;
01249    case AST_FORMAT_GSM:
01250       len = (samples / 160) * 33;
01251       break;
01252    case AST_FORMAT_G729A:
01253       len = samples / 8;
01254       break;
01255    case AST_FORMAT_SLINEAR:
01256       len = samples * 2;
01257       break;
01258    case AST_FORMAT_ULAW:
01259    case AST_FORMAT_ALAW:
01260       len = samples;
01261       break;
01262    case AST_FORMAT_ADPCM:
01263    case AST_FORMAT_G726:
01264       len = samples / 2;
01265       break;
01266    default:
01267       ast_log(LOG_WARNING, "Unable to calculate sample length for format %s\n", ast_getformatname(format));
01268    }
01269 
01270    return len;
01271 }
01272 
01273 int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
01274 {
01275    int count;
01276    short *fdata = f->data;
01277    short adjust_value = abs(adjustment);
01278 
01279    if ((f->frametype != AST_FRAME_VOICE) || (f->subclass != AST_FORMAT_SLINEAR))
01280       return -1;
01281 
01282    if (!adjustment)
01283       return 0;
01284 
01285    for (count = 0; count < f->samples; count++) {
01286       if (adjustment > 0) {
01287          ast_slinear_saturated_multiply(&fdata[count], &adjust_value);
01288       } else if (adjustment < 0) {
01289          ast_slinear_saturated_divide(&fdata[count], &adjust_value);
01290       }
01291    }
01292 
01293    return 0;
01294 }
01295 
01296 int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
01297 {
01298    int count;
01299    short *data1, *data2;
01300 
01301    if ((f1->frametype != AST_FRAME_VOICE) || (f1->subclass != AST_FORMAT_SLINEAR))
01302       return -1;
01303 
01304    if ((f2->frametype != AST_FRAME_VOICE) || (f2->subclass != AST_FORMAT_SLINEAR))
01305       return -1;
01306 
01307    if (f1->samples != f2->samples)
01308       return -1;
01309 
01310    for (count = 0, data1 = f1->data, data2 = f2->data;
01311         count < f1->samples;
01312         count++, data1++, data2++)
01313       ast_slinear_saturated_add(data1, data2);
01314 
01315    return 0;
01316 }

Generated on Sat Sep 16 05:47:46 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7