Sat Aug 6 00:39:28 2011

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 "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 233782 $")
00033 
00034 #include <unistd.h>
00035 #include <netinet/in.h>
00036 #include <arpa/inet.h>
00037 #include <stdlib.h>
00038 #include <sys/time.h>
00039 #include <stdio.h>
00040 #include <errno.h>
00041 #include <string.h>
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 #define  GSM_FRAME_SIZE 33
00058 #define  MSGSM_FRAME_SIZE  65
00059 #define  MSGSM_DATA_OFFSET    60 /* offset of data bytes */
00060 #define  GSM_SAMPLES    160   /* samples in a GSM block */
00061 #define  MSGSM_SAMPLES     (2*GSM_SAMPLES)   /* samples in an MSGSM block */
00062 
00063 /* begin binary data: */
00064 char msgsm_silence[] = /* 65 */
00065 {0x48,0x17,0xD6,0x84,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49
00066 ,0x92,0x24,0x89,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49,0x92
00067 ,0x24,0x09,0x82,0x74,0x61,0x4D,0x28,0x00,0x48,0x92,0x24,0x49,0x92,0x28,0x00
00068 ,0x48,0x92,0x24,0x49,0x92,0x28,0x00,0x48,0x92,0x24,0x49,0x92,0x28,0x00,0x48
00069 ,0x92,0x24,0x49,0x92,0x00};
00070 /* end binary data. size = 65 bytes */
00071 
00072 struct wavg_desc {
00073    /* Believe it or not, we must decode/recode to account for the
00074       weird MS format */
00075    int secondhalf;                  /* Are we on the second half */
00076 };
00077 
00078 #if __BYTE_ORDER == __LITTLE_ENDIAN
00079 #define htoll(b) (b)
00080 #define htols(b) (b)
00081 #define ltohl(b) (b)
00082 #define ltohs(b) (b)
00083 #else
00084 #if __BYTE_ORDER == __BIG_ENDIAN
00085 #define htoll(b)  \
00086           (((((b)      ) & 0xFF) << 24) | \
00087           ((((b) >>  8) & 0xFF) << 16) | \
00088          ((((b) >> 16) & 0xFF) <<  8) | \
00089          ((((b) >> 24) & 0xFF)      ))
00090 #define htols(b) \
00091           (((((b)      ) & 0xFF) << 8) | \
00092          ((((b) >> 8) & 0xFF)      ))
00093 #define ltohl(b) htoll(b)
00094 #define ltohs(b) htols(b)
00095 #else
00096 #error "Endianess not defined"
00097 #endif
00098 #endif
00099 
00100 
00101 static int check_header(FILE *f)
00102 {
00103    int type, size, formtype;
00104    int fmt, hsize, fact;
00105    short format, chans;
00106    int freq;
00107    int data;
00108    if (fread(&type, 1, 4, f) != 4) {
00109       ast_log(LOG_WARNING, "Read failed (type)\n");
00110       return -1;
00111    }
00112    if (fread(&size, 1, 4, f) != 4) {
00113       ast_log(LOG_WARNING, "Read failed (size)\n");
00114       return -1;
00115    }
00116    size = ltohl(size);
00117    if (fread(&formtype, 1, 4, f) != 4) {
00118       ast_log(LOG_WARNING, "Read failed (formtype)\n");
00119       return -1;
00120    }
00121    if (memcmp(&type, "RIFF", 4)) {
00122       ast_log(LOG_WARNING, "Does not begin with RIFF\n");
00123       return -1;
00124    }
00125    if (memcmp(&formtype, "WAVE", 4)) {
00126       ast_log(LOG_WARNING, "Does not contain WAVE\n");
00127       return -1;
00128    }
00129    if (fread(&fmt, 1, 4, f) != 4) {
00130       ast_log(LOG_WARNING, "Read failed (fmt)\n");
00131       return -1;
00132    }
00133    if (memcmp(&fmt, "fmt ", 4)) {
00134       ast_log(LOG_WARNING, "Does not say fmt\n");
00135       return -1;
00136    }
00137    if (fread(&hsize, 1, 4, f) != 4) {
00138       ast_log(LOG_WARNING, "Read failed (formtype)\n");
00139       return -1;
00140    }
00141    if (ltohl(hsize) != 20) {
00142       ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
00143       return -1;
00144    }
00145    if (fread(&format, 1, 2, f) != 2) {
00146       ast_log(LOG_WARNING, "Read failed (format)\n");
00147       return -1;
00148    }
00149    if (ltohs(format) != 49) {
00150       ast_log(LOG_WARNING, "Not a GSM file %d\n", ltohs(format));
00151       return -1;
00152    }
00153    if (fread(&chans, 1, 2, f) != 2) {
00154       ast_log(LOG_WARNING, "Read failed (format)\n");
00155       return -1;
00156    }
00157    if (ltohs(chans) != 1) {
00158       ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
00159       return -1;
00160    }
00161    if (fread(&freq, 1, 4, f) != 4) {
00162       ast_log(LOG_WARNING, "Read failed (freq)\n");
00163       return -1;
00164    }
00165    if (ltohl(freq) != DEFAULT_SAMPLE_RATE) {
00166       ast_log(LOG_WARNING, "Unexpected frequency %d\n", ltohl(freq));
00167       return -1;
00168    }
00169    /* Ignore the byte frequency */
00170    if (fread(&freq, 1, 4, f) != 4) {
00171       ast_log(LOG_WARNING, "Read failed (X_1)\n");
00172       return -1;
00173    }
00174    /* Ignore the two weird fields */
00175    if (fread(&freq, 1, 4, f) != 4) {
00176       ast_log(LOG_WARNING, "Read failed (X_2/X_3)\n");
00177       return -1;
00178    }
00179    /* Ignore the byte frequency */
00180    if (fread(&freq, 1, 4, f) != 4) {
00181       ast_log(LOG_WARNING, "Read failed (Y_1)\n");
00182       return -1;
00183    }
00184    /* Check for the word fact */
00185    if (fread(&fact, 1, 4, f) != 4) {
00186       ast_log(LOG_WARNING, "Read failed (fact)\n");
00187       return -1;
00188    }
00189    if (memcmp(&fact, "fact", 4)) {
00190       ast_log(LOG_WARNING, "Does not say fact\n");
00191       return -1;
00192    }
00193    /* Ignore the "fact value" */
00194    if (fread(&fact, 1, 4, f) != 4) {
00195       ast_log(LOG_WARNING, "Read failed (fact header)\n");
00196       return -1;
00197    }
00198    if (fread(&fact, 1, 4, f) != 4) {
00199       ast_log(LOG_WARNING, "Read failed (fact value)\n");
00200       return -1;
00201    }
00202    /* Check for the word data */
00203    if (fread(&data, 1, 4, f) != 4) {
00204       ast_log(LOG_WARNING, "Read failed (data)\n");
00205       return -1;
00206    }
00207    if (memcmp(&data, "data", 4)) {
00208       ast_log(LOG_WARNING, "Does not say data\n");
00209       return -1;
00210    }
00211    /* Ignore the data length */
00212    if (fread(&data, 1, 4, f) != 4) {
00213       ast_log(LOG_WARNING, "Read failed (data)\n");
00214       return -1;
00215    }
00216    return 0;
00217 }
00218 
00219 static int update_header(FILE *f)
00220 {
00221    off_t cur,end,bytes;
00222    int datalen, filelen, samples;
00223 
00224    cur = ftello(f);
00225    fseek(f, 0, SEEK_END);
00226    end = ftello(f);
00227    /* in a gsm WAV, data starts 60 bytes in */
00228    bytes = end - MSGSM_DATA_OFFSET;
00229    samples = htoll(bytes / MSGSM_FRAME_SIZE * MSGSM_SAMPLES);
00230    datalen = htoll(bytes);
00231    filelen = htoll(MSGSM_DATA_OFFSET - 8 + bytes);
00232    if (cur < 0) {
00233       ast_log(LOG_WARNING, "Unable to find our position\n");
00234       return -1;
00235    }
00236    if (fseek(f, 4, SEEK_SET)) {
00237       ast_log(LOG_WARNING, "Unable to set our position\n");
00238       return -1;
00239    }
00240    if (fwrite(&filelen, 1, 4, f) != 4) {
00241       ast_log(LOG_WARNING, "Unable to write file size\n");
00242       return -1;
00243    }
00244    if (fseek(f, 48, SEEK_SET)) {
00245       ast_log(LOG_WARNING, "Unable to set our position\n");
00246       return -1;
00247    }
00248    if (fwrite(&samples, 1, 4, f) != 4) {
00249       ast_log(LOG_WARNING, "Unable to write samples\n");
00250       return -1;
00251    }
00252    if (fseek(f, 56, SEEK_SET)) {
00253       ast_log(LOG_WARNING, "Unable to set our position\n");
00254       return -1;
00255    }
00256    if (fwrite(&datalen, 1, 4, f) != 4) {
00257       ast_log(LOG_WARNING, "Unable to write datalen\n");
00258       return -1;
00259    }
00260    if (fseeko(f, cur, SEEK_SET)) {
00261       ast_log(LOG_WARNING, "Unable to return to position\n");
00262       return -1;
00263    }
00264    return 0;
00265 }
00266 
00267 static int write_header(FILE *f)
00268 {
00269    /* Samples per second (always 8000 for this format). */
00270    unsigned int sample_rate = htoll(8000);
00271    /* Bytes per second (always 1625 for this format). */
00272    unsigned int byte_sample_rate = htoll(1625);
00273    /* This is the size of the "fmt " subchunk */
00274    unsigned int fmtsize = htoll(20);
00275    /* WAV #49 */
00276    unsigned short fmt = htols(49);
00277    /* Mono = 1 channel */
00278    unsigned short chans = htols(1);
00279    /* Each block of data is exactly 65 bytes in size. */
00280    unsigned int block_align = htoll(MSGSM_FRAME_SIZE);
00281    /* Not actually 2, but rounded up to the nearest bit */
00282    unsigned short bits_per_sample = htols(2);
00283    /* Needed for compressed formats */
00284    unsigned short extra_format = htols(MSGSM_SAMPLES);
00285    /* This is the size of the "fact" subchunk */
00286    unsigned int factsize = htoll(4);
00287    /* Number of samples in the data chunk */
00288    unsigned int num_samples = htoll(0);
00289    /* Number of bytes in the data chunk */
00290    unsigned int size = htoll(0);
00291    /* Write a GSM header, ignoring sizes which will be filled in later */
00292 
00293    /*  0: Chunk ID */
00294    if (fwrite("RIFF", 1, 4, f) != 4) {
00295       ast_log(LOG_WARNING, "Unable to write header\n");
00296       return -1;
00297    }
00298    /*  4: Chunk Size */
00299    if (fwrite(&size, 1, 4, f) != 4) {
00300       ast_log(LOG_WARNING, "Unable to write header\n");
00301       return -1;
00302    }
00303    /*  8: Chunk Format */
00304    if (fwrite("WAVE", 1, 4, f) != 4) {
00305       ast_log(LOG_WARNING, "Unable to write header\n");
00306       return -1;
00307    }
00308    /* 12: Subchunk 1: ID */
00309    if (fwrite("fmt ", 1, 4, f) != 4) {
00310       ast_log(LOG_WARNING, "Unable to write header\n");
00311       return -1;
00312    }
00313    /* 16: Subchunk 1: Size (minus 8) */
00314    if (fwrite(&fmtsize, 1, 4, f) != 4) {
00315       ast_log(LOG_WARNING, "Unable to write header\n");
00316       return -1;
00317    }
00318    /* 20: Subchunk 1: Audio format (49) */
00319    if (fwrite(&fmt, 1, 2, f) != 2) {
00320       ast_log(LOG_WARNING, "Unable to write header\n");
00321       return -1;
00322    }
00323    /* 22: Subchunk 1: Number of channels */
00324    if (fwrite(&chans, 1, 2, f) != 2) {
00325       ast_log(LOG_WARNING, "Unable to write header\n");
00326       return -1;
00327    }
00328    /* 24: Subchunk 1: Sample rate */
00329    if (fwrite(&sample_rate, 1, 4, f) != 4) {
00330       ast_log(LOG_WARNING, "Unable to write header\n");
00331       return -1;
00332    }
00333    /* 28: Subchunk 1: Byte rate */
00334    if (fwrite(&byte_sample_rate, 1, 4, f) != 4) {
00335       ast_log(LOG_WARNING, "Unable to write header\n");
00336       return -1;
00337    }
00338    /* 32: Subchunk 1: Block align */
00339    if (fwrite(&block_align, 1, 4, f) != 4) {
00340       ast_log(LOG_WARNING, "Unable to write header\n");
00341       return -1;
00342    }
00343    /* 36: Subchunk 1: Bits per sample */
00344    if (fwrite(&bits_per_sample, 1, 2, f) != 2) {
00345       ast_log(LOG_WARNING, "Unable to write header\n");
00346       return -1;
00347    }
00348    /* 38: Subchunk 1: Extra format bytes */
00349    if (fwrite(&extra_format, 1, 2, f) != 2) {
00350       ast_log(LOG_WARNING, "Unable to write header\n");
00351       return -1;
00352    }
00353    /* 40: Subchunk 2: ID */
00354    if (fwrite("fact", 1, 4, f) != 4) {
00355       ast_log(LOG_WARNING, "Unable to write header\n");
00356       return -1;
00357    }
00358    /* 44: Subchunk 2: Size (minus 8) */
00359    if (fwrite(&factsize, 1, 4, f) != 4) {
00360       ast_log(LOG_WARNING, "Unable to write header\n");
00361       return -1;
00362    }
00363    /* 48: Subchunk 2: Number of samples */
00364    if (fwrite(&num_samples, 1, 4, f) != 4) {
00365       ast_log(LOG_WARNING, "Unable to write header\n");
00366       return -1;
00367    }
00368    /* 52: Subchunk 3: ID */
00369    if (fwrite("data", 1, 4, f) != 4) {
00370       ast_log(LOG_WARNING, "Unable to write header\n");
00371       return -1;
00372    }
00373    /* 56: Subchunk 3: Size */
00374    if (fwrite(&size, 1, 4, f) != 4) {
00375       ast_log(LOG_WARNING, "Unable to write header\n");
00376       return -1;
00377    }
00378    return 0;
00379 }
00380 
00381 static int wav_open(struct ast_filestream *s)
00382 {
00383    /* We don't have any header to read or anything really, but
00384       if we did, it would go here.  We also might want to check
00385       and be sure it's a valid file.  */
00386    struct wavg_desc *fs = (struct wavg_desc *)s->_private;
00387 
00388    if (check_header(s->f))
00389       return -1;
00390    fs->secondhalf = 0;  /* not strictly necessary */
00391    return 0;
00392 }
00393 
00394 static int wav_rewrite(struct ast_filestream *s, const char *comment)
00395 {
00396    /* We don't have any header to read or anything really, but
00397       if we did, it would go here.  We also might want to check
00398       and be sure it's a valid file.  */
00399 
00400    if (write_header(s->f))
00401       return -1;
00402    return 0;
00403 }
00404 
00405 static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
00406 {
00407    /* Send a frame from the file to the appropriate channel */
00408    struct wavg_desc *fs = (struct wavg_desc *)s->_private;
00409 
00410    s->fr.frametype = AST_FRAME_VOICE;
00411    s->fr.subclass = AST_FORMAT_GSM;
00412    s->fr.offset = AST_FRIENDLY_OFFSET;
00413    s->fr.samples = GSM_SAMPLES;
00414    s->fr.mallocd = 0;
00415    AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE);
00416    if (fs->secondhalf) {
00417       /* Just return a frame based on the second GSM frame */
00418       s->fr.data = (char *)s->fr.data + GSM_FRAME_SIZE;
00419       s->fr.offset += GSM_FRAME_SIZE;
00420    } else {
00421       /* read and convert */
00422       unsigned char msdata[MSGSM_FRAME_SIZE];
00423       int res;
00424       
00425       if ((res = fread(msdata, 1, MSGSM_FRAME_SIZE, s->f)) != MSGSM_FRAME_SIZE) {
00426          if (res && (res != 1))
00427             ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
00428          return NULL;
00429       }
00430       /* Convert from MS format to two real GSM frames */
00431       conv65(msdata, s->fr.data);
00432    }
00433    fs->secondhalf = !fs->secondhalf;
00434    *whennext = GSM_SAMPLES;
00435    return &s->fr;
00436 }
00437 
00438 static int wav_write(struct ast_filestream *s, struct ast_frame *f)
00439 {
00440    int len;
00441    int size;
00442    struct wavg_desc *fs = (struct wavg_desc *)s->_private;
00443 
00444    if (f->frametype != AST_FRAME_VOICE) {
00445       ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
00446       return -1;
00447    }
00448    if (f->subclass != AST_FORMAT_GSM) {
00449       ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
00450       return -1;
00451    }
00452    /* XXX this might fail... if the input is a multiple of MSGSM_FRAME_SIZE
00453     * we assume it is already in the correct format.
00454     */
00455    if (!(f->datalen % MSGSM_FRAME_SIZE)) {
00456       size = MSGSM_FRAME_SIZE;
00457       fs->secondhalf = 0;
00458    } else {
00459       size = GSM_FRAME_SIZE;
00460    }
00461    for (len = 0; len < f->datalen ; len += size) {
00462       int res;
00463       unsigned char *src, msdata[MSGSM_FRAME_SIZE];
00464       if (fs->secondhalf) {   /* second half of raw gsm to be converted */
00465          memcpy(s->buf + GSM_FRAME_SIZE, f->data + len, GSM_FRAME_SIZE);
00466          conv66((unsigned char *) s->buf, msdata);
00467          src = msdata;
00468          fs->secondhalf = 0;
00469       } else if (size == GSM_FRAME_SIZE) {   /* first half of raw gsm */
00470          memcpy(s->buf, f->data + len, GSM_FRAME_SIZE);
00471          src = NULL; /* nothing to write */
00472          fs->secondhalf = 1;
00473       } else { /* raw msgsm data */
00474          src = f->data + len;
00475       }
00476       if (src && (res = fwrite(src, 1, MSGSM_FRAME_SIZE, s->f)) != MSGSM_FRAME_SIZE) {
00477          ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno));
00478          return -1;
00479       }
00480       update_header(s->f); /* XXX inefficient! */
00481    }
00482    return 0;
00483 }
00484 
00485 static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
00486 {
00487    off_t offset=0, distance, max;
00488    struct wavg_desc *s = (struct wavg_desc *)fs->_private;
00489 
00490    off_t min = MSGSM_DATA_OFFSET;
00491    off_t cur = ftello(fs->f);
00492    fseek(fs->f, 0, SEEK_END);
00493    max = ftello(fs->f); /* XXX ideally, should round correctly */
00494    /* Compute the distance in bytes, rounded to the block size */
00495    distance = (sample_offset/MSGSM_SAMPLES) * MSGSM_FRAME_SIZE;
00496    if (whence == SEEK_SET)
00497       offset = distance + min;
00498    else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
00499       offset = distance + cur;
00500    else if (whence == SEEK_END)
00501       offset = max - distance;
00502    /* always protect against seeking past end of header */
00503    if (offset < min)
00504       offset = min;
00505    if (whence != SEEK_FORCECUR) {
00506       if (offset > max)
00507          offset = max;
00508    } else if (offset > max) {
00509       int i;
00510       fseek(fs->f, 0, SEEK_END);
00511       for (i=0; i< (offset - max) / MSGSM_FRAME_SIZE; i++) {
00512          if (!fwrite(msgsm_silence, 1, MSGSM_FRAME_SIZE, fs->f)) {
00513             ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00514          }
00515       }
00516    }
00517    s->secondhalf = 0;
00518    return fseeko(fs->f, offset, SEEK_SET);
00519 }
00520 
00521 static int wav_trunc(struct ast_filestream *fs)
00522 {
00523    if (ftruncate(fileno(fs->f), ftello(fs->f)))
00524       return -1;
00525    return update_header(fs->f);
00526 }
00527 
00528 static off_t wav_tell(struct ast_filestream *fs)
00529 {
00530    off_t offset;
00531    offset = ftello(fs->f);
00532    /* since this will most likely be used later in play or record, lets stick
00533     * to that level of resolution, just even frames boundaries */
00534    return (offset - MSGSM_DATA_OFFSET)/MSGSM_FRAME_SIZE*MSGSM_SAMPLES;
00535 }
00536 
00537 static const struct ast_format wav49_f = {
00538    .name = "wav49",
00539    .exts = "WAV|wav49",
00540    .format = AST_FORMAT_GSM,
00541    .open =  wav_open,
00542    .rewrite = wav_rewrite,
00543    .write = wav_write,
00544    .seek = wav_seek,
00545    .trunc = wav_trunc,
00546    .tell = wav_tell,
00547    .read = wav_read,
00548    .buf_size = 2*GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET,
00549    .desc_size = sizeof(struct wavg_desc),
00550 };
00551 
00552 static int load_module(void)
00553 {
00554    return ast_format_register(&wav49_f);
00555 }
00556 
00557 static int unload_module(void)
00558 {
00559    return ast_format_unregister(wav49_f.name);
00560 }  
00561 
00562 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_FIRST, "Microsoft WAV format (Proprietary GSM)",
00563    .load = load_module,
00564    .unload = unload_module,
00565 );

Generated on Sat Aug 6 00:39:28 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7