void AudioRtpSession::startSender() { if (not send_.enabled or send_.holding) { RING_WARN("Audio sending disabled"); if (sender_) { if (socketPair_) socketPair_->interrupt(); sender_.reset(); } return; } if (sender_) RING_WARN("Restarting audio sender"); // be sure to not send any packets before saving last RTP seq value socketPair_->stopSendOp(); if (sender_) initSeqVal_ = sender_->getLastSeqValue() + 1; try { sender_.reset(); socketPair_->stopSendOp(false); sender_.reset(new AudioSender(callID_, getRemoteRtpUri(), send_, *socketPair_, initSeqVal_, muteState_, mtu_)); } catch (const MediaEncoderException &e) { RING_ERR("%s", e.what()); send_.enabled = false; } }
std::shared_ptr<SipTransport> SipTransportBroker::getUdpTransport(const SipTransportDescr& descr) { std::lock_guard<std::mutex> lock(transportMapMutex_); auto itp = udpTransports_.find(descr); if (itp != udpTransports_.end()) { auto it = transports_.find(itp->second); if (it != transports_.end()) { if (auto spt = it->second.lock()) { RING_DBG("Reusing transport %s", descr.toString().c_str()); return spt; } else { // Transport still exists but have not been destroyed yet. RING_WARN("Recycling transport %s", descr.toString().c_str()); auto ret = std::make_shared<SipTransport>(itp->second); it->second = ret; return ret; } } else { RING_WARN("Cleaning up UDP transport %s", descr.toString().c_str()); udpTransports_.erase(itp); } } auto ret = createUdpTransport(descr); if (ret) { udpTransports_[descr] = ret->get(); transports_[ret->get()] = ret; } return ret; }
bool AudioRecord::openFile() { #ifndef RING_UWP fileHandle_.reset(); // do it before calling fileExists() const bool doAppend = fileExists(); const int access = doAppend ? SFM_RDWR : SFM_WRITE; RING_DBG("Opening file %s with format %s", getFilename().c_str(), sndFormat_.toString().c_str()); fileHandle_.reset(new SndfileHandle (getFilename().c_str(), access, SF_FORMAT_WAV | SF_FORMAT_PCM_16, sndFormat_.nb_channels, sndFormat_.sample_rate)); // check overloaded boolean operator if (!*fileHandle_) { RING_WARN("Could not open WAV file!"); fileHandle_.reset(); return false; } if (doAppend and fileHandle_->seek(0, SEEK_END) < 0) RING_WARN("Couldn't seek to the end of the file "); return true; #else return false; #endif }
void JackLayer::read(AudioBuffer &buffer) { for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) { const size_t incomingSamples = jack_ringbuffer_read_space(in_ringbuffers_[i]) / sizeof(captureFloatBuffer_[0]); if (!incomingSamples) continue; captureFloatBuffer_.resize(incomingSamples); buffer.resize(incomingSamples); // write to output const size_t from_ringbuffer = jack_ringbuffer_read_space(in_ringbuffers_[i]); const size_t expected_bytes = std::min(incomingSamples * sizeof(captureFloatBuffer_[0]), from_ringbuffer); // FIXME: while we have samples to write AND while we have space to write them const size_t read_bytes = jack_ringbuffer_read(in_ringbuffers_[i], (char *) captureFloatBuffer_.data(), expected_bytes); if (read_bytes < expected_bytes) { RING_WARN("Dropped %zu bytes", expected_bytes - read_bytes); break; } /* Write the data one frame at a time. This is * inefficient, but makes things simpler. */ // FIXME: this is braindead, we should write blocks of samples at a time // convert a vector of samples from 1 channel to a float vector convertFromFloat(captureFloatBuffer_, *buffer.getChannel(i)); } }
void* VideoInput::obtainFrame(int length) { std::lock_guard<std::mutex> lck(mutex_); /* allocate buffers. This is done there because it's only when the Android * application requests a buffer that we know its size */ for(auto& buffer : buffers_) { if (buffer.status == BUFFER_NOT_ALLOCATED) { allocateOneBuffer(buffer, length); } } /* search for an available frame */ for(auto& buffer : buffers_) { if (buffer.length == length && buffer.status == BUFFER_AVAILABLE) { buffer.status = BUFFER_CAPTURING; return buffer.data; } } RING_WARN("No buffer found"); return nullptr; }
void SIPCall::setTransport(std::shared_ptr<SipTransport> t) { const auto list_id = reinterpret_cast<uintptr_t>(this); if (transport_) transport_->removeStateListener(list_id); transport_ = t; if (transport_) { std::weak_ptr<SIPCall> wthis_ = std::static_pointer_cast<SIPCall>(shared_from_this()); // listen for transport destruction transport_->addStateListener(list_id, [wthis_] (pjsip_transport_state state, const pjsip_transport_state_info*) { if (auto this_ = wthis_.lock()) { // end the call if the SIP transport is shut down if (not SipTransport::isAlive(this_->transport_, state) and this_->getConnectionState() != ConnectionState::DISCONNECTED) { RING_WARN("[call:%s] Ending call because underlying SIP transport was closed", this_->getCallId().c_str()); this_->onFailure(ECONNRESET); } } }); } }
int JackLayer::process_capture(jack_nframes_t frames, void *arg) { JackLayer *context = static_cast<JackLayer*>(arg); for (unsigned i = 0; i < context->in_ringbuffers_.size(); ++i) { // read from input jack_default_audio_sample_t *in_buffers = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer(context->in_ports_[i], frames)); const size_t bytes_to_read = frames * sizeof(*in_buffers); size_t bytes_to_rb = jack_ringbuffer_write(context->in_ringbuffers_[i], (char *) in_buffers, bytes_to_read); // fill the rest with silence if (bytes_to_rb < bytes_to_read) { // TODO: set some flag for underrun? RING_WARN("Dropped %lu bytes", bytes_to_read - bytes_to_rb); } } /* Tell the ringbuffer thread there is work to do. If it is already * running, the lock will not be available. We can't wait * here in the process() thread, but we don't need to signal * in that case, because the ringbuffer thread will read all the * data queued before waiting again. */ if (context->ringbuffer_thread_mutex_.try_lock()) { context->data_ready_.notify_one(); context->ringbuffer_thread_mutex_.unlock(); } return 0; }
static AVPixelFormat getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats) { auto accel = static_cast<HardwareAccel*>(codecCtx->opaque); if (!accel) { // invalid state, try to recover return avcodec_default_get_format(codecCtx, formats); } for (int i = 0; formats[i] != AV_PIX_FMT_NONE; i++) { if (formats[i] == accel->format()) { accel->setWidth(codecCtx->coded_width); accel->setHeight(codecCtx->coded_height); accel->setProfile(codecCtx->profile); accel->setCodecCtx(codecCtx); if (accel->init()) return accel->format(); break; } } accel->fail(true); RING_WARN("Falling back to software decoding"); codecCtx->get_format = avcodec_default_get_format; codecCtx->get_buffer2 = avcodec_default_get_buffer2; for (int i = 0; formats[i] != AV_PIX_FMT_NONE; i++) { auto desc = av_pix_fmt_desc_get(formats[i]); if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { return formats[i]; } } return AV_PIX_FMT_NONE; }
int exportAccounts(std::vector<std::string> accountIDs, std::string filepath, std::string password) { if (filepath.empty() || !accountIDs.size()) { RING_ERR("Missing arguments"); return EINVAL; } std::size_t found = filepath.find_last_of(DIR_SEPARATOR_STR); auto toDir = filepath.substr(0,found); auto filename = filepath.substr(found+1); if (!fileutils::isDirectory(toDir)) { RING_ERR("%s is not a directory", toDir.c_str()); return ENOTDIR; } // Add Json::Value root; Json::Value array; for (size_t i = 0; i < accountIDs.size(); ++i) { auto detailsMap = Manager::instance().getAccountDetails(accountIDs[i]); if (detailsMap.empty()) { RING_WARN("Can't export account %s", accountIDs[i].c_str()); continue; } auto jsonAccount = accountToJsonValue(detailsMap); array.append(jsonAccount); } root["accounts"] = array; Json::FastWriter fastWriter; std::string output = fastWriter.write(root); // Compress std::vector<uint8_t> compressed; try { compressed = compress(output); } catch (const std::runtime_error& ex) { RING_ERR("Export failed: %s", ex.what()); return 1; } // Encrypt using provided password auto encrypted = dht::crypto::aesEncrypt(compressed, password); // Write try { fileutils::saveFile(toDir + DIR_SEPARATOR_STR + filename, encrypted); } catch (const std::runtime_error& ex) { RING_ERR("Export failed: %s", ex.what()); return EIO; } return 0; }
void AudioRtpSession::startReceiver() { if (not receive_.enabled or receive_.holding) { RING_WARN("Audio receiving disabled"); receiveThread_.reset(); return; } if (receiveThread_) RING_WARN("Restarting audio receiver"); auto accountAudioCodec = std::static_pointer_cast<AccountAudioCodecInfo>(receive_.codec); receiveThread_.reset(new AudioReceiveThread(callID_, accountAudioCodec->audioformat, receive_.receiving_sdp)); receiveThread_->addIOContext(*socketPair_); receiveThread_->startLoop(); }
void SipTransportBroker::transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info) { RING_DBG("pjsip transport@%p %s -> %s", tp, tp->info, SipTransport::stateToStr(state)); // First make sure that this transport is handled by us // and remove it from any mapping if destroy pending or done. std::shared_ptr<SipTransport> sipTransport; { std::lock_guard<std::mutex> lock(transportMapMutex_); auto key = transports_.find(tp); if (key == transports_.end()) { RING_WARN("spurious pjsip transport state change"); return; } sipTransport = key->second.lock(); #if PJ_VERSION_NUM > (2 << 24 | 1 << 16) bool destroyed = state == PJSIP_TP_STATE_DESTROY; #else bool destroyed = tp->is_destroying; #endif // maps cleanup if (destroyed) { RING_DBG("unmap pjsip transport@%p {SipTransport@%p}", tp, sipTransport.get()); transports_.erase(key); // If UDP const auto type = tp->key.type; if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) { const auto updKey = std::find_if( udpTransports_.cbegin(), udpTransports_.cend(), [tp](const std::pair<SipTransportDescr, pjsip_transport*>& pair) { return pair.second == tp; }); if (updKey != udpTransports_.cend()) udpTransports_.erase(updKey); } } } // Propagate the event to the appropriate transport // Note the SipTransport may not be in our mappings if marked as dead if (sipTransport) sipTransport->stateCallback(state, info); }
/** * Make given call ID a reader of given ring buffer */ void RingBufferPool::addReaderToRingBuffer(std::shared_ptr<RingBuffer> rbuf, const std::string& call_id) { if (call_id != DEFAULT_ID and rbuf->id == call_id) RING_WARN("RingBuffer has a readoffset on itself"); rbuf->createReadOffset(call_id); readBindingsMap_[call_id].insert(rbuf); // bindings list created if not existing RING_DBG("Bind rbuf '%s' to callid '%s'", rbuf->id.c_str(), call_id.c_str()); }
RingBufferPool::~RingBufferPool() { readBindingsMap_.clear(); defaultRingBuffer_.reset(); // Verify ringbuffer not removed yet // XXXX: With a good design this should never happen! :-P for (const auto& item : ringBufferMap_) { const auto& weak = item.second; if (not weak.expired()) RING_WARN("Leaking RingBuffer '%s'", item.first.c_str()); } }
void SinkClient::setFrameSize(int width, int height) { if (width > 0 and height > 0) { RING_WARN("Start sink <%s / %s>, size=%dx%d, mixer=%u", getId().c_str(), openedName().c_str(), width, height, mixer_); emitSignal<DRing::VideoSignal::DecodingStarted>(getId(), openedName(), width, height, mixer_); started_ = true; } else if (started_) { RING_ERR("Stop sink <%s / %s>, mixer=%u", getId().c_str(), openedName().c_str(), mixer_); emitSignal<DRing::VideoSignal::DecodingStopped>(getId(), openedName(), mixer_); started_ = false; } }
bool SIPCall::onhold() { if (not setState(CallState::HOLD)) return false; stopAllMedia(); if (getConnectionState() == ConnectionState::CONNECTED) { if (SIPSessionReinvite() != PJ_SUCCESS) RING_WARN("[call:%s] Reinvite failed", getCallId().c_str()); } return true; }
void AudioRecord::recData(AudioBuffer& buffer) { #ifndef RING_UWP if (not recordingEnabled_) return; auto interleaved = buffer.interleave(); const int nSamples = interleaved.size(); if (fileHandle_->write(interleaved.data(), nSamples) != nSamples) { RING_WARN("Could not record data!"); } else { fileHandle_->writeSync(); } #endif }
int PortAudioLayer::paInputCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { (void) outputBuffer; (void) timeInfo; (void) statusFlags; auto ref = (PortAudioLayer*)userData; auto in = (AudioSample*)inputBuffer; if (framesPerBuffer == 0) { RING_WARN("No frames for input."); return paContinue; } const auto mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat(); bool resample = ref->audioInputFormat_.sample_rate != mainBufferFormat.sample_rate; AudioBuffer inBuff(framesPerBuffer, ref->audioInputFormat_); inBuff.deinterleave(in, framesPerBuffer, ref->audioInputFormat_.nb_channels); inBuff.applyGain(ref->isCaptureMuted_ ? 0.0 : ref->captureGain_); if (resample) { auto outSamples = framesPerBuffer / (static_cast<double>(ref->audioInputFormat_.sample_rate) / mainBufferFormat.sample_rate); AudioBuffer out(outSamples, mainBufferFormat); ref->inputResampler_->resample(inBuff, out); ref->dcblocker_.process(out); ref->mainRingBuffer_->put(out); } else { ref->dcblocker_.process(inBuff); ref->mainRingBuffer_->put(inBuff); } return paContinue; }
void JackLayer::write(AudioBuffer &buffer, std::vector<float> &floatBuffer) { for (unsigned i = 0; i < out_ringbuffers_.size(); ++i) { const unsigned inChannel = std::min(i, buffer.channels() - 1); convertToFloat(*buffer.getChannel(inChannel), floatBuffer); // write to output const size_t to_ringbuffer = jack_ringbuffer_write_space(out_ringbuffers_[i]); const size_t write_bytes = std::min(buffer.frames() * sizeof(floatBuffer[0]), to_ringbuffer); // FIXME: while we have samples to write AND while we have space to write them const size_t written_bytes = jack_ringbuffer_write(out_ringbuffers_[i], (const char *) floatBuffer.data(), write_bytes); if (written_bytes < write_bytes) RING_WARN("Dropped %zu bytes for channel %u", write_bytes - written_bytes, i); } }
void VideoMixer::start_sink() { stop_sink(); if (width_ == 0 or height_ == 0) { RING_WARN("MX: unable to start with zero-sized output"); return; } if (not sink_->start()) { RING_ERR("MX: sink startup failed"); return; } if (this->attach(sink_.get())) sink_->setFrameSize(width_, height_); }
bool SIPCall::internalOffHold(const std::function<void()>& sdp_cb) { if (not setState(CallState::ACTIVE)) return false; sdp_cb(); if (getConnectionState() == ConnectionState::CONNECTED) { if (SIPSessionReinvite() != PJ_SUCCESS) { RING_WARN("[call:%s] resuming hold", getCallId().c_str()); onhold(); return false; } } return true; }
bool ToneControl::setAudioFile(const std::string& file) { std::lock_guard<std::mutex> lk(mutex_); if (audioFile_) { emitSignal<DRing::CallSignal::RecordPlaybackStopped>(audioFile_->getFilePath()); audioFile_.reset(); } try { audioFile_.reset(new AudioFile(file, sampleRate_)); } catch (const AudioFileException& e) { RING_WARN("Audio file error: %s", e.what()); } return static_cast<bool>(audioFile_); }
void SIPCall::answer() { auto& account = getSIPAccount(); if (not inv) throw VoipLinkException("No invite session for this call"); if (!inv->neg) { RING_WARN("[call:%s] Negotiator is NULL, we've received an INVITE without an SDP", getCallId().c_str()); pjmedia_sdp_session *dummy = 0; getSIPVoIPLink()->createSDPOffer(inv.get(), &dummy); if (account.isStunEnabled()) updateSDPFromSTUN(); } pj_str_t contact(account.getContactHeader(transport_ ? transport_->get() : nullptr)); setContactHeader(&contact); pjsip_tx_data *tdata; if (!inv->last_answer) throw std::runtime_error("Should only be called for initial answer"); // answer with SDP if no SDP was given in initial invite (i.e. inv->neg is NULL) if (pjsip_inv_answer(inv.get(), PJSIP_SC_OK, NULL, !inv->neg ? sdp_->getLocalSdpSession() : NULL, &tdata) != PJ_SUCCESS) throw std::runtime_error("Could not init invite request answer (200 OK)"); // contactStr must stay in scope as long as tdata if (contactHeader_.slen) { RING_DBG("[call:%s] Answering with contact header: %.*s", getCallId().c_str(), (int)contactHeader_.slen, contactHeader_.ptr); sip_utils::addContactHeader(&contactHeader_, tdata); } if (pjsip_inv_send_msg(inv.get(), tdata) != PJ_SUCCESS) { inv.reset(); throw std::runtime_error("Could not send invite request answer (200 OK)"); } setState(CallState::ACTIVE, ConnectionState::CONNECTED); }
void AudioReceiveThread::process() { AudioFormat mainBuffFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat(); AudioFrame decodedFrame; switch (audioDecoder_->decode(decodedFrame)) { case MediaDecoder::Status::FrameFinished: audioDecoder_->writeToRingBuffer(decodedFrame, *ringbuffer_, mainBuffFormat); // Refresh the remote audio codec in the callback SmartInfo Smartools::getInstance().setRemoteAudioCodec(audioDecoder_->getDecoderName()); return; case MediaDecoder::Status::DecodeError: RING_WARN("decoding failure, trying to reset decoder..."); if (not setup()) { RING_ERR("fatal error, rx thread re-setup failed"); loop_.stop(); break; } if (not audioDecoder_->setupFromAudioData(format_)) { RING_ERR("fatal error, a-decoder setup failed"); loop_.stop(); break; } break; case MediaDecoder::Status::ReadError: RING_ERR("fatal error, read failed"); loop_.stop(); break; case MediaDecoder::Status::Success: case MediaDecoder::Status::EOFError: default: break; } }
void SIPCall::setupLocalSDPFromIce() { if (not iceTransport_) { RING_WARN("[call:%s] null icetransport, no attributes added to SDP", getCallId().c_str()); return; } if (waitForIceInitialization(DEFAULT_ICE_INIT_TIMEOUT) <= 0) { RING_ERR("[call:%s] Local ICE init failed", getCallId().c_str()); return; } sdp_->addIceAttributes(iceTransport_->getLocalAttributes()); // Add video and audio channels sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_AUDIO_RTP_COMPID)); sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_AUDIO_RTCP_COMPID)); #ifdef RING_VIDEO sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_VIDEO_RTP_COMPID)); sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_VIDEO_RTCP_COMPID)); #endif }
IpAddr ip_utils::getLocalAddr(pj_uint16_t family) { if (family == pj_AF_UNSPEC()) { #if HAVE_IPV6 family = pj_AF_INET6(); #else family = pj_AF_INET(); #endif } IpAddr ip_addr = {}; pj_status_t status = pj_gethostip(family, ip_addr.pjPtr()); if (status == PJ_SUCCESS) { return ip_addr; } #if HAVE_IPV6 RING_WARN("Could not get preferred address familly (%s)", (family == pj_AF_INET6()) ? "IPv6" : "IPv4"); family = (family == pj_AF_INET()) ? pj_AF_INET6() : pj_AF_INET(); status = pj_gethostip(family, ip_addr.pjPtr()); if (status == PJ_SUCCESS) return ip_addr; #endif RING_ERR("Could not get local IP"); return ip_addr; }
std::unique_ptr<HardwareAccel> makeHardwareAccel(AVCodecContext* codecCtx) { enum class AccelID { Vaapi, }; struct AccelInfo { AccelID type; std::string name; AVPixelFormat format; std::unique_ptr<HardwareAccel> (*create)(const std::string name, const AVPixelFormat format); }; /* Each item in this array reprensents a fully implemented hardware acceleration in Ring. * Each item should be enclosed in an #ifdef to prevent its compilation on an * unsupported platform (VAAPI for Linux Intel won't compile on a Mac). * A new item should be added when support for an acceleration has been added to Ring, * which is also supported by FFmpeg. * Steps to add an acceleration (after its implementation): * - Create an AccelID and add it to the switch statement * - Give it a name (this is used for the daemon logs) * - Specify its AVPixelFormat (the one used by FFmpeg: check pixfmt.h) * - Add a function pointer that returns an instance (makeHardwareAccel<> does this already) * Note: the include of the acceleration's header file must be guarded by the same #ifdef as * in this array. */ const AccelInfo accels[] = { #if RING_VAAPI { AccelID::Vaapi, "vaapi", AV_PIX_FMT_VAAPI, makeHardwareAccel<VaapiAccel> }, #endif }; std::vector<AccelID> possibleAccels = {}; switch (codecCtx->codec_id) { case AV_CODEC_ID_H264: case AV_CODEC_ID_MPEG4: case AV_CODEC_ID_H263P: possibleAccels.push_back(AccelID::Vaapi); break; case AV_CODEC_ID_VP8: break; default: break; } for (auto& info : accels) { for (auto& pa : possibleAccels) { if (info.type == pa) { auto accel = info.create(info.name, info.format); // don't break if the check fails, we want to check every possibility if (accel->check()) { codecCtx->get_format = getFormatCb; codecCtx->get_buffer2 = allocateBufferCb; codecCtx->thread_safe_callbacks = 1; codecCtx->thread_count = 1; RING_DBG("Succesfully set up '%s' acceleration", accel->name().c_str()); return accel; } } } } RING_WARN("Not using hardware acceleration"); return nullptr; }
void SIPCall::updateSDPFromSTUN() { RING_WARN("[call:%s] SIPCall::updateSDPFromSTUN() not implemented", getCallId().c_str()); }
void SIPCall::startAllMedia() { if (isSecure() && not transport_->isSecure()) { RING_ERR("[call:%s] Can't perform secure call over insecure SIP transport", getCallId().c_str()); onFailure(EPROTONOSUPPORT); return; } auto slots = sdp_->getMediaSlots(); unsigned ice_comp_id = 0; bool peer_holding {true}; int slotN = -1; for (const auto& slot : slots) { ++slotN; const auto& local = slot.first; const auto& remote = slot.second; if (local.type != remote.type) { RING_ERR("[call:%s] [SDP:slot#%u] Inconsistent media types between local and remote", getCallId().c_str(), slotN); continue; } RtpSession* rtp = local.type == MEDIA_AUDIO ? static_cast<RtpSession*>(avformatrtp_.get()) #ifdef RING_VIDEO : static_cast<RtpSession*>(&videortp_); #else : nullptr; #endif if (not rtp) continue; if (!local.codec) { RING_WARN("[call:%s] [SDP:slot#%u] Missing local codec", getCallId().c_str(), slotN); continue; } if (!remote.codec) { RING_WARN("[call:%s] [SDP:slot#%u] Missing remote codec", getCallId().c_str(), slotN); continue; } peer_holding &= remote.holding; if (isSecure() && (not local.crypto || not remote.crypto)) { RING_ERR("[call:%s] [SDP:slot#%u] Can't perform secure call over insecure RTP transport", getCallId().c_str(), slotN); continue; } #ifdef RING_VIDEO if (local.type == MEDIA_VIDEO) videortp_.switchInput(videoInput_); #endif rtp->updateMedia(remote, local); if (isIceRunning()) { rtp->start(newIceSocket(ice_comp_id + 0), newIceSocket(ice_comp_id + 1)); ice_comp_id += 2; } else rtp->start(); switch (local.type) { #ifdef RING_VIDEO case MEDIA_VIDEO: isVideoMuted_ = videoInput_.empty(); break; #endif case MEDIA_AUDIO: isAudioMuted_ = not rtp->isSending(); break; default: break; } }
SipIceTransport::SipIceTransport(pjsip_endpoint* endpt, pj_pool_t& /* pool */, long /* t_type */, const std::shared_ptr<IceTransport>& ice, int comp_id) : pool_(nullptr, pj_pool_release) , rxPool_(nullptr, pj_pool_release) , trData_() , rdata_() , ice_(ice) , comp_id_(comp_id) { trData_.self = this; if (not ice or not ice->isRunning()) throw std::logic_error("ice transport must exist and negotiation completed"); RING_DBG("SipIceTransport@%p {tr=%p}", this, &trData_.base); auto& base = trData_.base; pool_.reset(pjsip_endpt_create_pool(endpt, "SipIceTransport.pool", POOL_TP_INIT, POOL_TP_INC)); if (not pool_) throw std::bad_alloc(); auto pool = pool_.get(); pj_ansi_snprintf(base.obj_name, PJ_MAX_OBJ_NAME, "SipIceTransport"); base.endpt = endpt; base.tpmgr = pjsip_endpt_get_tpmgr(endpt); base.pool = pool; rdata_.tp_info.pool = pool; // FIXME: not destroyed in case of exception if (pj_atomic_create(pool, 0, &base.ref_cnt) != PJ_SUCCESS) throw std::runtime_error("Can't create PJSIP atomic."); // FIXME: not destroyed in case of exception if (pj_lock_create_recursive_mutex(pool, "SipIceTransport.mutex", &base.lock) != PJ_SUCCESS) throw std::runtime_error("Can't create PJSIP mutex."); auto remote = ice->getRemoteAddress(comp_id); RING_DBG("SipIceTransport: remote is %s", remote.toString(true).c_str()); pj_sockaddr_cp(&base.key.rem_addr, remote.pjPtr()); base.key.type = PJSIP_TRANSPORT_UDP;//t_type; base.type_name = (char*)pjsip_transport_get_type_name((pjsip_transport_type_e)base.key.type); base.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)base.key.type); base.info = (char*) pj_pool_alloc(pool, TRANSPORT_INFO_LENGTH); char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_ansi_snprintf(base.info, TRANSPORT_INFO_LENGTH, "%s to %s", base.type_name, pj_sockaddr_print(remote.pjPtr(), print_addr, sizeof(print_addr), 3)); base.addr_len = remote.getLength(); base.dir = PJSIP_TP_DIR_NONE;//is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; base.data = nullptr; /* Set initial local address */ auto local = ice->getDefaultLocalAddress(); pj_sockaddr_cp(&base.local_addr, local.pjPtr()); sockaddr_to_host_port(pool, &base.local_name, &base.local_addr); sockaddr_to_host_port(pool, &base.remote_name, remote.pjPtr()); base.send_msg = [](pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { auto& this_ = reinterpret_cast<TransportData*>(transport)->self; return this_->send(tdata, rem_addr, addr_len, token, callback); }; base.do_shutdown = [](pjsip_transport *transport) -> pj_status_t { auto& this_ = reinterpret_cast<TransportData*>(transport)->self; RING_WARN("SipIceTransport@%p: shutdown", this_); return PJ_SUCCESS; }; base.destroy = [](pjsip_transport *transport) -> pj_status_t { auto& this_ = reinterpret_cast<TransportData*>(transport)->self; RING_WARN("SipIceTransport@%p: destroy", this_); delete this_; return PJ_SUCCESS; }; /* Init rdata */ rxPool_.reset(pjsip_endpt_create_pool(base.endpt, "SipIceTransport.rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC)); if (not rxPool_) throw std::bad_alloc(); auto rx_pool = rxPool_.get(); rdata_.tp_info.pool = rx_pool; rdata_.tp_info.transport = &base; rdata_.tp_info.tp_data = this; rdata_.tp_info.op_key.rdata = &rdata_; pj_ioqueue_op_key_init(&rdata_.tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); rdata_.pkt_info.src_addr = base.key.rem_addr; rdata_.pkt_info.src_addr_len = sizeof(rdata_.pkt_info.src_addr); auto rem_addr = &base.key.rem_addr; pj_sockaddr_print(rem_addr, rdata_.pkt_info.src_name, sizeof(rdata_.pkt_info.src_name), 0); rdata_.pkt_info.src_port = pj_sockaddr_get_port(rem_addr); rdata_.pkt_info.len = 0; rdata_.pkt_info.zero = 0; if (pjsip_transport_register(base.tpmgr, &base) != PJ_SUCCESS) throw std::runtime_error("Can't register PJSIP transport."); is_registered_ = true; Manager::instance().registerEventHandler((uintptr_t)this, [this]{ loop(); }); }
int PortAudioLayer::paOutputCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { (void) inputBuffer; (void) timeInfo; (void) statusFlags; auto ref = (PortAudioLayer*)userData; auto out = (AudioSample*)outputBuffer; AudioFormat mainBufferAudioFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat(); bool resample = ref->audioFormat_.sample_rate != mainBufferAudioFormat.sample_rate; auto urgentFramesToGet = ref->urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID); if (urgentFramesToGet > 0) { RING_WARN("Getting urgent frames."); size_t totSample = std::min(framesPerBuffer, (unsigned long)urgentFramesToGet); ref->playbackBuff_.setFormat(ref->audioFormat_); ref->playbackBuff_.resize(totSample); ref->urgentRingBuffer_.get(ref->playbackBuff_, RingBufferPool::DEFAULT_ID); ref->playbackBuff_.applyGain(ref->isPlaybackMuted_ ? 0.0 : ref->playbackGain_); ref->playbackBuff_.interleave(out); Manager::instance().getRingBufferPool().discard(totSample, RingBufferPool::DEFAULT_ID); } unsigned normalFramesToGet = Manager::instance().getRingBufferPool().availableForGet(RingBufferPool::DEFAULT_ID); if (normalFramesToGet > 0) { double resampleFactor = 1.0; unsigned readableSamples = framesPerBuffer; if (resample) { resampleFactor = static_cast<double>(ref->audioFormat_.sample_rate) / mainBufferAudioFormat.sample_rate; readableSamples = std::ceil(framesPerBuffer / resampleFactor); } readableSamples = std::min(readableSamples, normalFramesToGet); ref->playbackBuff_.setFormat(ref->audioFormat_); ref->playbackBuff_.resize(readableSamples); Manager::instance().getRingBufferPool().getData(ref->playbackBuff_, RingBufferPool::DEFAULT_ID); ref->playbackBuff_.applyGain(ref->isPlaybackMuted_ ? 0.0 : ref->playbackGain_); if (resample) { AudioBuffer resampledOutput(readableSamples, ref->audioFormat_); ref->resampler_->resample(ref->playbackBuff_, resampledOutput); resampledOutput.interleave(out); } else { ref->playbackBuff_.interleave(out); } } if (normalFramesToGet <= 0) { auto tone = Manager::instance().getTelephoneTone(); auto file_tone = Manager::instance().getTelephoneFile(); ref->playbackBuff_.setFormat(ref->audioFormat_); ref->playbackBuff_.resize(framesPerBuffer); if (tone) { tone->getNext(ref->playbackBuff_, ref->playbackGain_); } else if (file_tone) { file_tone->getNext(ref->playbackBuff_, ref->playbackGain_); } else { //RING_WARN("No tone or file_tone!"); ref->playbackBuff_.reset(); } ref->playbackBuff_.interleave(out); } return paContinue; }