Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/media/AudioManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,3 +528,37 @@ void AudioManager::setPlaybackAudioVolume(qreal volume)
}
}
}

void AudioManager::acquireDevice()
{
if (++m_acquireRefCount > 1) {
return;
}

if (m_playbackAudioPort) {
m_playbackAudioPort->acquire();
}

if (m_captureAudioPort) {
m_captureAudioPort->acquire();
}
}

void AudioManager::releaseDevice()
{
if (m_acquireRefCount == 0) {
return;
}

if (--m_acquireRefCount > 0) {
return;
}

if (m_playbackAudioPort) {
m_playbackAudioPort->release();
}

if (m_captureAudioPort) {
m_captureAudioPort->release();
}
}
5 changes: 5 additions & 0 deletions src/media/AudioManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ class AudioManager : public QObject
void *userdata);
#endif

void acquireDevice();
void releaseDevice();

pj::AudioMedia &getPlaybackDevMedia() const;
pj::AudioMedia &getCaptureDevMedia() const;

Expand Down Expand Up @@ -139,6 +142,8 @@ private Q_SLOTS:
unsigned m_captureDeviceId = 0;
unsigned m_currentAudioProfile = 0;

int m_acquireRefCount = 0;

QTimer m_updateDebouncer;
QList<SIPAudioDevice *> m_devices;

Expand Down
78 changes: 69 additions & 9 deletions src/media/AudioPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ using namespace std::chrono_literals;

AudioPort::AudioPort(QAudioDevice device) : m_device(device)
{
m_idleTimer.setInterval(1s);
m_idleTimer.setInterval(10s);
connect(&m_idleTimer, &QTimer::timeout, this, &AudioPort::stopIO);

connect(this, &AudioPort::startIdleTimer, this,
Expand Down Expand Up @@ -168,6 +168,11 @@ void AudioPort::startSinkIO()
{
m_idleTimer.stop();

if (m_isDraining && !m_sink.isNull()) {
m_isDraining = false;
return;
}

if (!m_sink.isNull()) {
stopSinkIO();
}
Expand All @@ -191,17 +196,25 @@ void AudioPort::startSinkIO()
void AudioPort::stopSinkIO()
{
m_idleTimer.stop();
if (m_sink.isNull() || m_isDraining) {
return;
}

if (m_sink) {
writeSilenceMS(SILENCE_BUFFER_MS);
writeSilenceMS(SILENCE_BUFFER_MS);

m_sink->stop();
m_sink->deleteLater();
m_sink = nullptr;
m_io = nullptr;
}
m_isDraining = true;
QTimer::singleShot(SILENCE_BUFFER_MS, this, [this]() {
m_isDraining = false;

Q_EMIT audioSinkChanged();
if (m_sink) {
m_sink->stop();
m_sink->deleteLater();
m_sink = nullptr;
m_io = nullptr;
}

Q_EMIT audioSinkChanged();
});
}

void AudioPort::startSourceIO()
Expand Down Expand Up @@ -267,6 +280,11 @@ void AudioPort::onFrameRequested(pj::MediaFrame &frame)
return;
}

if (m_isWarmingUp) {
m_isWarmingUp = false;
QObject::disconnect(m_warmUpDrain);
}

auto bytes = m_io->read(frame.size);

if (!m_isMuted) {
Expand Down Expand Up @@ -297,8 +315,50 @@ void AudioPort::onFrameReceived(pj::MediaFrame &frame)
return;
}

m_isWarmingUp = false;

m_io->write(reinterpret_cast<char *>(frame.buf.data()), frame.size);

// Auto destroy sink after timeout
Q_EMIT startIdleTimer();
}

void AudioPort::acquire()
{
if (m_device.mode() == QAudioDevice::Mode::Input) {
if (m_source.isNull()) {
startSourceIO();
}
} else {
if (m_sink.isNull()) {
startSinkIO();
}
}

m_isWarmingUp = true;

if (m_device.mode() == QAudioDevice::Mode::Input && !m_io.isNull()) {
QObject::disconnect(m_warmUpDrain);

m_warmUpDrain = connect(m_io.data(), &QIODevice::readyRead, this, [this]() {
if (m_isWarmingUp && !m_io.isNull()) {
m_io->readAll();
}
});
}
}

void AudioPort::release()
{
if (!m_isWarmingUp) {
return;
}

m_isWarmingUp = false;

QObject::disconnect(m_warmUpDrain);

if (!m_idleTimer.isActive()) {
stopIO();
}
}
12 changes: 10 additions & 2 deletions src/media/AudioPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class AudioPort : public QObject, public pj::AudioMediaPort
QString getDeviceID() const;
QString getSystemDeviceID() const;

void acquire();
void release();

void setAudioDevice(QAudioDevice device);
QAudioDevice audioDevice() { return m_device; }

Expand All @@ -32,15 +35,15 @@ class AudioPort : public QObject, public pj::AudioMediaPort

qreal sourceLevel() const { return m_sourceAudioLevel; }

void writeSilenceMS(unsigned milliseconds);

Q_SIGNALS:
void startIdleTimer();
void audioSourceChanged();
void audioSinkChanged();
void sourceLevelChanged(qreal level);

private:
void writeSilenceMS(unsigned milliseconds);

void startIO();
void stopIO();

Expand All @@ -55,6 +58,9 @@ class AudioPort : public QObject, public pj::AudioMediaPort
void setSourceAudioLevel(qreal level);

bool m_isMuted = false;
bool m_isDraining = false;
bool m_isWarmingUp = false;

QAudioDevice m_device;

QPointer<QIODevice> m_io;
Expand All @@ -67,4 +73,6 @@ class AudioPort : public QObject, public pj::AudioMediaPort
pj::MediaFormatAudio m_pj_fmt;

QAudioFormat m_audioFormat;

QMetaObject::Connection m_warmUpDrain;
};
28 changes: 22 additions & 6 deletions src/sip/RingTone.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "RingTone.h"
#include "AudioManager.h"
#include "ReadOnlyConfdSettings.h"
#include "AudioPort.h"

