void AmJbPlayout::prepare_buffer(unsigned int audio_buffer_ts, unsigned int ms) { ShortSample buf[AUDIO_BUFFER_SIZE]; unsigned int ts; unsigned int nb_samples; /** * Get all RTP packets that correspond to the required interval, * decode them and put into playout buffer. */ while (m_jb.get(audio_buffer_ts, ms, buf, &nb_samples, &ts)) { direct_write_buffer(ts, buf, nb_samples); m_plcbuffer->add_to_history(buf, PCM16_S2B(nb_samples)); /* Conceal the gap between previous and current RTP packets */ if (last_ts_i && ts_less()(m_last_rtp_endts, ts)) { int concealed_size = m_plcbuffer->conceal_loss(ts - m_last_rtp_endts, (unsigned char *)buf); if (concealed_size > 0) direct_write_buffer(m_last_rtp_endts, buf, PCM16_B2S(concealed_size)); } m_last_rtp_endts = ts + nb_samples; last_ts_i = true; } if (!last_ts_i) { return; } if (ts_less()(m_last_rtp_endts, audio_buffer_ts + ms)) { /* Last packets have been lost. Conceal them */ int concealed_size = m_plcbuffer->conceal_loss(audio_buffer_ts + ms - m_last_rtp_endts, (unsigned char *)buf); if (concealed_size > 0) direct_write_buffer(m_last_rtp_endts, buf, PCM16_B2S(concealed_size)); m_last_rtp_endts = audio_buffer_ts + ms; } }
unsigned int AmInternalResamplerState::resample(unsigned char *samples, unsigned int s, double ratio) { if (rstate == NULL) { ERROR("Uninitialized resampling state"); return s; } //DBG("Resampling with ration %f", ratio); //DBG("Putting %d samples in the buffer", PCM16_B2S(s)); rstate->put_samples((signed short *)samples, PCM16_B2S(s)); s = rstate->resample((signed short *)samples, ratio, PCM16_B2S(s) * ratio); //DBG("Returning %d samples", s); return PCM16_S2B(s); }
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; } return 0; }
void AmMultiPartyMixer::PutChannelPacket(unsigned int channel_id, unsigned long long system_ts, unsigned char* buffer, unsigned int size) { if(!size) return; assert(size <= AUDIO_BUFFER_SIZE); std::deque<MixerBufferState>::iterator bstate = findOrCreateBufferState(GetCurrentSampleRate()); SampleArrayShort* channel = 0; if((channel = bstate->get_channel(channel_id)) != 0) { unsigned samples = PCM16_B2S(size); unsigned long long put_ts = system_ts + (MIXER_DELAY_MS * WALLCLOCK_RATE / 1000); unsigned long long user_put_ts = put_ts * (GetCurrentSampleRate()/100) / (WALLCLOCK_RATE/100); channel->put(user_put_ts,(short*)buffer,samples); bstate->mixed_channel->get(user_put_ts,tmp_buffer,samples); mix_add(tmp_buffer,tmp_buffer,(short*)buffer,samples); bstate->mixed_channel->put(user_put_ts,tmp_buffer,samples); bstate->last_ts = put_ts + (samples * (WALLCLOCK_RATE/100) / (GetCurrentSampleRate()/100)); } else { /* ERROR("XXDebugMixerXX: MultiPartyMixer::PutChannelPacket: " "channel #%i doesn't exist\n",channel_id); DBG("XXDebugMixer:: PutChannelPacket failed ts=%u", ts); for (std::deque<MixerBufferState>::iterator it = buffer_state.begin(); it != buffer_state.end(); it++) { DEBUG_MIXER_BUFFER_STATE(*it, "on PutChannelPacket failure"); }*/ } }
void Packet::init(const ShortSample *data, unsigned int size, unsigned int ts) { size = PCM16_S2B(size); if (size > sizeof(m_data)) size = sizeof(m_data); m_size = PCM16_B2S(size); memcpy(m_data, data, size); m_ts = ts; }
void AmRtpAudio::add_to_history(int16_t *buffer, unsigned int size) { if (!use_default_plc) return; #ifdef USE_SPANDSP_PLC plc_rx(plc_state, buffer, PCM16_B2S(size)); #else // USE_SPANDSP_PLC int16_t* buf_offset = buffer; unsigned int sample_rate = getSampleRate(); for(unsigned int i=0; i<(PCM16_B2S(size)/FRAMESZ); i++){ fec->addtohistory(buf_offset); buf_offset += FRAMESZ; } #endif // USE_SPANDSP_PLC }
void AmMultiPartyMixer::GetChannelPacket(unsigned int channel_id, unsigned long long system_ts, unsigned char* buffer, unsigned int& size, unsigned int& output_sample_rate) { if (!size) return; assert(size <= AUDIO_BUFFER_SIZE); unsigned int last_ts = system_ts + (PCM16_B2S(size) * (WALLCLOCK_RATE/100) / (GetCurrentSampleRate()/100)); std::deque<MixerBufferState>::iterator bstate = findBufferStateForReading(GetCurrentSampleRate(), last_ts); SampleArrayShort* channel = 0; if(bstate != buffer_state.end() && (channel = bstate->get_channel(channel_id)) != 0) { unsigned int samples = PCM16_B2S(size) * (bstate->sample_rate/100) / (GetCurrentSampleRate()/100); assert(samples <= PCM16_B2S(AUDIO_BUFFER_SIZE)); unsigned long long cur_ts = system_ts * (bstate->sample_rate/100) / (WALLCLOCK_RATE/100); bstate->mixed_channel->get(cur_ts,tmp_buffer,samples); channel->get(cur_ts,(short*)buffer,samples); mix_sub(tmp_buffer,tmp_buffer,(short*)buffer,samples); scale((short*)buffer,tmp_buffer,samples); size = PCM16_S2B(samples); output_sample_rate = bstate->sample_rate; } else if (bstate != buffer_state.end()) { memset(buffer,0,size); output_sample_rate = GetCurrentSampleRate(); //DBG("XXDebugMixerXX: GetChannelPacket returned zeroes, ts=%u, last_ts=%u, output_sample_rate=%u", ts, last_ts, output_sample_rate); } else { /* ERROR("XXDebugMixerXX: MultiPartyMixer::GetChannelPacket: " "channel #%i doesn't exist\n",channel_id); DBG("XXDebugMixerXX: GetChannelPacket failed, ts=%u", ts); for (std::deque<MixerBufferState>::iterator it = buffer_state.begin(); it != buffer_state.end(); it++) { DEBUG_MIXER_BUFFER_STATE(*it, "on GetChannelPacket failure"); }*/ } cleanupBufferStates(last_ts); }
unsigned int AmRtpAudio::default_plc(unsigned char* out_buf, unsigned int size, unsigned int channels, unsigned int sample_rate) { short* buf_offset = (short*)out_buf; #ifdef USE_SPANDSP_PLC plc_fillin(plc_state, buf_offset, PCM16_B2S(size)); #else for(unsigned int i=0; i<(PCM16_B2S(size)/FRAMESZ); i++){ fec->dofe(buf_offset); buf_offset += FRAMESZ; } #endif // USE_SPANDSP_PLC return PCM16_S2B(buf_offset - (short*)out_buf); }
void AmBufferedAudio::input_get_audio(unsigned int user_ts) { if (r && (r != w)) { // move contents to beginning of buffer memmove(output_buffer, &output_buffer[r], w-r); w -= r; r = 0; } while (w < full_buffer_thresh) { int size = calcBytesToRead(PCM16_B2S(output_buffer_size - w)); // DBG("calc %d bytes to read\n", size); // resync might be delayed until buffer empty // (but output resync never happens) size = read(user_ts + PCM16_B2S(w-r),size); // DBG("read returned size = %d\n",size); if(size <= 0){ err_code = size; eof = true; return; } size = decode(size); if(size < 0) { // DBG("decode returned %i\n",size); err_code = size; eof = true; return; } // DBG("decode returned %i\n",size); size = downMixChannels(size); size = downMixRate(size); if(size>0) { memcpy(&output_buffer[w],(unsigned char*)samples,size); w+=size; } } }
void RtmpAudio::process_recv_queue(unsigned int ref_ts) { int size; // flush the recv queue into the playout buffer m_q_recv.lock(); while(!q_recv.empty()){ RTMPPacket p = q_recv.front(); q_recv.pop(); m_q_recv.unlock(); //TODO: // - copy RTMP payload into this->samples // - decode // - put packet in playout buffer if(p.m_nBodySize <= (unsigned int)AUDIO_BUFFER_SIZE){ size = p.m_nBodySize-1; memcpy((unsigned char*)samples, p.m_body+1, size); size = decode(size); if(size <= 0){ ERROR("decode() returned %i\n",size); return; } // TODO: generate some reasonable RTP timestamp // bool begin_talk = false; if(!recv_offset_i){ recv_rtp_offset = ref_ts; recv_rtmp_offset = p.m_nTimeStamp; recv_offset_i = true; begin_talk = true; } unsigned int rtp_ts = (p.m_nTimeStamp - recv_rtmp_offset) * (SPEEX_WB_SAMPLE_RATE/1000); playout_buffer.write(ref_ts, rtp_ts, (ShortSample*)((unsigned char *)samples), PCM16_B2S(size), begin_talk); RTMPPacket_Free(&p); } m_q_recv.lock(); } m_q_recv.unlock(); }
void AmPlayoutBuffer::write(u_int32_t ref_ts, u_int32_t rtp_ts, int16_t* buf, u_int32_t len, bool begin_talk) { unsigned int mapped_ts; if(!recv_offset_i) { recv_offset = rtp_ts - ref_ts; recv_offset_i = true; DBG("initialized recv_offset with %u (%u - %u)\n", recv_offset, ref_ts, rtp_ts); mapped_ts = r_ts = w_ts = ref_ts; } else { mapped_ts = rtp_ts - recv_offset; // resync if( ts_less()(mapped_ts, ref_ts - MAX_DELAY/2) || !ts_less()(mapped_ts, ref_ts + MAX_DELAY) ){ DBG("resync needed: reference ts = %u; write ts = %u\n", ref_ts, mapped_ts); recv_offset = rtp_ts - ref_ts; mapped_ts = r_ts = w_ts = ref_ts; } } if(!last_ts_i) { last_ts = mapped_ts; last_ts_i = true; } if(ts_less()(last_ts, mapped_ts) && !begin_talk && (mapped_ts - last_ts <= PLC_MAX_SAMPLES)) { unsigned char tmp[AUDIO_BUFFER_SIZE * 2]; int l_size = m_plcbuffer->conceal_loss(mapped_ts - last_ts, tmp); if (l_size>0) { direct_write_buffer(last_ts, (ShortSample*)tmp, PCM16_B2S(l_size)); } } write_buffer(ref_ts, mapped_ts, buf, len); m_plcbuffer->add_to_history(buf, PCM16_S2B(len)); // update last_ts to end of received packet // if not out-of-sequence if (ts_less()(last_ts, mapped_ts) || last_ts == mapped_ts) last_ts = mapped_ts + len; }
unsigned int AmAudio::downMix(unsigned int size) { unsigned int s = size; if(fmt->channels == 2){ stereo2mono(samples.back_buffer(),(unsigned char*)samples,s); samples.swap(); } #ifdef USE_LIBSAMPLERATE if (fmt->rate != SYSTEM_SAMPLERATE) { if (!resample_state) { int src_error; // for better quality but more CPU usage, use SRC_SINC_ converters resample_state = src_new(SRC_LINEAR, 1, &src_error); if (!resample_state) { ERROR("samplerate initialization error: "); } } if (resample_state) { if (resample_buf_samples + PCM16_B2S(s) > PCM16_B2S(AUDIO_BUFFER_SIZE) * 2) { WARN("resample input buffer overflow! (%d)\n", resample_buf_samples + PCM16_B2S(s)); } else { signed short* samples_s = (signed short*)(unsigned char*)samples; src_short_to_float_array(samples_s, &resample_in[resample_buf_samples], PCM16_B2S(s)); resample_buf_samples += PCM16_B2S(s); } SRC_DATA src_data; src_data.data_in = resample_in; src_data.input_frames = resample_buf_samples; src_data.data_out = resample_out; src_data.output_frames = PCM16_B2S(AUDIO_BUFFER_SIZE); src_data.src_ratio = (double)SYSTEM_SAMPLERATE / (double)fmt->rate; src_data.end_of_input = 0; int src_err = src_process(resample_state, &src_data); if (src_err) { DBG("resample error: '%s'\n", src_strerror(src_err)); }else { signed short* samples_s = (signed short*)(unsigned char*)samples; src_float_to_short_array(resample_out, samples_s, src_data.output_frames_gen); s = PCM16_S2B(src_data.output_frames_gen); if (resample_buf_samples != (unsigned int)src_data.input_frames_used) { memmove(resample_in, &resample_in[src_data.input_frames_used], (resample_buf_samples - src_data.input_frames_used) * sizeof(float)); } resample_buf_samples = resample_buf_samples - src_data.input_frames_used; } } } #endif return s; }
int AmRingTone::read(unsigned int user_ts, unsigned int size) { int ts_on = on_period*(SYSTEM_SAMPLECLOCK_RATE/1000); int ts_off = off_period*(SYSTEM_SAMPLECLOCK_RATE/1000); int t = user_ts % (ts_on + ts_off); if(length < 0) return -1; if(t >= ts_on){ memset((unsigned char*)samples,0,size); return size; } short* s = (short*)((unsigned char*)samples); for(unsigned int i=0; i<PCM16_B2S(size); i++, s++, t++){ if(t < ts_on){ float fs = sin((float(t*freq)/(float)SYSTEM_SAMPLECLOCK_RATE)*2.0*PI)*15000.0; if(freq2 != 0) fs += sin((float(t*freq2)/(float)SYSTEM_SAMPLECLOCK_RATE)*2.0*PI)*15000.0; *s = (short)(fs); } else *s = 0; } if(length != 0){ length -= PCM16_B2S(size) / (SYSTEM_SAMPLECLOCK_RATE/1000); if(!length) length--; } return size; }
unsigned int AmLibSamplerateResamplingState::resample(unsigned char* samples, unsigned int s, double ratio) { DBG("resampling packet of size %d with ratio %f", s, ratio); if (!resample_state) { int src_error; // for better quality but more CPU usage, use SRC_SINC_ converters resample_state = src_new(SRC_LINEAR, 1, &src_error); if (!resample_state) { ERROR("samplerate initialization error: "); } } if (resample_state) { if (resample_buf_samples + PCM16_B2S(s) > PCM16_B2S(AUDIO_BUFFER_SIZE) * 2) { WARN("resample input buffer overflow! (%lu)\n", resample_buf_samples + PCM16_B2S(s)); } else if (resample_out_buf_samples + (PCM16_B2S(s) * ratio) + 20 > PCM16_B2S(AUDIO_BUFFER_SIZE)) { WARN("resample: possible output buffer overflow! (%lu)\n", (resample_out_buf_samples + (size_t) ((PCM16_B2S(s) * ratio)) + 20)); } else { signed short* samples_s = (signed short*)samples; src_short_to_float_array(samples_s, &resample_in[resample_buf_samples], PCM16_B2S(s)); resample_buf_samples += PCM16_B2S(s); } SRC_DATA src_data; src_data.data_in = resample_in; src_data.input_frames = resample_buf_samples; src_data.data_out = &resample_out[resample_out_buf_samples]; src_data.output_frames = PCM16_B2S(AUDIO_BUFFER_SIZE); src_data.src_ratio = ratio; src_data.end_of_input = 0; int src_err = src_process(resample_state, &src_data); if (src_err) { DBG("resample error: '%s'\n", src_strerror(src_err)); }else { signed short* samples_s = (signed short*)(unsigned char*)samples; resample_out_buf_samples += src_data.output_frames_gen; s *= ratio; src_float_to_short_array(resample_out, samples_s, PCM16_B2S(s)); DBG("resample: output_frames_gen = %ld", src_data.output_frames_gen); if (resample_buf_samples != (unsigned int)src_data.input_frames_used) { memmove(resample_in, &resample_in[src_data.input_frames_used], (resample_buf_samples - src_data.input_frames_used) * sizeof(float)); } resample_buf_samples = resample_buf_samples - src_data.input_frames_used; if (resample_out_buf_samples != s) { memmove(resample_out, &resample_out[PCM16_B2S(s)], (resample_out_buf_samples - PCM16_B2S(s)) * sizeof(float)); } resample_out_buf_samples -= PCM16_B2S(s); } } DBG("resample: output size is %d", s); return s; }
/* @param wallclock_ts [in] the current ts in the audio buffer */ int AmRtpAudio::receive(unsigned long long system_ts) { int size; unsigned int rtp_ts; int new_payload = -1; if(!fmt.get() || (!playout_buffer.get())) { DBG("audio format not initialized\n"); return RTP_ERROR; } unsigned int wallclock_ts = scaleSystemTS(system_ts); while(true) { size = AmRtpStream::receive((unsigned char*)samples, (unsigned int)AUDIO_BUFFER_SIZE, rtp_ts, new_payload); if(size <= 0) { switch(size){ case 0: break; case RTP_DTMF: case RTP_UNKNOWN_PL: case RTP_PARSE_ERROR: continue; case RTP_TIMEOUT: //FIXME: postRequest(new SchedRequest(AmMediaProcessor::RemoveSession,s)); // post to the session (FIXME: is session always set? seems to be...) session->postEvent(new AmRtpTimeoutEvent()); return -1; case RTP_BUFFER_SIZE: default: ERROR("AmRtpStream::receive() returned %i\n",size); //FIXME: postRequest(new SchedRequest(AmMediaProcessor::ClearSession,s)); // or AmMediaProcessor::instance()->clearSession(session); return -1; break; } break; } if (// don't process if we don't need to // ignore CN COMFORT_NOISE_PAYLOAD_TYPE == new_payload || // ignore packet if payload not found setCurrentPayload(new_payload) ){ playout_buffer->clearLastTs(); continue; } size = decode(size); if(size <= 0){ ERROR("decode() returned %i\n",size); return -1; } // This only works because the possible ratio (Rate/TSRate) // is 2. Rate and TSRate are only different in case of g722. // For g722, TSRate=8000 and Rate=16000 // AmAudioRtpFormat* rtp_fmt = (AmAudioRtpFormat*)fmt.get(); unsigned long long adjusted_rtp_ts = rtp_ts; if(rtp_fmt->getRate() != rtp_fmt->getTSRate()) { adjusted_rtp_ts = (unsigned long long)rtp_ts * (unsigned long long)rtp_fmt->getRate() / (unsigned long long)rtp_fmt->getTSRate(); } playout_buffer->write(wallclock_ts, adjusted_rtp_ts, (ShortSample*)((unsigned char *)samples), PCM16_B2S(size), begin_talk); if(!active) { DBG("switching to active-mode\t(ts=%u;stream=%p)\n", rtp_ts,this); active = true; } } return size; }