void TransportCCRTP::flush() { if( sendBuffer->getReadyCount() > 0 ) { uint32 current_timestamp = socket->getCurrentTimestamp(); //printf("%ld %ld\n", current_timestamp, timestamp); //fprintf(out1, "%ld\n", current_timestamp); //fprintf(out2, "%ld\n", timestamp); if(seq32_t(timestamp) <= seq32_t(current_timestamp + framesPerPacket)) { socket->setExpireTimeout(frameSize*framesPerPacket * 1000); char *buf; int length = sendBuffer->peekMessage(&buf); socket->putData(timestamp, (unsigned char*)buf, length); sendBuffer->skipMessage(); timestamp += framesPerPacket; } else { //printf("discarding surplus of audio samples, current timestamp: %ld, timestamp: %ld\n", //current_timestamp, timestamp); fflush(stdout); } if(current_timestamp > timestamp+2000) timestamp = socket->getCurrentTimestamp(); } sendBuffer->debug(); }
// NOTE: no operations on the phone object are allowed inside the run() method. // Such an operation needs a lock on the transaction layer. The destructor // on audio_rx is called while this lock is locked. The destructor waits // in a busy loop for the run() method to finish. If the run() method would // need the phone lock, this would lead to a dead lock (and a long trip // in debug hell!) void t_audio_rx::run(void) { //struct timeval debug_timer; unsigned short sound_payload_size; uint32 dtmf_rtp_timestamp; phone->add_prohibited_thread(); ui->add_prohibited_thread(); // This flag indicates if we are currently in a silence period. // The start of a new stream is assumed to start in silence, such // that the very first RTP packet will be marked. bool silence_period = true; uint64 silence_nsamples = 0; // duration in samples // This flag indicates if a sound frame can be suppressed bool suppress_samples = false; // The running flag is set already in t_audio_session::run to prevent // a crash when the thread gets destroyed before it starts running. // is_running = true; // For a 3-way conference only the main receiver has access // to the dsp. if (!is_3way || is_main_rx_3way) { // Enable recording if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(), sys_config->get_dev_mic())) { input_device->enable(true, true); } else { input_device->enable(false, true); } // If the stream is stopped for call-hold, then the buffer might // be filled with old sound samples. input_device->flush(false, true); } // Synchronize the timestamp driven by the sampling rate // of the recording with the timestamp of the RTP session. // As the RTP session is already created in advance, the // RTP clock is a bit ahead already. timestamp = rtp_session->getCurrentTimestamp() + nsamples; // This loop keeps running until the stop_running flag is set to true. // When a call is being released the stop_running flag is set to true. // At that moment the lock on the transaction layer (phone) is taken. // So do not use operations that take the phone lock, otherwise a // dead lock may occur during call release. while (true) { if (stop_running) break; if (dtmf_player) { rtp_session->setMark(false); // Skip samples from sound card input_device->read(input_sample_buf, SAMPLE_BUF_SIZE); sound_payload_size = dtmf_player->get_payload( payload, payload_size, timestamp, dtmf_rtp_timestamp); silence_period = false; } else if (get_dtmf_event()) { // RFC 2833 // Set marker in first RTP packet of a DTMF event rtp_session->setMark(true); // Skip samples from sound card input_device->read(input_sample_buf, SAMPLE_BUF_SIZE); assert(dtmf_player); sound_payload_size = dtmf_player->get_payload( payload, payload_size, timestamp, dtmf_rtp_timestamp); silence_period = false; } else if (get_sound_samples(sound_payload_size, suppress_samples)) { if (suppress_samples && use_nat_keepalive) { if (!silence_period) silence_nsamples = 0; // Send a silence packet at the NAT keep alive interval // to keep the NAT bindings for RTP fresh. silence_nsamples += SAMPLE_BUF_SIZE / 2; if (silence_nsamples > (uint64_t)user_config->get_timer_nat_keepalive() * 1000 * audio_encoder->get_sample_rate()) { suppress_samples = false; } } if (silence_period && !suppress_samples) { // RFC 3551 4.1 // Set marker bit in first RTP packet after silence rtp_session->setMark(true); } else { rtp_session->setMark(false); } silence_period = suppress_samples; } else { continue; } // If timestamp is more than 1 payload size ahead of the clock of // the ccRTP stack, then drop the current payload and do not advance // the timestamp. This will happen if the DSP delivers more // sound samples than the set sample rate. To compensate for this // samples must be dropped. uint32 current_timestamp = rtp_session->getCurrentTimestamp(); if (seq32_t(timestamp) <= seq32_t(current_timestamp + nsamples)) { if (dtmf_player) { // Send DTMF payload rtp_session->putData(dtmf_rtp_timestamp, payload, sound_payload_size); // If DTMF has ended then set payload back to sound if (dtmf_player->finished()) { set_sound_payload_format(); MEMMAN_DELETE(dtmf_player); delete dtmf_player; dtmf_player = NULL; } } else if (!suppress_samples) { // Send sound samples // Set the expire timeout to the jitter buffer size. // This allows for old packets still to be sent out. rtp_session->setExpireTimeout(MAX_OUT_AUDIO_DELAY_MS * 1000); rtp_session->putData(timestamp, payload, sound_payload_size); } timestamp += nsamples; } else { log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG); log_file->write_raw("Audio rx line "); log_file->write_raw(get_line()->get_line_number()+1); log_file->write_raw(": discarded surplus of sound samples.\n"); log_file->write_raw("Timestamp: "); log_file->write_raw(timestamp); log_file->write_endl(); log_file->write_raw("Current timestamp: "); log_file->write_raw(current_timestamp); log_file->write_endl(); log_file->write_raw("nsamples: "); log_file->write_raw(nsamples); log_file->write_endl(); log_file->write_footer(); } // If there is enough data in the DSP buffers to fill another // RTP packet then do not sleep, but immediately go to the // next cycle to play out the data. Probably this thread did // not get enough time, so the buffer filled up. The far end // jitter buffer has to cope with the jitter caused by this. if (is_3way && !is_main_rx_3way) { if (media_3way_peer_rx->size_content() >= SAMPLE_BUF_SIZE) { continue; } } else { if (input_device->get_buffer_space(true) >= SAMPLE_BUF_SIZE) continue; } // There is no data left in the DSP buffers to play out anymore. // So the timestamp must be in sync with the clock of the ccRTP // stack. It might get behind if the sound cards samples a bit // slower than the set sample rate. Advance the timestamp to get // in sync again. current_timestamp = rtp_session->getCurrentTimestamp(); if (seq32_t(timestamp) <= seq32_t(current_timestamp - (JITTER_BUF_MS / audio_encoder->get_ptime()) * nsamples)) { timestamp += nsamples * (JITTER_BUF_MS / audio_encoder->get_ptime()); log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG); log_file->write_raw("Audio rx line "); log_file->write_raw(get_line()->get_line_number()+1); log_file->write_raw(": timestamp forwarded by "); log_file->write_raw(nsamples * (JITTER_BUF_MS / audio_encoder->get_ptime())); log_file->write_endl(); log_file->write_raw("Timestamp: "); log_file->write_raw(timestamp); log_file->write_endl(); log_file->write_raw("Current timestamp: "); log_file->write_raw(current_timestamp); log_file->write_endl(); log_file->write_raw("nsamples: "); log_file->write_raw(nsamples); log_file->write_endl(); log_file->write_footer(); } } phone->remove_prohibited_thread(); ui->remove_prohibited_thread(); is_running = false; }