Ticket #614: ticket614.patch

File ticket614.patch, 13.9 KB (added by nanang, 16 years ago)

Added support for writing and reading WAV files encoded as 8-bit A-law/U-law in wav_port.

  • pjmedia/include/pjmedia/wav_port.h

     
    142142 */ 
    143143 
    144144 
     145/** 
     146 * WAV file writer options. 
     147 */ 
     148enum pjmedia_file_writer_option 
     149{ 
     150    /** 
     151     * Tell the file writer to save the audio in PCM format. 
     152     */ 
     153    PJMEDIA_FILE_WRITE_PCM = 0, 
    145154 
     155    /** 
     156     * Tell the file writer to save the audio in G711 Alaw format. 
     157     */ 
     158    PJMEDIA_FILE_WRITE_ALAW = 1, 
     159 
     160    /** 
     161     * Tell the file writer to save the audio in G711 Alaw format. 
     162     */ 
     163    PJMEDIA_FILE_WRITE_ULAW = 2, 
     164}; 
     165 
     166 
    146167/** 
    147168 * Create a media port to record streams to a WAV file. Note that the port 
    148169 * must be closed properly (with #pjmedia_port_destroy()) so that the WAV 
     
    154175 * @param channel_count     Number of channels. 
    155176 * @param samples_per_frame Number of samples per frame. 
    156177 * @param bits_per_sample   Number of bits per sample (eg 16). 
    157  * @param flags             Port creation flags (must be 0 at present). 
     178 * @param flags             Port creation flags. 
    158179 * @param buff_size         Buffer size to be allocated. If the value is  
    159180 *                          zero or negative, the port will use default buffer 
    160181 *                          size (which is about 4KB). 
  • pjmedia/include/pjmedia/wave.h

     
    6666 */ 
    6767#define PJMEDIA_DATA_TAG        ('a'<<24|'t'<<16|'a'<<8|'d') 
    6868 
     69/** 
     70 * Standard FACT tag to identify fact chunks. 
     71 */ 
     72#define PJMEDIA_FACT_TAG        ('t'<<24|'c'<<16|'a'<<8|'f') 
    6973 
     74 
    7075/** 
     76 * Enumeration of format compression tag. 
     77 */ 
     78typedef enum { 
     79    PJMEDIA_WAVE_FMT_TAG_PCM    = 1, 
     80    PJMEDIA_WAVE_FMT_TAG_ALAW   = 6, 
     81    PJMEDIA_WAVE_FMT_TAG_ULAW   = 7 
     82} pjmedia_wave_fmt_tag; 
     83 
     84 
     85/** 
    7186 * This file describes the simpler/canonical version of a WAVE file. 
    7287 * It does not support the full RIFF format specification. 
    7388 */ 
  • pjmedia/src/pjmedia/wav_writer.c

     
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    1919#include <pjmedia/wav_port.h> 
     20#include <pjmedia/alaw_ulaw.h> 
    2021#include <pjmedia/errno.h> 
    2122#include <pjmedia/wave.h> 
    2223#include <pj/assert.h> 
     
    2930 
    3031#define THIS_FILE           "wav_writer.c" 
    3132#define SIGNATURE           PJMEDIA_PORT_SIGNATURE('F', 'W', 'R', 'T') 
    32 #define BYTES_PER_SAMPLE    2 
    3333 
    3434 
    3535struct file_port 
    3636{ 
    3737    pjmedia_port     base; 
     38    pjmedia_wave_fmt_tag fmt_tag; 
     39    pj_uint16_t      bytes_per_sample; 
     40 
    3841    pj_size_t        bufsize; 
    3942    char            *buf; 
    4043    char            *writepos; 
     
    7275    pj_str_t name; 
    7376    pj_status_t status; 
    7477 
    75     PJ_UNUSED_ARG(flags); 
    76  
    7778    /* Check arguments. */ 
    7879    PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); 
    7980 
     
    9697    fport->base.put_frame = &file_put_frame; 
    9798    fport->base.on_destroy = &file_on_destroy; 
    9899 
     100    if (flags == PJMEDIA_FILE_WRITE_ALAW) { 
     101        fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_ALAW; 
     102        fport->bytes_per_sample = 1; 
     103    } else if (flags == PJMEDIA_FILE_WRITE_ULAW) { 
     104        fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_ULAW; 
     105        fport->bytes_per_sample = 1; 
     106    } else { 
     107        fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_PCM; 
     108        fport->bytes_per_sample = 2; 
     109    } 
    99110 
    100111    /* Open file in write and read mode. 
    101112     * We need the read mode because we'll modify the WAVE header once 
     
    113124 
    114125    wave_hdr.fmt_hdr.fmt = PJMEDIA_FMT_TAG; 
    115126    wave_hdr.fmt_hdr.len = 16; 
    116     wave_hdr.fmt_hdr.fmt_tag = 1; 
     127    wave_hdr.fmt_hdr.fmt_tag = fport->fmt_tag; 
    117128    wave_hdr.fmt_hdr.nchan = (pj_int16_t)channel_count; 
    118129    wave_hdr.fmt_hdr.sample_rate = sampling_rate; 
    119130    wave_hdr.fmt_hdr.bytes_per_sec = sampling_rate * channel_count *  
    120131                                     bits_per_sample / 8; 
    121     wave_hdr.fmt_hdr.block_align = (pj_int16_t) (channel_count *  
    122                                                  bits_per_sample / 8); 
    123     wave_hdr.fmt_hdr.bits_per_sample = (pj_int16_t)bits_per_sample; 
     132    wave_hdr.fmt_hdr.block_align = fport->bytes_per_sample; 
     133    wave_hdr.fmt_hdr.bits_per_sample = fport->bytes_per_sample * 8; 
    124134 
    125135    wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG; 
    126136    wave_hdr.data_hdr.len = 0;      /* will be filled later */ 
     
    133143 
    134144 
    135145    /* Write WAVE header */ 
    136     size = sizeof(pjmedia_wave_hdr); 
    137     status = pj_file_write(fport->fd, &wave_hdr, &size); 
    138     if (status != PJ_SUCCESS) { 
    139         pj_file_close(fport->fd); 
    140         return status; 
     146    if (fport->fmt_tag != PJMEDIA_WAVE_FMT_TAG_PCM) { 
     147        pjmedia_wave_subchunk fact_chunk; 
     148        pj_uint32_t tmp = 0; 
     149 
     150        fact_chunk.id = PJMEDIA_FACT_TAG; 
     151        fact_chunk.len = 4; 
     152 
     153        PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&fact_chunk); 
     154 
     155        /* Write WAVE header without DATA chunk header */ 
     156        size = sizeof(pjmedia_wave_hdr) - sizeof(wave_hdr.data_hdr); 
     157        status = pj_file_write(fport->fd, &wave_hdr, &size); 
     158        if (status != PJ_SUCCESS) { 
     159            pj_file_close(fport->fd); 
     160            return status; 
     161        } 
     162 
     163        /* Write FACT chunk if it stores compressed data */ 
     164        size = sizeof(fact_chunk); 
     165        status = pj_file_write(fport->fd, &fact_chunk, &size); 
     166        if (status != PJ_SUCCESS) { 
     167            pj_file_close(fport->fd); 
     168            return status; 
     169        } 
     170        size = 4; 
     171        status = pj_file_write(fport->fd, &tmp, &size); 
     172        if (status != PJ_SUCCESS) { 
     173            pj_file_close(fport->fd); 
     174            return status; 
     175        } 
     176 
     177        /* Write DATA chunk header */ 
     178        size = sizeof(wave_hdr.data_hdr); 
     179        status = pj_file_write(fport->fd, &wave_hdr.data_hdr, &size); 
     180        if (status != PJ_SUCCESS) { 
     181            pj_file_close(fport->fd); 
     182            return status; 
     183        } 
     184    } else { 
     185        size = sizeof(pjmedia_wave_hdr); 
     186        status = pj_file_write(fport->fd, &wave_hdr, &size); 
     187        if (status != PJ_SUCCESS) { 
     188            pj_file_close(fport->fd); 
     189            return status; 
     190        } 
    141191    } 
    142192 
    143193    /* Set buffer size. */ 
     
    258308                                  const pjmedia_frame *frame) 
    259309{ 
    260310    struct file_port *fport = (struct file_port *)this_port; 
     311    unsigned frame_size; 
    261312 
     313    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) 
     314        frame_size = frame->size; 
     315    else 
     316        frame_size = frame->size >> 1; 
     317 
    262318    /* Flush buffer if we don't have enough room for the frame. */ 
    263     if (fport->writepos + frame->size > fport->buf + fport->bufsize) { 
     319    if (fport->writepos + frame_size > fport->buf + fport->bufsize) { 
    264320        pj_status_t status; 
    265321        status = flush_buffer(fport); 
    266322        if (status != PJ_SUCCESS) 
     
    268324    } 
    269325 
    270326    /* Check if frame is not too large. */ 
    271     PJ_ASSERT_RETURN(fport->writepos+frame->size <= fport->buf+fport->bufsize, 
     327    PJ_ASSERT_RETURN(fport->writepos+frame_size <= fport->buf+fport->bufsize, 
    272328                     PJMEDIA_EFRMFILETOOBIG); 
    273329 
    274330    /* Copy frame to buffer. */ 
    275     pj_memcpy(fport->writepos, frame->buf, frame->size); 
    276     fport->writepos += frame->size; 
     331    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { 
     332        pj_memcpy(fport->writepos, frame->buf, frame->size); 
     333    } else { 
     334        unsigned i; 
     335        pj_int16_t *src = (pj_int16_t*)frame->buf; 
     336        pj_uint8_t *dst = (pj_uint8_t*)fport->writepos; 
    277337 
     338        if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { 
     339            for (i = 0; i < frame_size; ++i) { 
     340                *dst++ = pjmedia_linear2ulaw(*src++); 
     341            } 
     342        } else { 
     343            for (i = 0; i < frame_size; ++i) { 
     344                *dst++ = pjmedia_linear2alaw(*src++); 
     345            } 
     346        } 
     347 
     348    } 
     349    fport->writepos += frame_size; 
     350 
    278351    /* Increment total written, and check if we need to call callback */ 
    279     fport->total += frame->size; 
     352    fport->total += frame_size; 
    280353    if (fport->cb && fport->total >= fport->cb_size) { 
    281354        pj_status_t (*cb)(pjmedia_port*, void*); 
    282355        pj_status_t status; 
     
    314387    pj_uint32_t wave_file_len; 
    315388    pj_uint32_t wave_data_len; 
    316389    pj_status_t status; 
     390    pj_uint32_t data_len_pos = DATA_LEN_POS; 
    317391 
    318392    /* Flush remaining buffers. */ 
    319393    if (fport->writepos != fport->buf)  
     
    341415    status = pj_file_write(fport->fd, &wave_file_len, &bytes); 
    342416    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
    343417 
     418    /* Write samples_len in FACT chunk */ 
     419    if (fport->fmt_tag != PJMEDIA_WAVE_FMT_TAG_PCM) { 
     420        enum { SAMPLES_LEN_POS = 44 }; 
     421        pj_uint32_t wav_samples_len; 
     422 
     423        /* Adjust wave_data_len & data_len_pos since there is FACT chunk */ 
     424        wave_data_len -= 12; 
     425        data_len_pos += 12; 
     426        wav_samples_len = wave_data_len; 
     427 
     428        /* Seek to samples_len field. */ 
     429        status = pj_file_setpos(fport->fd, SAMPLES_LEN_POS, PJ_SEEK_SET); 
     430        PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
     431 
     432        /* Write samples_len */ 
     433        bytes = sizeof(wav_samples_len); 
     434        status = pj_file_write(fport->fd, &wav_samples_len, &bytes); 
     435        PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
     436    } 
     437 
    344438    /* Seek to data_len field. */ 
    345     status = pj_file_setpos(fport->fd, DATA_LEN_POS, PJ_SEEK_SET); 
     439    status = pj_file_setpos(fport->fd, data_len_pos, PJ_SEEK_SET); 
    346440    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
    347441 
    348442    /* Write file_len */ 
  • pjmedia/src/pjmedia/wav_player.c

     
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    1919#include <pjmedia/wav_port.h> 
     20#include <pjmedia/alaw_ulaw.h> 
    2021#include <pjmedia/errno.h> 
    2122#include <pjmedia/wave.h> 
    2223#include <pj/assert.h> 
     
    3132 
    3233 
    3334#define SIGNATURE           PJMEDIA_PORT_SIGNATURE('F', 'P', 'l', 'y') 
    34 #define BYTES_PER_SAMPLE    2 
     35#define BITS_PER_SAMPLE     16 
    3536 
    36  
    3737#if 1 
    3838#   define TRACE_(x)    PJ_LOG(4,x) 
    3939#else 
     
    5656{ 
    5757    pjmedia_port     base; 
    5858    unsigned         options; 
     59    pjmedia_wave_fmt_tag fmt_tag; 
     60    pj_uint16_t      bytes_per_sample; 
    5961    pj_bool_t        eof; 
    6062    pj_size_t        bufsize; 
    6163    char            *buf; 
     
    145147    } 
    146148 
    147149    /* Convert samples to host rep */ 
    148     samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/BYTES_PER_SAMPLE); 
     150    samples_to_host((pj_int16_t*)fport->buf,  
     151                    fport->bufsize/fport->bytes_per_sample); 
    149152 
    150153    return PJ_SUCCESS; 
    151154} 
     
    165168    pj_ssize_t size_to_read, size_read; 
    166169    struct file_reader_port *fport; 
    167170    pj_off_t pos; 
    168     pj_status_t status; 
     171    pj_status_t status = PJ_SUCCESS; 
    169172 
    170173 
    171174    /* Check arguments. */ 
     
    235238        return PJMEDIA_ENOTVALIDWAVE; 
    236239    } 
    237240 
    238     /* Must be PCM with 16bits per sample */ 
    239     if (wave_hdr.fmt_hdr.fmt_tag != 1 || 
    240         wave_hdr.fmt_hdr.bits_per_sample != 16) 
    241     { 
    242         pj_file_close(fport->fd); 
    243         return PJMEDIA_EWAVEUNSUPP; 
     241    /* Validate format and its attributes (i.e: bits per sample, block align) */ 
     242    switch (wave_hdr.fmt_hdr.fmt_tag) { 
     243    case PJMEDIA_WAVE_FMT_TAG_PCM: 
     244        if (wave_hdr.fmt_hdr.bits_per_sample != 16 ||  
     245            wave_hdr.fmt_hdr.block_align != 2) 
     246            status = PJMEDIA_EWAVEUNSUPP; 
     247        break; 
     248 
     249    case PJMEDIA_WAVE_FMT_TAG_ALAW: 
     250    case PJMEDIA_WAVE_FMT_TAG_ULAW: 
     251        if (wave_hdr.fmt_hdr.bits_per_sample != 8 || 
     252            wave_hdr.fmt_hdr.block_align != 1) 
     253            status = PJMEDIA_ENOTVALIDWAVE; 
     254        break; 
     255 
     256    default: 
     257        status = PJMEDIA_EWAVEUNSUPP; 
     258        break; 
    244259    } 
    245260 
    246     /* Block align must be 2*nchannels */ 
    247     if (wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan*BYTES_PER_SAMPLE) { 
     261    if (status != PJ_SUCCESS) { 
    248262        pj_file_close(fport->fd); 
    249         return PJMEDIA_EWAVEUNSUPP; 
     263        return status; 
    250264    } 
    251265 
     266    fport->fmt_tag = wave_hdr.fmt_hdr.fmt_tag; 
     267    fport->bytes_per_sample = wave_hdr.fmt_hdr.block_align; 
     268 
    252269    /* If length of fmt_header is greater than 16, skip the remaining 
    253270     * fmt header data. 
    254271     */ 
     
    299316        pj_file_close(fport->fd); 
    300317        return PJMEDIA_EWAVEUNSUPP; 
    301318    } 
    302     if (wave_hdr.data_hdr.len < 200) { 
     319    if (wave_hdr.data_hdr.len < ptime * wave_hdr.fmt_hdr.sample_rate * 
     320                                wave_hdr.fmt_hdr.nchan / 1000) 
     321    { 
    303322        pj_file_close(fport->fd); 
    304323        return PJMEDIA_EWAVETOOSHORT; 
    305324    } 
     
    312331    /* Update port info. */ 
    313332    fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan; 
    314333    fport->base.info.clock_rate = wave_hdr.fmt_hdr.sample_rate; 
    315     fport->base.info.bits_per_sample = wave_hdr.fmt_hdr.bits_per_sample; 
     334    fport->base.info.bits_per_sample = BITS_PER_SAMPLE; 
    316335    fport->base.info.samples_per_frame = fport->base.info.clock_rate * 
    317336                                         wave_hdr.fmt_hdr.nchan * 
    318337                                         ptime / 1000; 
     
    337356    /* samples_per_frame must be smaller than bufsize (because get_frame() 
    338357     * doesn't handle this case). 
    339358     */ 
    340     if (fport->base.info.samples_per_frame * BYTES_PER_SAMPLE >= 
     359    if (fport->base.info.samples_per_frame * fport->bytes_per_sample >= 
    341360        fport->bufsize) 
    342361    { 
    343362        pj_file_close(fport->fd); 
     
    523542        fport->eof = PJ_FALSE; 
    524543    } 
    525544 
    526     //frame_size = fport->base.info.bytes_per_frame; 
    527     //pj_assert(frame->size == frame_size); 
    528     frame_size = frame->size; 
     545    //pj_assert(frame->size == fport->base.info.bytes_per_frame); 
     546    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { 
     547        frame_size = frame->size; 
     548        //frame->size = frame_size; 
     549    } else { 
     550        /* Must be ULAW or ALAW */ 
     551        pj_assert(fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW ||  
     552                  fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW); 
    529553 
     554        frame_size = frame->size >> 1; 
     555        frame->size = frame_size << 1; 
     556    } 
     557 
    530558    /* Copy frame from buffer. */ 
    531559    frame->type = PJMEDIA_FRAME_TYPE_AUDIO; 
    532     frame->size = frame_size; 
    533560    frame->timestamp.u64 = 0; 
    534561 
    535562    if ((fport->readpos + frame_size) <= (fport->buf + fport->bufsize)) 
     
    579606        fport->readpos = fport->buf + (frame_size - endread); 
    580607    } 
    581608 
     609    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW || 
     610        fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW) 
     611    { 
     612        unsigned i; 
     613        pj_uint16_t *dst; 
     614        pj_uint8_t *src; 
     615 
     616        dst = (pj_uint16_t*)frame->buf + frame_size - 1; 
     617        src = (pj_uint8_t*)frame->buf + frame_size - 1; 
     618 
     619        if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { 
     620            for (i = 0; i < frame_size; ++i) { 
     621                *dst-- = (pj_uint16_t) pjmedia_ulaw2linear(*src--); 
     622            } 
     623        } else { 
     624            for (i = 0; i < frame_size; ++i) { 
     625                *dst-- = (pj_uint16_t) pjmedia_alaw2linear(*src--); 
     626            } 
     627        } 
     628    } 
     629 
    582630    return PJ_SUCCESS; 
    583631} 
    584632