From 58f33c855661054c974026cd1f16998ab079631b Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Thu, 21 Dec 2006 07:52:05 +0000 Subject: [PATCH] Second, much improved revision of adaptive jitter buffering patch. Changes ------- 1. Make the AmJitterBuffer work with variable size RTP packets. Packet size can be changed even during session (ex. Cisco in fax passthrough mode). Also several improvements and fixes have been made to resyncronization logic. 2. Fix made to the AmPlayoutBuffer class to avoid reading chunks of size larger than requested. This is required in cases when RTP packets contain more or less data than internal frame size. 3. Small fix to AmRtpPacket class - replace the pointer to internal buffer with offset in the buffer. This eliminates the nesessity to reparse the packet each time the packet has been copied. 4. Replace the sample size field in amci_codec_t structure with two functions - sampes2bytes and bytes2samples as that field did not allow to specify sample size for LBR codecs (iLBC, gsm). This also brings ability for codecs to determine the sample size at runtime (ex. iLBC). 5. Remove the sample size from amci_file_desc_t structure as it was used as internal attribute of WAV files only and doesn't make much sense for other file formats. Use the codec's ability to calculate sample size instead. 6. Parameter list for amci_inoutfmt_t.on_close() has been changed to give ability to determine sample size in this file handler (WAV write_header procedure requires this). 7. Fix gsm, ilbc, wav plugins and AmPlugin.c to reflect changes to amci. Add corresponding samples2bytes and bytes2samples functions. Caveats -------- 1. AmAdaptivePlayout class needs additional checking with RTP streams with packets containing number of samples different from internal frame size (for example 240 samples per packet in G711). Adaptive playout class potentially may produce big packets of audio and therefore make the Conference application work badly. The adaptive playout is used in Conference application only and the application is working fine now with jitter buffer and without adaptive playout. So I turned the adaptive playout off in the Conference app as a workaround. Developed by: Sippy Software, Inc. Sponsored by: Digifonica Canada Limited git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@185 8eb893ce-cfd4-0310-b710-fb5ebe64c474 --- apps/conference/Conference.cpp | 2 +- core/AmAudio.cpp | 42 +++++---- core/AmAudio.h | 14 ++- core/AmJitterBuffer.cpp | 153 ++++++++++++++++++++++++--------- core/AmJitterBuffer.h | 50 ++++++----- core/AmPlayoutBuffer.cpp | 11 +-- core/AmPlugIn.cpp | 32 ++++++- core/AmRtpAudio.cpp | 95 +++++++++++--------- core/AmRtpAudio.h | 4 +- core/AmRtpPacket.cpp | 17 ++-- core/AmRtpPacket.h | 6 +- core/AmRtpStream.cpp | 45 ++++------ core/AmRtpStream.h | 9 +- core/amci/amci.h | 55 +++++++++--- core/plug-in/gsm/gsm.c | 19 +++- core/plug-in/ilbc/ilbc.c | 34 ++++++-- core/plug-in/wav/wav.c | 19 +++- core/plug-in/wav/wav_hdr.c | 22 +++-- core/plug-in/wav/wav_hdr.h | 2 +- 19 files changed, 411 insertions(+), 220 deletions(-) diff --git a/apps/conference/Conference.cpp b/apps/conference/Conference.cpp index 0d652a25..59ae880d 100644 --- a/apps/conference/Conference.cpp +++ b/apps/conference/Conference.cpp @@ -110,7 +110,7 @@ ConferenceDialog::ConferenceDialog(const string& conf_id, allow_dialout(false) { dialedout = this->dialout_channel.get() != 0; - rtp_str.setAdaptivePlayout(true); +// rtp_str.setAdaptivePlayout(true); } ConferenceDialog::~ConferenceDialog() diff --git a/core/AmAudio.cpp b/core/AmAudio.cpp index aa4309ba..0e615060 100644 --- a/core/AmAudio.cpp +++ b/core/AmAudio.cpp @@ -46,7 +46,6 @@ AmAudioRtpFormat::AmAudioRtpFormat(int payload, string format_parameters) amci_payload_t* pl = getPayloadP(); if(pl && codec){ - sample = codec->sample_size; channels = pl->channels; rate = pl->sample_rate; } else { @@ -55,7 +54,7 @@ AmAudioRtpFormat::AmAudioRtpFormat(int payload, string format_parameters) } AmAudioFormat::AmAudioFormat() - : sample(-1), channels(-1), rate(-1), codec(0), + : channels(-1), rate(-1), codec(0), frame_length(20), frame_size(160), frame_encoded_size(320) { @@ -65,7 +64,6 @@ AmAudioSimpleFormat::AmAudioSimpleFormat(int codec_id) : AmAudioFormat(), codec_id(codec_id) { codec = getCodec(); - sample = codec->sample_size; rate = 8000; channels = 1; } @@ -77,7 +75,6 @@ AmAudioFileFormat::AmAudioFileFormat(const string& name, int subtype) codec = getCodec(); if(p_subtype && codec){ - sample = codec->sample_size; rate = p_subtype->sample_rate; channels = p_subtype->channels; subtype = p_subtype->type; @@ -89,11 +86,27 @@ AmAudioFormat::~AmAudioFormat() destroyCodec(); } +unsigned int AmAudioFormat::samples2bytes(unsigned int nb_samples) const +{ + if (codec && codec->samples2bytes) + return codec->samples2bytes(h_codec, nb_samples) * channels; + WARN("Cannot convert samples to bytes\n"); + return nb_samples * channels; +} + +unsigned int AmAudioFormat::bytes2samples(unsigned int bytes) const +{ + if (codec && codec->samples2bytes) + return codec->bytes2samples(h_codec, bytes) / channels; + WARN("Cannot convert bytes to samples\n"); + return bytes / channels; +} + bool AmAudioFormat::operator == (const AmAudioFormat& r) const { return ( codec && r.codec && (r.codec->id == codec->id) - && (r.sample == sample) + && (r.bytes2samples(1024) == bytes2samples(1024)) && (r.channels == channels) && (r.rate == rate)); } @@ -211,7 +224,7 @@ void AmAudio::close() // returns bytes read, else -1 if error (0 is OK) int AmAudio::get(unsigned int user_ts, unsigned char* buffer, unsigned int nb_samples) { - int size = nb_samples * fmt->sample * fmt->channels; + int size = samples2bytes(nb_samples); size = read(user_ts,size); //DBG("size = %d\n",size); @@ -337,14 +350,14 @@ unsigned int AmAudio::getFrameSize() return fmt->frame_size; } -unsigned int AmAudio::samples2bytes(unsigned int nb_samples) +unsigned int AmAudio::samples2bytes(unsigned int nb_samples) const { - return nb_samples * fmt->sample * fmt->channels; + return fmt->samples2bytes(nb_samples); } -unsigned int AmAudio::bytes2samples(unsigned int bytes) +unsigned int AmAudio::bytes2samples(unsigned int bytes) const { - return bytes / (fmt->sample * fmt->channels); + return fmt->bytes2samples(bytes); } void AmAudio::setRecordTime(unsigned int ms) @@ -427,14 +440,12 @@ int AmAudioFile::open(const string& filename, OpenMode mode, bool is_tmp) } fd.subtype = f_fmt->getSubtypeId(); - fd.sample = f_fmt->sample; fd.channels = f_fmt->channels; fd.rate = f_fmt->rate; if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) { if (mode == AmAudioFile::Read) { f_fmt->setSubtypeId(fd.subtype); - f_fmt->sample = fd.sample; f_fmt->channels = fd.channels; f_fmt->rate = fd.rate; } @@ -492,14 +503,12 @@ int AmAudioFile::fpopen(const string& filename, OpenMode mode, FILE* n_fp) } fd.subtype = f_fmt->getSubtypeId(); - fd.sample = f_fmt->sample; fd.channels = f_fmt->channels; fd.rate = f_fmt->rate; if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) { if (mode == AmAudioFile::Read) { f_fmt->setSubtypeId(fd.subtype); - f_fmt->sample = fd.sample; f_fmt->channels = fd.channels; f_fmt->rate = fd.rate; } @@ -518,7 +527,6 @@ int AmAudioFile::fpopen(const string& filename, OpenMode mode, FILE* n_fp) // DBG("After open:\n"); // DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); -// DBG("fmt::sample = %i\n",f_fmt->sample); // DBG("fmt::channels = %i\n",f_fmt->channels); // DBG("fmt::rate = %i\n",f_fmt->rate); // } @@ -554,7 +562,6 @@ void AmAudioFile::on_close() if(f_fmt){ amci_file_desc_t fmt_desc = { f_fmt->getSubtypeId(), - f_fmt->sample, f_fmt->rate, f_fmt->channels, data_size }; @@ -563,14 +570,13 @@ void AmAudioFile::on_close() ERROR("file format pointer not initialized: on_close will not be called\n"); } else if(iofmt->on_close) - (*iofmt->on_close)(fp,&fmt_desc,open_mode, fmt->getHCodecNoInit()); + (*iofmt->on_close)(fp,&fmt_desc,open_mode, fmt->getHCodecNoInit(), fmt->getCodec()); } if(open_mode == AmAudioFile::Write){ DBG("After close:\n"); DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId()); - DBG("fmt::sample = %i\n",f_fmt->sample); DBG("fmt::channels = %i\n",f_fmt->channels); DBG("fmt::rate = %i\n",f_fmt->rate); } diff --git a/core/AmAudio.h b/core/AmAudio.h index 93e92c49..5a0d4804 100644 --- a/core/AmAudio.h +++ b/core/AmAudio.h @@ -82,11 +82,6 @@ public: void swap(); }; -struct amci_codec_t; -struct amci_inoutfmt_t; -struct amci_file_desc_t; -struct amci_subtype_t; - class AmAudio; /** @@ -102,8 +97,6 @@ class AmAudio; class AmAudioFormat { public: - /** Sampling size (in bytes). */ - int sample; /** Number of channels. */ int channels; /** Sampling rate. */ @@ -126,6 +119,9 @@ public: long getHCodec(); long getHCodecNoInit() { return h_codec; } // do not initialize + unsigned int samples2bytes(unsigned int) const; + unsigned int bytes2samples(unsigned int) const; + /** @return true if same format. */ bool operator == (const AmAudioFormat& r) const; /** @return false if same format. */ @@ -292,12 +288,12 @@ protected: /** * Convert the size from samples to bytes, depending on the format. */ - unsigned int samples2bytes(unsigned int nb_samples); + unsigned int samples2bytes(unsigned int nb_samples) const; /** * Convert the size from bytes to samples, depending on the format. */ - unsigned int bytes2samples(unsigned int bytes); + unsigned int bytes2samples(unsigned int bytes) const; public: //bool begin_talk; diff --git a/core/AmJitterBuffer.cpp b/core/AmJitterBuffer.cpp index 45470cbd..a651a5ad 100644 --- a/core/AmJitterBuffer.cpp +++ b/core/AmJitterBuffer.cpp @@ -25,65 +25,106 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "AmRtpStream.h" #include "AmJitterBuffer.h" #include "AmRtpPacket.h" #include "log.h" #include "SampleArray.h" -#define INITIAL_JITTER 2 -#define MAX_JITTER 200 // 2 seconds -#define RESYNC_THRESHOLD 10 - -template -RingBuffer::RingBuffer(unsigned int size) - : m_buffer(new T[size]), m_size(size) +bool Packet::operator < (const Packet& p) const { - memset(m_buffer, 0, sizeof(T)*size); + return ts_less()(m_packet.timestamp, p.m_packet.timestamp); } -template -void RingBuffer::get(unsigned int idx, T *dest) +PacketAllocator::PacketAllocator() { - memcpy(dest, &m_buffer[idx % m_size], sizeof(T)); + m_mutex.lock(); + m_free_packets = m_packets; + for (int i = 1; i < MAX_JITTER / 80; ++i) { + m_packets[i - 1].m_next = &m_packets[i]; + } + m_packets[MAX_JITTER / 80 - 1].m_next = NULL; + m_mutex.unlock(); } -template -void RingBuffer::put(unsigned int idx, const T *src) +Packet *PacketAllocator::alloc(const AmRtpPacket *p) { - memcpy(&m_buffer[idx % m_size], src, sizeof(T)); + if (m_free_packets == NULL) + return NULL; + m_mutex.lock(); + Packet *retval = m_free_packets; + m_free_packets = retval->m_next; + memcpy(&retval->m_packet, p, sizeof(*p)); + retval->m_next = retval->m_prev = NULL; + m_mutex.unlock(); + return retval; } -template -void RingBuffer::clear(unsigned int idx) +void PacketAllocator::free(Packet *p) { - memset(&m_buffer[idx % m_size], 0, sizeof(T)); + m_mutex.lock(); + p->m_prev = NULL; + p->m_next = m_free_packets; + m_free_packets = p; + m_mutex.unlock(); } -AmJitterBuffer::AmJitterBuffer(unsigned int frame_size) - : m_tsInited(false), m_tsDeltaInited(false), m_ringBuffer(MAX_JITTER), - m_delayCount(0), m_jitter(INITIAL_JITTER), m_frameSize(frame_size) +AmJitterBuffer::AmJitterBuffer(AmRtpStream *owner) + : m_tsInited(false), m_tsDeltaInited(false), m_delayCount(0), + m_jitter(INITIAL_JITTER), m_owner(owner) { } void AmJitterBuffer::put(const AmRtpPacket *p) { m_mutex.lock(); - //m_inputBuffer[p->timestamp].copy(p); - // DBG("Putting pkt at %u me = %ld\n", p->timestamp / m_frameSize, (long) this); - if (m_tsInited && ts_less()(p->timestamp + m_jitter * m_frameSize, m_lastTs)) { + if (m_tsInited && ts_less()(m_lastTs + m_jitter, p->timestamp)) { unsigned int delay = p->timestamp - m_lastTs; - if (delay > m_jitter * m_frameSize && m_jitter < MAX_JITTER) { - m_jitter += (delay / m_frameSize - m_jitter) / 2 + 1; + if (delay > m_jitter && m_jitter < MAX_JITTER) + { + m_jitter += (delay - m_jitter) / 2; if (m_jitter > MAX_JITTER) m_jitter = MAX_JITTER; + // DBG("Jitter buffer delay increased to %u\n", m_jitter); } // Packet arrived too late to be put into buffer - if (ts_less()(p->timestamp + m_jitter * m_frameSize, m_lastTs)) { + if (ts_less()(p->timestamp + m_jitter, m_lastTs)) { m_mutex.unlock(); return; } } - m_ringBuffer.put(p->timestamp / m_frameSize, p); + Packet *elem = m_allocator.alloc(p); + if (elem == NULL) { + elem = m_head; + m_head = m_head->m_next; + m_head->m_prev = NULL; + memcpy(&elem->m_packet, p, sizeof(*p)); + } + if (m_tail == NULL) + { + m_tail = m_head = elem; + elem->m_next = elem->m_prev = NULL; + } + else { + if (*m_tail < *elem) // elem is later than tail - put it in tail + { + m_tail->m_next = elem; + elem->m_prev = m_tail; + m_tail = elem; + elem->m_next = NULL; + } + else { // elem is out of order - place it properly + Packet *i; + for (i = m_tail; i->m_prev && *elem < *(i->m_prev); i = i->m_prev); + elem->m_prev = i->m_prev; + if (i->m_prev) + i->m_prev->m_next = elem; + else + m_head = elem; + i->m_prev = elem; + elem->m_next = i; + } + } if (!m_tsInited) { m_lastTs = p->timestamp; m_tsInited = true; @@ -95,7 +136,12 @@ void AmJitterBuffer::put(const AmRtpPacket *p) m_mutex.unlock(); } -bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts) +/** + * This method will return from zero to several packets. + * To get all the packets for the single ts the caller must call this + * method with the same ts till the return value will become false. + */ +bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts, unsigned int ms) { bool retval = true; @@ -105,28 +151,25 @@ bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts) return false; } if (!m_tsDeltaInited) { - m_tsDelta = m_lastTs - ts; + m_tsDelta = m_lastTs - ts + ms; m_tsDeltaInited = true; + m_lastAudioTs = ts; } - else { - unsigned int new_delta = m_lastTs - ts; - if (ts_less()(m_tsDelta, new_delta)) { + else if (m_lastAudioTs != ts && m_lastResyncTs != m_lastTs) { + if (ts_less()(ts + m_tsDelta, m_lastTs)) { /* * New packet arrived earlier than expected - * immediate resync required */ - m_ringBuffer.clear((ts + m_tsDelta - m_jitter * m_frameSize) / m_frameSize); - ++m_tsDelta; + m_tsDelta += ms; // DBG("Jitter buffer resynced forward (-> %u)\n", m_tsDelta); m_delayCount = 0; } - else if (ts_less()(new_delta, m_tsDelta)) { + else if (ts_less()(m_lastTs, ts + m_tsDelta - m_jitter / 2)) { /* New packet hasn't arrived yet */ if (m_delayCount > RESYNC_THRESHOLD) { - --m_tsDelta; + m_tsDelta -= 80; // 10ms // DBG("Jitter buffer resynced backward (-> %u)\n", m_tsDelta); -// m_tsDelta = new_delta; -// m_delayCount = 0; } else ++m_delayCount; @@ -135,12 +178,38 @@ bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts) /* New packet arrived at proper time */ m_delayCount = 0; } + m_lastResyncTs = m_lastTs; } - unsigned int get_ts = ts + m_tsDelta - m_jitter * m_frameSize; - m_ringBuffer.get(get_ts / m_frameSize, &p); + m_lastAudioTs = ts; + unsigned int get_ts = ts + m_tsDelta - m_jitter; // DBG("Getting pkt at %u, res ts = %u\n", get_ts / m_frameSize, p.timestamp); - m_ringBuffer.clear(get_ts / m_frameSize); - if (!p.timestamp) + // First of all throw away all too old packets from the head + Packet *tmp; + for (tmp = m_head; tmp && ts_less()(tmp->m_packet.timestamp + m_owner->bytes2samples(tmp->m_packet.getDataSize()), get_ts); ) + { + m_head = tmp->m_next; + if (m_head == NULL) + m_tail = NULL; + else + m_head->m_prev = NULL; + m_allocator.free(tmp); + tmp = m_head; + } + // Get the packet from the head + if (m_head && ts_less()(m_head->m_packet.timestamp, get_ts + ms)) + { + tmp = m_head; + m_head = tmp->m_next; + if (m_head == NULL) + m_tail = NULL; + else + m_head->m_prev = NULL; + memcpy(&p, &tmp->m_packet, sizeof(p)); + // Map RTP timestamp to internal audio timestamp + p.timestamp -= m_tsDelta - m_jitter; + m_allocator.free(tmp); + } + else retval = false; m_mutex.unlock(); diff --git a/core/AmJitterBuffer.h b/core/AmJitterBuffer.h index e06f3a21..e576a162 100644 --- a/core/AmJitterBuffer.h +++ b/core/AmJitterBuffer.h @@ -31,47 +31,55 @@ #include "AmThread.h" #include "AmRtpPacket.h" -#include -using std::map; +class AmRtpStream; -template class RingBuffer +#define INITIAL_JITTER 640 // 80 miliseconds +#define MAX_JITTER 16000 // 2 seconds +#define RESYNC_THRESHOLD 10 + +class Packet { +public: + AmRtpPacket m_packet; + Packet *m_next; + Packet *m_prev; + + bool operator < (const Packet&) const; +}; + +class PacketAllocator { private: - T *m_buffer; - unsigned int m_size; + Packet m_packets[MAX_JITTER / 80]; + Packet *m_free_packets; + AmMutex m_mutex; public: - RingBuffer(unsigned int size); - ~RingBuffer(); - void put(unsigned int idx, const T*); - void get(unsigned int idx, T*); - void clear(unsigned int idx); + PacketAllocator(); + Packet *alloc(const AmRtpPacket *); + void free(Packet *p); }; class AmJitterBuffer { private: AmMutex m_mutex; - RingBuffer m_ringBuffer; + PacketAllocator m_allocator; + Packet *m_head; + Packet *m_tail; bool m_tsInited; unsigned int m_lastTs; + unsigned int m_lastResyncTs; + unsigned int m_lastAudioTs; unsigned int m_tsDelta; bool m_tsDeltaInited; int m_delayCount; unsigned int m_jitter; - unsigned int m_frameSize; + AmRtpStream *m_owner; public: - AmJitterBuffer(unsigned int frame_size); + AmJitterBuffer(AmRtpStream *owner); void put(const AmRtpPacket *); - bool get(AmRtpPacket &, unsigned int ts); + bool get(AmRtpPacket &, unsigned int ts, unsigned int ms); }; -template -RingBuffer::~RingBuffer() -{ - delete [] m_buffer; -} - - #endif // _AmJitterBuffer_h_ diff --git a/core/AmPlayoutBuffer.cpp b/core/AmPlayoutBuffer.cpp index b2822dec..f002bd46 100644 --- a/core/AmPlayoutBuffer.cpp +++ b/core/AmPlayoutBuffer.cpp @@ -38,20 +38,13 @@ u_int32_t AmPlayoutBuffer::read(u_int32_t ts, int16_t* buf, u_int32_t len) { if(ts_less()(r_ts,w_ts)){ - u_int32_t rlen=0; - if(ts_less()(r_ts+PCM16_B2S(AUDIO_BUFFER_SIZE),w_ts)) - rlen = PCM16_B2S(AUDIO_BUFFER_SIZE); - else - rlen = w_ts - r_ts; - - buffer_get(r_ts,buf,rlen); - return rlen; + buffer_get(r_ts,buf,len); + return len; } return 0; } - AmAdaptivePlayout::AmAdaptivePlayout() : idx(0), loss_rate(ORDER_STAT_LOSS_RATE), diff --git a/core/AmPlugIn.cpp b/core/AmPlugIn.cpp index 4e71f5e0..f82ae69b 100644 --- a/core/AmPlugIn.cpp +++ b/core/AmPlugIn.cpp @@ -41,22 +41,46 @@ #include #include +static unsigned int pcm16_bytes2samples(long h_codec, unsigned int num_bytes) +{ + return num_bytes / 2; +} + +static unsigned int pcm16_samples2bytes(long h_codec, unsigned int num_samples) +{ + return num_samples * 2; +} + +static unsigned int tevent_bytes2samples(long h_codec, unsigned int num_bytes) +{ + return num_bytes; +} + +static unsigned int tevent_samples2bytes(long h_codec, unsigned int num_samples) +{ + return num_samples; +} + amci_codec_t _codec_pcm16 = { CODEC_PCM16, - 2, NULL, NULL, NULL, - NULL + NULL, + NULL, + pcm16_bytes2samples, + pcm16_samples2bytes }; amci_codec_t _codec_tevent = { CODEC_TELEPHONE_EVENT, - 1, NULL, NULL, NULL, - NULL + NULL, + NULL, + tevent_bytes2samples, + tevent_samples2bytes }; amci_payload_t _payload_tevent = { diff --git a/core/AmRtpAudio.cpp b/core/AmRtpAudio.cpp index 809da512..50e9532d 100644 --- a/core/AmRtpAudio.cpp +++ b/core/AmRtpAudio.cpp @@ -62,60 +62,76 @@ bool AmRtpAudio::sendIntReached() return send_int; } +unsigned int AmRtpAudio::bytes2samples(unsigned int bytes) const +{ + return AmAudio::bytes2samples(bytes); +} /* @param audio_buffer_ts [in] the current ts in the audio buffer */ int AmRtpAudio::receive(unsigned int audio_buffer_ts) { - int size; + int rtp_size; + int audio_size; + unsigned int ts; - size = AmRtpStream::receive((unsigned char*)samples, - (unsigned int)AUDIO_BUFFER_SIZE,/*ts,*/ - audio_buffer_ts); - if (size < 0) - return size; - - if(send_only){ + if (send_only) { last_ts_i = false; return 0; } - - if (size == 0) + /** + * Receive all RTP packets that correspond to the required interval, + * decode them and put into playout buffer. + */ + while (true) { - if (last_ts_i) - { - int size = conceal_loss(audio_buffer_ts - m_audio_last_ts); - if (size > 0) - { - playout_buffer->direct_write(audio_buffer_ts, - (ShortSample*)samples.back_buffer(), - PCM16_B2S(size)); - } + rtp_size = AmRtpStream::receive((unsigned char*)samples, + (unsigned int)AUDIO_BUFFER_SIZE, &ts, + audio_buffer_ts, getFrameSize()); + if (rtp_size < 0) { + return rtp_size; } - else - return 0; - } - else - { - last_ts_i = true; - - - size = decode(size); - if (size <= 0) { - ERROR("decode() returned %i\n",size); + if (rtp_size == 0) { // No more RTP packets for this interval + break; + } + audio_size = decode(rtp_size); + if (audio_size <= 0) { + ERROR("decode() returned %i\n", audio_size); return -1; } - - if(use_default_plc) - add_to_history(size); - - playout_buffer->direct_write(audio_buffer_ts, /*ts,*/ - (ShortSample*)((unsigned char*)samples), - PCM16_B2S(size)); + playout_buffer->direct_write(ts, + (ShortSample*)((unsigned char*)samples), + PCM16_B2S(audio_size)); + /* Conceal the gap between previous and current RTP packets */ + if (last_ts_i && ts_less()(m_last_rtp_endts, ts)) + { + int concealed_size = conceal_loss(ts - m_last_rtp_endts); + if (concealed_size > 0) + playout_buffer->direct_write(m_last_rtp_endts, + (ShortSample*)((unsigned char*)samples.back_buffer()), + PCM16_B2S(concealed_size)); + } + m_last_rtp_endts = ts + bytes2samples(rtp_size); + last_ts_i = true; + if(use_default_plc) { + add_to_history(audio_size); + } + } + if (!last_ts_i) { + return 0; + } + if (ts_less()(m_last_rtp_endts, audio_buffer_ts + getFrameSize())) + { + /* Last packets have been lost. Conceal them */ + int concealed_size = conceal_loss(audio_buffer_ts + getFrameSize() - m_last_rtp_endts); + if (concealed_size > 0) + playout_buffer->direct_write(m_last_rtp_endts, + (ShortSample*)((unsigned char*)samples.back_buffer()), + PCM16_B2S(concealed_size)); + m_last_rtp_endts = audio_buffer_ts + getFrameSize(); } - m_audio_last_ts = audio_buffer_ts; - return size; + return PCM16_S2B(getFrameSize()); } int AmRtpAudio::get(unsigned int user_ts, unsigned char* buffer, unsigned int nb_samples) @@ -146,7 +162,6 @@ void AmRtpAudio::init(const SdpPayload* sdp_payload) DBG("AmRtpAudio::init(...)\n"); AmRtpStream::init(sdp_payload); fmt.reset(new AmAudioRtpFormat(int_payload, format_parameters)); - initJitterBuffer(getFrameSize()); amci_codec_t* codec = fmt->getCodec(); use_default_plc = !(codec && codec->plc); diff --git a/core/AmRtpAudio.h b/core/AmRtpAudio.h index 4246c05a..270fa559 100644 --- a/core/AmRtpAudio.h +++ b/core/AmRtpAudio.h @@ -52,7 +52,7 @@ class AmRtpAudio: public AmRtpStream, public AmAudio bool last_check_i; bool send_int; - unsigned int m_audio_last_ts; + unsigned int m_last_rtp_endts; bool last_ts_i; bool send_only; @@ -94,6 +94,8 @@ public: void init(const SdpPayload* sdp_payload); void setAdaptivePlayout(bool on); + + virtual unsigned int bytes2samples(unsigned int) const; }; #endif diff --git a/core/AmRtpPacket.cpp b/core/AmRtpPacket.cpp index 4e142801..9dc9ad70 100644 --- a/core/AmRtpPacket.cpp +++ b/core/AmRtpPacket.cpp @@ -38,7 +38,7 @@ #include AmRtpPacket::AmRtpPacket() - : data(0) + : data_offset(0) { memset(buffer,0,4096); } @@ -98,16 +98,21 @@ int AmRtpPacket::parse() timestamp = ntohl(hdr->ts); ssrc = ntohl(hdr->ssrc); - data = buffer + sizeof(rtp_hdr_t) + (hdr->cc*4); - d_size = b_size - (data - buffer); + data_offset = sizeof(rtp_hdr_t) + (hdr->cc*4); + d_size = b_size - data_offset; if(hdr->p){ - d_size -= data[d_size-1]; + d_size -= buffer[data_offset+d_size-1]; } return 0; } +unsigned char *AmRtpPacket::getData() +{ + return &buffer[data_offset]; +} + int AmRtpPacket::compile(unsigned char* data_buf, unsigned int size) { assert(data_buf); @@ -133,8 +138,8 @@ int AmRtpPacket::compile(unsigned char* data_buf, unsigned int size) hdr->ts = htonl(timestamp); hdr->ssrc = htonl(ssrc); - data = buffer + sizeof(rtp_hdr_t); - memcpy(data,data_buf,d_size); + data_offset = sizeof(rtp_hdr_t); + memcpy(&buffer[data_offset],data_buf,d_size); return 0; } diff --git a/core/AmRtpPacket.h b/core/AmRtpPacket.h index 028c1d97..02c3cffc 100644 --- a/core/AmRtpPacket.h +++ b/core/AmRtpPacket.h @@ -39,7 +39,7 @@ class AmRtpPacket { unsigned char buffer[4096]; unsigned int b_size; - unsigned char* data; + unsigned int data_offset; unsigned int d_size; public: @@ -76,8 +76,8 @@ public: int parse(); - unsigned int getDataSize() { return d_size; } - unsigned char* getData() { return data; } + unsigned int getDataSize() const { return d_size; } + unsigned char* getData(); void copy(const AmRtpPacket* p); diff --git a/core/AmRtpStream.cpp b/core/AmRtpStream.cpp index 1e8c0c36..88d7884e 100644 --- a/core/AmRtpStream.cpp +++ b/core/AmRtpStream.cpp @@ -193,28 +193,25 @@ int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size // in audio buffer relative time // @param audio_buffer_ts [in] current ts at the audio_buffer -int AmRtpStream::receive( unsigned char* buffer, unsigned int size, - /*unsigned int& ts,*/ unsigned int audio_buffer_ts) +int AmRtpStream::receive( unsigned char* buffer, unsigned int buf_size, + unsigned int *ts, + unsigned int audio_buffer_ts, unsigned int ms) { AmRtpPacket rp; AmRtpPacket dtmf_pkt; - int err = nextAudioPacket(rp, audio_buffer_ts); - if (m_telephone_event_jb->get(dtmf_pkt, audio_buffer_ts)) + while (m_telephone_event_jb->get(dtmf_pkt, audio_buffer_ts, ms)) { - if (dtmf_pkt.parse() != -1) - { - dtmf_payload_t* dpl = (dtmf_payload_t*)dtmf_pkt.getData(); + dtmf_payload_t* dpl = (dtmf_payload_t*)dtmf_pkt.getData(); - DBG("DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i\n", - dpl->event,dpl->e,dpl->r,dpl->volume,ntohs(dpl->duration)); - session->postDtmfEvent(new AmRtpDtmfEvent(dpl, getTelephoneEventRate())); - } + DBG("DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i\n", + dpl->event,dpl->e,dpl->r,dpl->volume,ntohs(dpl->duration)); + session->postDtmfEvent(new AmRtpDtmfEvent(dpl, getTelephoneEventRate())); } + int err = nextAudioPacket(rp, audio_buffer_ts, ms); if(err <= 0) return err; - #ifdef SUPPORT_IPV6 struct sockaddr_storage recv_addr; #else @@ -237,11 +234,6 @@ int AmRtpStream::receive( unsigned char* buffer, unsigned int size, } #endif - if(rp.parse() == -1){ - ERROR("while parsing RTP packet.\n"); - return RTP_PARSE_ERROR; - } - /* do we have a new talk spurt? */ begin_talk = ((last_payload == 13) || rp.marker); last_payload = rp.payload; @@ -250,11 +242,12 @@ int AmRtpStream::receive( unsigned char* buffer, unsigned int size, return RTP_EMPTY; assert(rp.getData()); - if(rp.getDataSize() > size){ + if(rp.getDataSize() > buf_size){ ERROR("received too big RTP packet\n"); return RTP_BUFFER_SIZE; } memcpy(buffer,rp.getData(),rp.getDataSize()); + *ts = rp.timestamp; return rp.getDataSize(); } @@ -271,10 +264,11 @@ AmRtpStream::AmRtpStream(AmSession* _s) first_recved(false), telephone_event_pt(NULL), mute(false), - m_main_jb(NULL), - m_telephone_event_jb(new AmJitterBuffer(160)) + m_main_jb(NULL) { //assert(session); + m_telephone_event_jb = new AmJitterBuffer(this); + m_main_jb = new AmJitterBuffer(this); #ifdef SUPPORT_IPV6 memset(&r_saddr,0,sizeof(struct sockaddr_storage)); memset(&l_saddr,0,sizeof(struct sockaddr_storage)); @@ -388,13 +382,6 @@ void AmRtpStream::icmpError() } } -void AmRtpStream::initJitterBuffer(unsigned int frame_size) -{ - if (m_main_jb) - return; - m_main_jb = new AmJitterBuffer(frame_size); -} - void AmRtpStream::bufferPacket(const AmRtpPacket* p) { gettimeofday(&last_recv_time,NULL); @@ -404,9 +391,9 @@ void AmRtpStream::bufferPacket(const AmRtpPacket* p) m_telephone_event_jb->put(p); } -int AmRtpStream::nextAudioPacket(AmRtpPacket& p, unsigned int ts) +int AmRtpStream::nextAudioPacket(AmRtpPacket& p, unsigned int ts, unsigned int ms) { - if (m_main_jb && m_main_jb->get(p, ts)) + if (m_main_jb && m_main_jb->get(p, ts, ms)) return 1; struct timeval now; diff --git a/core/AmRtpStream.h b/core/AmRtpStream.h index e6f06a99..ae26d6ea 100644 --- a/core/AmRtpStream.h +++ b/core/AmRtpStream.h @@ -138,8 +138,7 @@ protected: void setLocalPort(); /* get next packet in buffer */ - int nextAudioPacket(AmRtpPacket& p, unsigned int ts); - void initJitterBuffer(unsigned int frame_size); + int nextAudioPacket(AmRtpPacket& p, unsigned int ts, unsigned int ms); public: @@ -151,8 +150,8 @@ public: unsigned char* buffer, unsigned int size ); - int receive( unsigned char* buffer, unsigned int size, - unsigned int audio_buffer_ts); + int receive( unsigned char* buffer, unsigned int size, unsigned int *ts, + unsigned int audio_buffer_ts, unsigned int ms); /** Allocates resources for future use of RTP. */ AmRtpStream(AmSession* _s=0); @@ -231,6 +230,8 @@ public: * Note: memory is owned by this instance. */ void bufferPacket(const AmRtpPacket* p); + + virtual unsigned int bytes2samples(unsigned int) const = 0; }; /** \brief represents info about an \ref AmRtpStream */ struct AmRtpStreamInfo diff --git a/core/amci/amci.h b/core/amci/amci.h index 370d0b13..80e8f854 100644 --- a/core/amci/amci.h +++ b/core/amci/amci.h @@ -74,6 +74,7 @@ extern "C" { /** @def AMCI_FMT_ENCODED_FRAME_SIZE encoded frame size in bytes */ #define AMCI_FMT_ENCODED_FRAME_SIZE 3 +struct amci_codec_t; /** * File format declaration @@ -84,9 +85,6 @@ struct amci_file_desc_t { /** subtype from current file format */ int subtype; - /** sample size */ - int sample; - /** sampling rate */ int rate; @@ -142,21 +140,37 @@ typedef int (*amci_plc_t)( unsigned char* out, long h_codec ); /** - * File format handler + * File format handler's open function * @param fptr [in] fresh opened file pointer * @param fmt_desc [out] file description * @param options [in] options (see amci_inoutfmt_t) * @param h_codec [in] handle of the codec * @return if failure -1, else 0. * @see amci_inoutfmt_t::open - * @see amci_inoutfmt_t::on_close */ -typedef int (*amci_file_handler_t)( FILE* fptr, +typedef int (*amci_file_open_t)( FILE* fptr, struct amci_file_desc_t* fmt_desc, int options, long h_codec ); +/** + * File format handler's close function + * @param fptr [in] fresh opened file pointer + * @param fmt_desc [out] file description + * @param options [in] options (see amci_inoutfmt_t) + * @param h_codec [in] handle of the codec + * @param codec [in] codec structure + * @return if failure -1, else 0. + * @see amci_inoutfmt_t::on_close + */ +typedef int (*amci_file_close_t)( FILE* fptr, + struct amci_file_desc_t* fmt_desc, + int options, + long h_codec, + struct amci_codec_t *codec + ); + /** * Codec's init function pointer. * @param format_parameters [in] parameters as passed by fmtp tag, 0 if none @@ -183,6 +197,16 @@ typedef long (*amci_codec_init_t)(const char* format_parameters, amci_codec_fmt_ */ typedef void (*amci_codec_destroy_t)(long h_codec); +/** + * Codec's function for calculating the number of samples from bytes + */ +typedef unsigned int (*amci_codec_bytes2samples_t)(long h_codec, unsigned int num_bytes); + +/** + * Codec's function for calculating the number of bytes from samples + */ +typedef unsigned int (*amci_codec_samples2bytes_t)(long h_codec, unsigned int num_samples); + /** * Codec description */ @@ -191,9 +215,6 @@ struct amci_codec_t { /** internal codec id (the ones from codecs.h) */ int id; - /** Size in bytes (!TODO: have bits in place of bytes) */ - int sample_size; - /** * Converts the input buffer (internal format: Pcm16) * to the format described in this structure. @@ -210,6 +231,12 @@ struct amci_codec_t { amci_codec_init_t init; /** Destroy function. can be NULL. */ amci_codec_destroy_t destroy; + + /** Function for calculating the number of bytes from samples. */ + amci_codec_bytes2samples_t bytes2samples; + + /** Function for calculating the number of samples from bytes. */ + amci_codec_samples2bytes_t samples2bytes; }; struct amci_subtype_t { @@ -253,10 +280,10 @@ struct amci_inoutfmt_t { char* email_content_type; /** options: AMCI_RDONLY, AMCI_WRONLY. */ - amci_file_handler_t open; + amci_file_open_t open; /** no options at the moment. */ - amci_file_handler_t on_close; + amci_file_close_t on_close; /** NULL terminated subtype array. */ struct amci_subtype_t* subtypes; @@ -351,7 +378,7 @@ struct amci_exports_t { * @hideinitializer */ #define END_CODECS \ - { -1, 0, 0, 0, 0, 0, 0 } \ + { -1, 0, 0, 0, 0, 0, 0, 0 } \ }, /** @@ -359,8 +386,8 @@ struct amci_exports_t { * see example media plug-in 'wav' (plug-in/wav/wav.c). * @hideinitializer */ -#define CODEC(id,sample_size,intern2type,type2intern,plc,init,destroy) \ - { id, sample_size, intern2type, type2intern, plc, init, destroy }, +#define CODEC(id,intern2type,type2intern,plc,init,destroy,bytes2samples,samples2bytes) \ + { id, intern2type, type2intern, plc, init, destroy, bytes2samples, samples2bytes }, /** * Portable export definition macro diff --git a/core/plug-in/gsm/gsm.c b/core/plug-in/gsm/gsm.c index bdb88fab..13f1f151 100644 --- a/core/plug-in/gsm/gsm.c +++ b/core/plug-in/gsm/gsm.c @@ -43,11 +43,14 @@ static long gsm_create_if(const char* format_parameters, amci_codec_fmt_info_t* static void gsm_destroy_if(long h_codec); +static unsigned int gsm_bytes2samples(long, unsigned int); +static unsigned int gsm_samples2bytes(long, unsigned int); + BEGIN_EXPORTS( "gsm" ) BEGIN_CODECS - CODEC( CODEC_GSM0610, 1, pcm16_2_gsm, gsm_2_pcm16, NULL, - (amci_codec_init_t)gsm_create_if, (amci_codec_destroy_t)gsm_destroy_if ) + CODEC( CODEC_GSM0610, pcm16_2_gsm, gsm_2_pcm16, NULL, + gsm_create_if, (amci_codec_destroy_t)gsm_destroy_if, gsm_bytes2samples, gsm_samples2bytes ) END_CODECS BEGIN_PAYLOADS @@ -59,6 +62,16 @@ BEGIN_EXPORTS( "gsm" ) END_EXPORTS +static unsigned int gsm_bytes2samples(long h_codec, unsigned int num_bytes) +{ + return 160 * (num_bytes / 33); +} + +static unsigned int gsm_samples2bytes(long h_codec, unsigned int num_samples) +{ + return 33 * (num_samples / 160); +} + static int pcm16_2_gsm(unsigned char* out_buf, unsigned char* in_buf, unsigned int size, unsigned int channels, unsigned int rate, long h_codec ) { @@ -113,7 +126,7 @@ static int gsm_2_pcm16(unsigned char* out_buf, unsigned char* in_buf, unsigned i } -static long gsm_create_if(const char* format_parameters, amci_codec_fmt_info_t* format_description) +static long gsm_create_if(const char* format_parameters, amci_codec_fmt_info_t* format_description) { gsm* h_codec=0; diff --git a/core/plug-in/ilbc/ilbc.c b/core/plug-in/ilbc/ilbc.c index 655ce15c..edf60891 100644 --- a/core/plug-in/ilbc/ilbc.c +++ b/core/plug-in/ilbc/ilbc.c @@ -72,14 +72,18 @@ static int Pcm16_2_iLBC( unsigned char* out_buf, unsigned char* in_buf, unsigned static long iLBC_create(const char* format_parameters, amci_codec_fmt_info_t* format_description); static void iLBC_destroy(long h_inst); static int iLBC_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec); -static int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec); +static int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec); + +static unsigned int ilbc_bytes2samples(long, unsigned int); +static unsigned int ilbc_samples2bytes(long, unsigned int); BEGIN_EXPORTS( "ilbc" ) BEGIN_CODECS - CODEC( CODEC_ILBC, 1, Pcm16_2_iLBC, iLBC_2_Pcm16, iLBC_PLC, - (amci_codec_init_t)iLBC_create, - (amci_codec_destroy_t)iLBC_destroy ) + CODEC( CODEC_ILBC, Pcm16_2_iLBC, iLBC_2_Pcm16, iLBC_PLC, + iLBC_create, + iLBC_destroy, + ilbc_bytes2samples, ilbc_samples2bytes ) END_CODECS BEGIN_PAYLOADS @@ -100,8 +104,26 @@ END_EXPORTS typedef struct { iLBC_Enc_Inst_t iLBC_Enc_Inst; iLBC_Dec_Inst_t iLBC_Dec_Inst; + int mode; } iLBC_Codec_Inst_t; +static unsigned int ilbc_bytes2samples(long h_codec, unsigned int num_bytes) +{ + iLBC_Codec_Inst_t* codec_inst = (iLBC_Codec_Inst_t*) h_codec; + if (codec_inst->mode == 30) + return 240 * (num_bytes / 50); + else + return 160 * (num_bytes / 38); +} + +static unsigned int ilbc_samples2bytes(long h_codec, unsigned int num_samples) +{ + iLBC_Codec_Inst_t* codec_inst = (iLBC_Codec_Inst_t*) h_codec; + if (codec_inst->mode == 30) + return (num_samples / 240) * 50; + else + return (num_samples / 160) * 38; +} long iLBC_create(const char* format_parameters, amci_codec_fmt_info_t* format_description) { @@ -140,6 +162,7 @@ long iLBC_create(const char* format_parameters, amci_codec_fmt_info_t* format_de } codec_inst = (iLBC_Codec_Inst_t*)malloc(sizeof(iLBC_Codec_Inst_t)); + codec_inst->mode = mode; if (!codec_inst) return -1; @@ -282,7 +305,6 @@ static int ilbc_read_header(FILE* fp, struct amci_file_desc_t* fmt_desc) DBG("wrong format !"); return -1; } - fmt_desc->sample = 2; fmt_desc->rate = 8000; fmt_desc->channels = 1; @@ -313,7 +335,7 @@ int iLBC_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_c } } -int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec) +int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec) { DBG("iLBC_close.\n"); return 0; diff --git a/core/plug-in/wav/wav.c b/core/plug-in/wav/wav.c index 072c0453..8044256d 100644 --- a/core/plug-in/wav/wav.c +++ b/core/plug-in/wav/wav.c @@ -89,11 +89,14 @@ static int Pcm16_2_ULaw( unsigned char* out_buf, unsigned char* in_buf, unsigned static int Pcm16_2_ALaw( unsigned char* out_buf, unsigned char* in_buf, unsigned int size, unsigned int channels, unsigned int rate, long h_codec ); +static unsigned int g711_bytes2samples(long, unsigned int); +static unsigned int g711_samples2bytes(long, unsigned int); + BEGIN_EXPORTS( "wav" ) BEGIN_CODECS - CODEC( CODEC_ULAW, 1, Pcm16_2_ULaw, ULaw_2_Pcm16, NULL, NULL, NULL ) - CODEC( CODEC_ALAW, 1, Pcm16_2_ALaw, ALaw_2_Pcm16, NULL, NULL, NULL ) + CODEC( CODEC_ULAW, Pcm16_2_ULaw, ULaw_2_Pcm16, NULL, NULL, NULL, g711_bytes2samples, g711_samples2bytes ) + CODEC( CODEC_ALAW, Pcm16_2_ALaw, ALaw_2_Pcm16, NULL, NULL, NULL, g711_bytes2samples, g711_samples2bytes ) END_CODECS BEGIN_PAYLOADS @@ -113,6 +116,18 @@ BEGIN_EXPORTS( "wav" ) END_EXPORTS +static unsigned int g711_bytes2samples(long h_codec, unsigned int num_bytes) +{ + /* ALAW and ULAW formats has one sample per byte */ + return num_bytes; +} + +static unsigned int g711_samples2bytes(long h_codec, unsigned int num_samples) +{ + /* ALAW and ULAW formats has one sample per byte */ + return num_samples; +} + static int ULaw_2_Pcm16( unsigned char* out_buf, unsigned char* in_buf, unsigned int size, unsigned int channels, unsigned int rate, long h_codec ) { diff --git a/core/plug-in/wav/wav_hdr.c b/core/plug-in/wav/wav_hdr.c index e37ea928..a5ebde36 100644 --- a/core/plug-in/wav/wav_hdr.c +++ b/core/plug-in/wav/wav_hdr.c @@ -67,6 +67,7 @@ static int wav_read_header(FILE* fp, struct amci_file_desc_t* fmt_desc) unsigned short channels=0; unsigned int rate=0; unsigned short bits_per_sample=0; + unsigned short sample_size=0; if(!fp) return -1; @@ -117,11 +118,11 @@ static int wav_read_header(FILE* fp, struct amci_file_desc_t* fmt_desc) DBG("bits/sample = <%i>\n",bits_per_sample); fmt_desc->subtype = fmt; - fmt_desc->sample = bits_per_sample>>3; + sample_size = bits_per_sample>>3; fmt_desc->rate = rate; fmt_desc->channels = channels; - if( (fmt == 0x01) && (fmt_desc->sample == 1)){ + if( (fmt == 0x01) && (sample_size == 1)){ ERROR("Sorry, we don't support PCM 8 bit\n"); return -1; } @@ -159,10 +160,17 @@ int wav_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_co } } -int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc) +int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc, long h_codec, struct amci_codec_t *codec) { struct wav_header hdr; + int sample_size; + if (codec && codec->samples2bytes) + sample_size = codec->samples2bytes(h_codec, 1); + else { + ERROR("Cannot determine sample size\n"); + sample_size = 2; + } memcpy(hdr.magic, "RIFF",4); hdr.length = fmt_desc->data_size + 36; memcpy(hdr.chunk_type, "WAVE",4); @@ -171,9 +179,9 @@ int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc) hdr.format = fmt_desc->subtype; hdr.channels = (unsigned short)fmt_desc->channels; hdr.sample_rate = (unsigned int)fmt_desc->rate; - hdr.sample_size = hdr.channels * fmt_desc->sample; + hdr.sample_size = hdr.channels * sample_size; hdr.bytes_per_second = hdr.sample_rate * (unsigned int)hdr.sample_size; - hdr.precision = (unsigned short)(fmt_desc->sample * 8); + hdr.precision = (unsigned short)(sample_size * 8); memcpy(hdr.chunk_data,"data",4); hdr.data_length=fmt_desc->data_size; @@ -189,11 +197,11 @@ int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc) } -int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec) +int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec) { if(options == AMCI_WRONLY){ rewind(fp); - return wav_write_header(fp,fmt_desc); + return wav_write_header(fp, fmt_desc, h_codec, codec); } return 0; } diff --git a/core/plug-in/wav/wav_hdr.h b/core/plug-in/wav/wav_hdr.h index fee3426c..06801d3a 100644 --- a/core/plug-in/wav/wav_hdr.h +++ b/core/plug-in/wav/wav_hdr.h @@ -31,7 +31,7 @@ #include "amci.h" int wav_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec); -int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec); +int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec); #endif