Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:54 2009

## Asterisk developer's documentation

# format_wav_gsm.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 Save GSM in the proprietary Microsoft format.
    00022  *
    00023  * Microsoft WAV format (Proprietary GSM)
    00024  * \arg File name extension: WAV,wav49  (Upper case WAV, lower case is another format)
    00025  * This format can be played on Windows systems, used for
    00026  * e-mail attachments mainly.
    00027  * \ingroup formats
    00028  */
    00029
    00030 #include <unistd.h>
    00031 #include <netinet/in.h>
    00032 #include <arpa/inet.h>
    00033 #include <stdlib.h>
    00034 #include <sys/time.h>
    00035 #include <stdio.h>
    00036 #include <errno.h>
    00037 #include <string.h>
    00038
    00039 #include "asterisk.h"
    00040
    00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
    00042
    00043 #include "asterisk/lock.h"
    00044 #include "asterisk/channel.h"
    00045 #include "asterisk/file.h"
    00046 #include "asterisk/logger.h"
    00047 #include "asterisk/sched.h"
    00048 #include "asterisk/module.h"
    00049 #include "asterisk/endian.h"
    00050
    00051 #include "msgsm.h"
    00052
    00053 /* Some Ideas for this code came from makewave.c by Jeffrey Chilton */
    00054
    00055 /* Portions of the conversion code are by guido@sienanet.it */
    00056
    00057 /* begin binary data: */
    00058 char msgsm_silence[] = /* 65 */
    00059 {0x48,0x17,0xD6,0x84,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49
    00060 ,0x92,0x24,0x89,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49,0x92
    00061 ,0x24,0x09,0x82,0x74,0x61,0x4D,0x28,0x00,0x48,0x92,0x24,0x49,0x92,0x28,0x00
    00062 ,0x48,0x92,0x24,0x49,0x92,0x28,0x00,0x48,0x92,0x24,0x49,0x92,0x28,0x00,0x48
    00063 ,0x92,0x24,0x49,0x92,0x00};
    00064 /* end binary data. size = 65 bytes */
    00065
    00066 struct ast_filestream {
    00067    void *reserved[AST_RESERVED_POINTERS];
    00068    /* Believe it or not, we must decode/recode to account for the
    00069       weird MS format */
    00070    /* This is what a filestream means to us */
    00071    FILE *f; /* Descriptor */
    00072    struct ast_frame fr;          /* Frame information */
    00073    char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
    00074    char empty;                   /* Empty character */
    00075    unsigned char gsm[66];           /* Two Real GSM Frames */
    00076    int foffset;
    00077    int secondhalf;                  /* Are we on the second half */
    00078    struct timeval last;
    00079 };
    00080
    00081
    00082 AST_MUTEX_DEFINE_STATIC(wav_lock);
    00083 static int glistcnt = 0;
    00084
    00085 static char *name = "wav49";
    00086 static char *desc = "Microsoft WAV format (Proprietary GSM)";
    00087 static char *exts = "WAV|wav49";
    00088
    00089 #if __BYTE_ORDER == __LITTLE_ENDIAN
    00090 #define htoll(b) (b)
    00091 #define htols(b) (b)
    00092 #define ltohl(b) (b)
    00093 #define ltohs(b) (b)
    00094 #else
    00095 #if __BYTE_ORDER == __BIG_ENDIAN
    00096 #define htoll(b)  \
    00097           (((((b)      ) & 0xFF) << 24) | \
    00098           ((((b) >>  8) & 0xFF) << 16) | \
    00099          ((((b) >> 16) & 0xFF) <<  8) | \
    00100          ((((b) >> 24) & 0xFF)      ))
    00101 #define htols(b) \
    00102           (((((b)      ) & 0xFF) << 8) | \
    00103          ((((b) >> 8) & 0xFF)      ))
    00104 #define ltohl(b) htoll(b)
    00105 #define ltohs(b) htols(b)
    00106 #else
    00107 #error "Endianess not defined"
    00108 #endif
    00109 #endif
    00110
    00111
    00112 static int check_header(FILE *f)
    00113 {
    00114    int type, size, formtype;
    00115    int fmt, hsize, fact;
    00116    short format, chans;
    00117    int freq;
    00118    int data;
    00119    if (fread(&type, 1, 4, f) != 4) {
    00120       ast_log(LOG_WARNING, "Read failed (type)\n");
    00121       return -1;
    00122    }
    00123    if (fread(&size, 1, 4, f) != 4) {
    00124       ast_log(LOG_WARNING, "Read failed (size)\n");
    00125       return -1;
    00126    }
    00127    size = ltohl(size);
    00128    if (fread(&formtype, 1, 4, f) != 4) {
    00129       ast_log(LOG_WARNING, "Read failed (formtype)\n");
    00130       return -1;
    00131    }
    00132    if (memcmp(&type, "RIFF", 4)) {
    00133       ast_log(LOG_WARNING, "Does not begin with RIFF\n");
    00134       return -1;
    00135    }
    00136    if (memcmp(&formtype, "WAVE", 4)) {
    00137       ast_log(LOG_WARNING, "Does not contain WAVE\n");
    00138       return -1;
    00139    }
    00140    if (fread(&fmt, 1, 4, f) != 4) {
    00141       ast_log(LOG_WARNING, "Read failed (fmt)\n");
    00142       return -1;
    00143    }
    00144    if (memcmp(&fmt, "fmt ", 4)) {
    00145       ast_log(LOG_WARNING, "Does not say fmt\n");
    00146       return -1;
    00147    }
    00148    if (fread(&hsize, 1, 4, f) != 4) {
    00149       ast_log(LOG_WARNING, "Read failed (formtype)\n");
    00150       return -1;
    00151    }
    00152    if (ltohl(hsize) != 20) {
    00153       ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
    00154       return -1;
    00155    }
    00156    if (fread(&format, 1, 2, f) != 2) {
    00157       ast_log(LOG_WARNING, "Read failed (format)\n");
    00158       return -1;
    00159    }
    00160    if (ltohs(format) != 49) {
    00161       ast_log(LOG_WARNING, "Not a GSM file %d\n", ltohs(format));
    00162       return -1;
    00163    }
    00164    if (fread(&chans, 1, 2, f) != 2) {
    00165       ast_log(LOG_WARNING, "Read failed (format)\n");
    00166       return -1;
    00167    }
    00168    if (ltohs(chans) != 1) {
    00169       ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
    00170       return -1;
    00171    }
    00172    if (fread(&freq, 1, 4, f) != 4) {
    00173       ast_log(LOG_WARNING, "Read failed (freq)\n");
    00174       return -1;
    00175    }
    00176    if (ltohl(freq) != 8000) {
    00177       ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq));
    00178       return -1;
    00179    }
    00180    /* Ignore the byte frequency */
    00181    if (fread(&freq, 1, 4, f) != 4) {
    00182       ast_log(LOG_WARNING, "Read failed (X_1)\n");
    00183       return -1;
    00184    }
    00185    /* Ignore the two weird fields */
    00186    if (fread(&freq, 1, 4, f) != 4) {
    00187       ast_log(LOG_WARNING, "Read failed (X_2/X_3)\n");
    00188       return -1;
    00189    }
    00190    /* Ignore the byte frequency */
    00191    if (fread(&freq, 1, 4, f) != 4) {
    00192       ast_log(LOG_WARNING, "Read failed (Y_1)\n");
    00193       return -1;
    00194    }
    00195    /* Check for the word fact */
    00196    if (fread(&fact, 1, 4, f) != 4) {
    00197       ast_log(LOG_WARNING, "Read failed (fact)\n");
    00198       return -1;
    00199    }
    00200    if (memcmp(&fact, "fact", 4)) {
    00201       ast_log(LOG_WARNING, "Does not say fact\n");
    00202       return -1;
    00203    }
    00204    /* Ignore the "fact value" */
    00205    if (fread(&fact, 1, 4, f) != 4) {
    00206       ast_log(LOG_WARNING, "Read failed (fact header)\n");
    00207       return -1;
    00208    }
    00209    if (fread(&fact, 1, 4, f) != 4) {
    00210       ast_log(LOG_WARNING, "Read failed (fact value)\n");
    00211       return -1;
    00212    }
    00213    /* Check for the word data */
    00214    if (fread(&data, 1, 4, f) != 4) {
    00215       ast_log(LOG_WARNING, "Read failed (data)\n");
    00216       return -1;
    00217    }
    00218    if (memcmp(&data, "data", 4)) {
    00219       ast_log(LOG_WARNING, "Does not say data\n");
    00220       return -1;
    00221    }
    00222    /* Ignore the data length */
    00223    if (fread(&data, 1, 4, f) != 4) {
    00224       ast_log(LOG_WARNING, "Read failed (data)\n");
    00225       return -1;
    00226    }
    00227    return 0;
    00228 }
    00229
    00230 static int update_header(FILE *f)
    00231 {
    00232    off_t cur,end,bytes;
    00233    int datalen,filelen;
    00234
    00235    cur = ftell(f);
    00236    fseek(f, 0, SEEK_END);
    00237    end = ftell(f);
    00238    /* in a gsm WAV, data starts 60 bytes in */
    00239    bytes = end - 60;
    00240    datalen = htoll((bytes + 1) & ~0x1);
    00241    filelen = htoll(52 + ((bytes + 1) & ~0x1));
    00242    if (cur < 0) {
    00243       ast_log(LOG_WARNING, "Unable to find our position\n");
    00244       return -1;
    00245    }
    00246    if (fseek(f, 4, SEEK_SET)) {
    00247       ast_log(LOG_WARNING, "Unable to set our position\n");
    00248       return -1;
    00249    }
    00250    if (fwrite(&filelen, 1, 4, f) != 4) {
    00251       ast_log(LOG_WARNING, "Unable to set write file size\n");
    00252       return -1;
    00253    }
    00254    if (fseek(f, 56, SEEK_SET)) {
    00255       ast_log(LOG_WARNING, "Unable to set our position\n");
    00256       return -1;
    00257    }
    00258    if (fwrite(&datalen, 1, 4, f) != 4) {
    00259       ast_log(LOG_WARNING, "Unable to set write datalen\n");
    00260       return -1;
    00261    }
    00262    if (fseek(f, cur, SEEK_SET)) {
    00263       ast_log(LOG_WARNING, "Unable to return to position\n");
    00264       return -1;
    00265    }
    00266    return 0;
    00267 }
    00268
    00269 static int write_header(FILE *f)
    00270 {
    00271    unsigned int hz=htoll(8000);
    00272    unsigned int bhz = htoll(1625);
    00273    unsigned int hs = htoll(20);
    00274    unsigned short fmt = htols(49);
    00275    unsigned short chans = htols(1);
    00276    unsigned int fhs = htoll(4);
    00277    unsigned int x_1 = htoll(65);
    00278    unsigned short x_2 = htols(2);
    00279    unsigned short x_3 = htols(320);
    00280    unsigned int y_1 = htoll(20160);
    00281    unsigned int size = htoll(0);
    00282    /* Write a GSM header, ignoring sizes which will be filled in later */
    00283    if (fwrite("RIFF", 1, 4, f) != 4) {
    00284       ast_log(LOG_WARNING, "Unable to write header\n");
    00285       return -1;
    00286    }
    00287    if (fwrite(&size, 1, 4, f) != 4) {
    00288       ast_log(LOG_WARNING, "Unable to write header\n");
    00289       return -1;
    00290    }
    00291    if (fwrite("WAVEfmt ", 1, 8, f) != 8) {
    00292       ast_log(LOG_WARNING, "Unable to write header\n");
    00293       return -1;
    00294    }
    00295    if (fwrite(&hs, 1, 4, f) != 4) {
    00296       ast_log(LOG_WARNING, "Unable to write header\n");
    00297       return -1;
    00298    }
    00299    if (fwrite(&fmt, 1, 2, f) != 2) {
    00300       ast_log(LOG_WARNING, "Unable to write header\n");
    00301       return -1;
    00302    }
    00303    if (fwrite(&chans, 1, 2, f) != 2) {
    00304       ast_log(LOG_WARNING, "Unable to write header\n");
    00305       return -1;
    00306    }
    00307    if (fwrite(&hz, 1, 4, f) != 4) {
    00308       ast_log(LOG_WARNING, "Unable to write header\n");
    00309       return -1;
    00310    }
    00311    if (fwrite(&bhz, 1, 4, f) != 4) {
    00312       ast_log(LOG_WARNING, "Unable to write header\n");
    00313       return -1;
    00314    }
    00315    if (fwrite(&x_1, 1, 4, f) != 4) {
    00316       ast_log(LOG_WARNING, "Unable to write header\n");
    00317       return -1;
    00318    }
    00319    if (fwrite(&x_2, 1, 2, f) != 2) {
    00320       ast_log(LOG_WARNING, "Unable to write header\n");
    00321       return -1;
    00322    }
    00323    if (fwrite(&x_3, 1, 2, f) != 2) {
    00324       ast_log(LOG_WARNING, "Unable to write header\n");
    00325       return -1;
    00326    }
    00327    if (fwrite("fact", 1, 4, f) != 4) {
    00328       ast_log(LOG_WARNING, "Unable to write header\n");
    00329       return -1;
    00330    }
    00331    if (fwrite(&fhs, 1, 4, f) != 4) {
    00332       ast_log(LOG_WARNING, "Unable to write header\n");
    00333       return -1;
    00334    }
    00335    if (fwrite(&y_1, 1, 4, f) != 4) {
    00336       ast_log(LOG_WARNING, "Unable to write header\n");
    00337       return -1;
    00338    }
    00339    if (fwrite("data", 1, 4, f) != 4) {
    00340       ast_log(LOG_WARNING, "Unable to write header\n");
    00341       return -1;
    00342    }
    00343    if (fwrite(&size, 1, 4, f) != 4) {
    00344       ast_log(LOG_WARNING, "Unable to write header\n");
    00345       return -1;
    00346    }
    00347    return 0;
    00348 }
    00349
    00350 static struct ast_filestream *wav_open(FILE *f)
    00351 {
    00352    /* We don't have any header to read or anything really, but
    00353       if we did, it would go here.  We also might want to check
    00354       and be sure it's a valid file.  */
    00355    struct ast_filestream *tmp;
    00356    if ((tmp = malloc(sizeof(struct ast_filestream)))) {
    00357       memset(tmp, 0, sizeof(struct ast_filestream));
    00358       if (check_header(f)) {
    00359          free(tmp);
    00360          return NULL;
    00361       }
    00362       if (ast_mutex_lock(&wav_lock)) {
    00363          ast_log(LOG_WARNING, "Unable to lock wav list\n");
    00364          free(tmp);
    00365          return NULL;
    00366       }
    00367       tmp->f = f;
    00368       tmp->fr.data = tmp->gsm;
    00369       tmp->fr.frametype = AST_FRAME_VOICE;
    00370       tmp->fr.subclass = AST_FORMAT_GSM;
    00371       /* datalen will vary for each frame */
    00372       tmp->fr.src = name;
    00373       tmp->fr.mallocd = 0;
    00374       tmp->secondhalf = 0;
    00375       glistcnt++;
    00376       ast_mutex_unlock(&wav_lock);
    00377       ast_update_use_count();
    00378    }
    00379    return tmp;
    00380 }
    00381
    00382 static struct ast_filestream *wav_rewrite(FILE *f, const char *comment)
    00383 {
    00384    /* We don't have any header to read or anything really, but
    00385       if we did, it would go here.  We also might want to check
    00386       and be sure it's a valid file.  */
    00387    struct ast_filestream *tmp;
    00388    if ((tmp = malloc(sizeof(struct ast_filestream)))) {
    00389       memset(tmp, 0, sizeof(struct ast_filestream));
    00390       if (write_header(f)) {
    00391          free(tmp);
    00392          return NULL;
    00393       }
    00394       if (ast_mutex_lock(&wav_lock)) {
    00395          ast_log(LOG_WARNING, "Unable to lock wav list\n");
    00396          free(tmp);
    00397          return NULL;
    00398       }
    00399       tmp->f = f;
    00400       glistcnt++;
    00401       ast_mutex_unlock(&wav_lock);
    00402       ast_update_use_count();
    00403    } else
    00404       ast_log(LOG_WARNING, "Out of memory\n");
    00405    return tmp;
    00406 }
    00407
    00408 static void wav_close(struct ast_filestream *s)
    00409 {
    00410    char zero = 0;
    00411    if (ast_mutex_lock(&wav_lock)) {
    00412       ast_log(LOG_WARNING, "Unable to lock wav list\n");
    00413       return;
    00414    }
    00415    glistcnt--;
    00416    ast_mutex_unlock(&wav_lock);
    00417    ast_update_use_count();
    00418    /* Pad to even length */
    00419    fseek(s->f, 0, SEEK_END);
    00420    if (ftell(s->f) & 0x1)
    00421       fwrite(&zero, 1, 1, s->f);
    00422    fclose(s->f);
    00423    free(s);
    00424    s = NULL;
    00425 }
    00426
    00427 static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
    00428 {
    00429    int res;
    00430    char msdata[66];
    00431    /* Send a frame from the file to the appropriate channel */
    00432
    00433    s->fr.frametype = AST_FRAME_VOICE;
    00434    s->fr.subclass = AST_FORMAT_GSM;
    00435    s->fr.offset = AST_FRIENDLY_OFFSET;
    00436    s->fr.samples = 160;
    00437    s->fr.datalen = 33;
    00438    s->fr.mallocd = 0;
    00439    if (s->secondhalf) {
    00440       /* Just return a frame based on the second GSM frame */
    00441       s->fr.data = s->gsm + 33;
    00442    } else {
    00443       if ((res = fread(msdata, 1, 65, s->f)) != 65) {
    00444          if (res && (res != 1))
    00445             ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
    00446          return NULL;
    00447       }
    00448       /* Convert from MS format to two real GSM frames */
    00449       conv65(msdata, s->gsm);
    00450       s->fr.data = s->gsm;
    00451    }
    00452    s->secondhalf = !s->secondhalf;
    00453    *whennext = 160;
    00454    return &s->fr;
    00455 }
    00456
    00457 static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
    00458 {
    00459    int res;
    00460    char msdata[66];
    00461    int len =0;
    00462    int alreadyms=0;
    00463    if (f->frametype != AST_FRAME_VOICE) {
    00464       ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
    00465       return -1;
    00466    }
    00467    if (f->subclass != AST_FORMAT_GSM) {
    00468       ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
    00469       return -1;
    00470    }
    00471    if (!(f->datalen % 65))
    00472       alreadyms = 1;
    00473    while(len < f->datalen) {
    00474       if (alreadyms) {
    00475          fs->secondhalf = 0;
    00476          if ((res = fwrite(f->data + len, 1, 65, fs->f)) != 65) {
    00477             ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno));
    00478             return -1;
    00479          }
    00480          update_header(fs->f);
    00481          len += 65;
    00482       } else {
    00483          if (fs->secondhalf) {
    00484             memcpy(fs->gsm + 33, f->data + len, 33);
    00485             conv66(fs->gsm, msdata);
    00486             if ((res = fwrite(msdata, 1, 65, fs->f)) != 65) {
    00487                ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno));
    00488                return -1;
    00489             }
    00490             update_header(fs->f);
    00491          } else {
    00492             /* Copy the data and do nothing */
    00493             memcpy(fs->gsm, f->data + len, 33);
    00494          }
    00495          fs->secondhalf = !fs->secondhalf;
    00496          len += 33;
    00497       }
    00498    }
    00499    return 0;
    00500 }
    00501
    00502 static int wav_seek(struct ast_filestream *fs, long sample_offset, int whence)
    00503 {
    00504    off_t offset=0,distance,cur,min,max;
    00505    min = 60;
    00506    cur = ftell(fs->f);
    00507    fseek(fs->f, 0, SEEK_END);
    00508    max = ftell(fs->f);
    00509    /* I'm getting sloppy here, I'm only going to go to even splits of the 2
    00510     * frames, if you want tighter cuts use format_gsm, format_pcm, or format_wav */
    00511    distance = (sample_offset/320) * 65;
    00512    if(whence == SEEK_SET)
    00513       offset = distance + min;
    00514    else if(whence == SEEK_CUR || whence == SEEK_FORCECUR)
    00515       offset = distance + cur;
    00516    else if(whence == SEEK_END)
    00517       offset = max - distance;
    00518    /* always protect against seeking past end of header */
    00519    offset = (offset < min)?min:offset;
    00520    if (whence != SEEK_FORCECUR) {
    00521       offset = (offset > max)?max:offset;
    00522    } else if (offset > max) {
    00523       int i;
    00524       fseek(fs->f, 0, SEEK_END);
    00525       for (i=0; i< (offset - max) / 65; i++) {
    00526          fwrite(msgsm_silence, 1, 65, fs->f);
    00527       }
    00528    }
    00529    fs->secondhalf = 0;
    00530    return fseek(fs->f, offset, SEEK_SET);
    00531 }
    00532
    00533 static int wav_trunc(struct ast_filestream *fs)
    00534 {
    00535    if (ftruncate(fileno(fs->f), ftell(fs->f)))
    00536       return -1;
    00537    return update_header(fs->f);
    00538 }
    00539
    00540 static long wav_tell(struct ast_filestream *fs)
    00541 {
    00542    off_t offset;
    00543    offset = ftell(fs->f);
    00544    /* since this will most likely be used later in play or record, lets stick
    00545     * to that level of resolution, just even frames boundaries */
    00546    return (offset - 52)/65*320;
    00547 }
    00548
    00549 static char *wav_getcomment(struct ast_filestream *s)
    00550 {
    00551    return NULL;
    00552 }
    00553
    00554 int load_module()
    00555 {
    00556    return ast_format_register(name, exts, AST_FORMAT_GSM,
    00557                         wav_open,
    00558                         wav_rewrite,
    00559                         wav_write,
    00560                         wav_seek,
    00561                         wav_trunc,
    00562                         wav_tell,
    00563                         wav_read,
    00564                         wav_close,
    00565                         wav_getcomment);
    00566
    00567
    00568 }
    00569
    00570 int unload_module()
    00571 {
    00572    return ast_format_unregister(name);
    00573 }
    00574
    00575 int usecount()
    00576 {
    00577    return glistcnt;
    00578 }
    00579
    00580 char *description()
    00581 {
    00582    return desc;
    00583 }
    00584
    00585
    00586 char *key()
    00587 {
    00588    return ASTERISK_GPL_KEY;
    00589 }