コード例 #1
0
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;
    }
}
コード例 #2
0
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;
}
コード例 #3
0
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
}
コード例 #4
0
ファイル: jacklayer.cpp プロジェクト: alexzah/ring-daemon
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));
    }
}
コード例 #5
0
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;
}
コード例 #6
0
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);
                    }
                }
            });
    }
}
コード例 #7
0
ファイル: jacklayer.cpp プロジェクト: alexzah/ring-daemon
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;
}
コード例 #8
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;
}
コード例 #9
0
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;
}
コード例 #10
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();
}
コード例 #11
0
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);
}
コード例 #12
0
/**
 * 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());
}
コード例 #13
0
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());
    }
}
コード例 #14
0
ファイル: sinkclient.cpp プロジェクト: asadsalman/ring-daemon
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;
    }
}
コード例 #15
0
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;
}
コード例 #16
0
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
}
コード例 #17
0
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;
}
コード例 #18
0
ファイル: jacklayer.cpp プロジェクト: alexzah/ring-daemon
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);
    }
}
コード例 #19
0
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_);
}
コード例 #20
0
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;
}
コード例 #21
0
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_);
}
コード例 #22
0
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);
}
コード例 #23
0
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;
    }
}
コード例 #24
0
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
}
コード例 #25
0
ファイル: ip_utils.cpp プロジェクト: alexzah/ring-daemon
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;
}
コード例 #26
0
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;
}
コード例 #27
0
void
SIPCall::updateSDPFromSTUN()
{
    RING_WARN("[call:%s] SIPCall::updateSDPFromSTUN() not implemented", getCallId().c_str());
}
コード例 #28
0
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;
        }
    }
コード例 #29
0
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(); });
}
コード例 #30
0
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;
}