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