RingTone::RingTone(quint16 frequency1, quint16 frequency2, QList<QPair<quint16, quint16>> intervals,
qint8 loopIndex, QObject *parent)
Expand Down Expand Up @@ -40,10 +40,17 @@ void RingTone::start()
m_isPlaying = true;
m_currentIndex = 0;

AudioManager::instance().acquireDevice();

if (m_stopTimer.isActive()) {
m_stopTimer.stop();
}

// Bridge gap between previous audio source (mostly the call)
if (auto *port = dynamic_cast<AudioPort *>(&m_mediaSink)) {
port->writeSilenceMS(120);
}

m_toneGen.startTransmit(m_mediaSink);
playNextTone();
}
Expand All @@ -55,20 +62,23 @@ void RingTone::stop()
if (!m_isPlaying) {
return;
}

if (m_loopTimer.isActive()) {
m_loopTimer.stop();
}

m_currentIndex = 0;
m_isPlaying = false;
m_toneGen.stop();
m_toneGen.stopTransmit(m_mediaSink);

AudioManager::instance().releaseDevice();

Q_EMIT ready();
}

void RingTone::playNextTone()
{

// Create and play tone
const auto &tuple = m_intervals.at(m_currentIndex);

Expand All @@ -92,16 +102,22 @@ void RingTone::playNextTone()
if (m_repeatTimes > 0) {
--m_repeatTimes;
} else if (m_repeatTimes == 0) {
stop();
scheduleStop(tuple.first + tuple.second);
return;
}
} else {
// No loop - stop the tone
m_stopTimer.setInterval(tuple.first + tuple.second);
m_stopTimer.start();
scheduleStop(tuple.first + tuple.second);
return;
}
}

m_loopTimer.start(tuple.first + tuple.second);
}

void RingTone::scheduleStop(unsigned delay)
{
static constexpr unsigned PIPELINE_GRACE_MS = 400;

m_stopTimer.setInterval(delay + PIPELINE_GRACE_MS);
m_stopTimer.start();
}
2 changes: 2 additions & 0 deletions src/sip/RingTone.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ private Q_SLOTS:
void playNextTone();

private:
void scheduleStop(unsigned delay);

bool m_isPlaying = false;
pj::ToneGenerator m_toneGen;
pj::AudioMedia &m_mediaSink;
Expand Down
2 changes: 1 addition & 1 deletion src/sip/SIPAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ void SIPAccount::removeCall(SIPCall *call)
{
if (call) {
m_calls.removeAll(call);
delete call;
call->deleteLater();
}
}

Expand Down
12 changes: 10 additions & 2 deletions src/sip/SIPCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "SIPCallManager.h"
#include "SIPAccount.h"
#include "AudioManager.h"
#include "AudioPort.h"
#include "ResponseLoader.h"
#include "RingToneFactory.h"
#include "RingTone.h"
Expand Down Expand Up @@ -79,6 +80,8 @@ SIPCall::SIPCall(SIPAccount *account, int callId, const QString &contactId, bool
}
}

AudioManager::instance().acquireDevice();

// Setup rtt timeout
m_rttTimeoutTimer.setSingleShot(true);
m_rttTimeoutTimer.setInterval(6s);
Expand All @@ -96,6 +99,8 @@ SIPCall::SIPCall(SIPAccount *account, int callId, const QString &contactId, bool

SIPCall::~SIPCall()
{
AudioManager::instance().releaseDevice();

if (m_isEmergencyCall) {
Q_EMIT ViewHelper::instance().hideEmergency();
}
Expand Down Expand Up @@ -286,8 +291,7 @@ void SIPCall::onCallState(pj::OnCallStateParam &prm)

if (m_isEstablished) {
ringToneFactory.endTone()->start();
}
if (!m_incoming) {
} else if (!m_incoming) {
if (statusCode == PJSIP_SC_BUSY_HERE) {
ringToneFactory.busyTone()->start(5);
} else if (static_cast<int>(statusCode) >= 400
Expand Down Expand Up @@ -370,6 +374,10 @@ void SIPCall::onCallMediaState(pj::OnCallMediaStateParam &prm)
tr("Failed to initialize microphone audio"));
}

if (auto *port = dynamic_cast<AudioPort *>(&speaker_media)) {
port->writeSilenceMS(120);
}

try {
aud_med.startTransmit(speaker_media);
} catch (pj::Error &err) {
Expand Down
Loading
Loading