7#include "private/qplatformaudiooutput_p.h"
8#include <QtCore/qloggingcategory.h>
19using namespace std::chrono_literals;
22constexpr auto AudioSinkBufferTime = 100000u
s;
23constexpr auto MinDesiredBufferTime = AudioSinkBufferTime / 10;
24constexpr auto MaxDesiredBufferTime = 6 * AudioSinkBufferTime / 10;
25constexpr auto SampleCompenationOffset = AudioSinkBufferTime / 10;
28constexpr qreal CompensationAngleFactor = 0.01;
30constexpr auto DurationBias = 2ms;
57 m_sink->setVolume(m_output->
isMuted() ? 0.f : m_output->
volume());
62 m_deviceChanged =
true;
70 if (!m_sink || !m_resampler || !m_ioDevice)
73 if (!m_bufferedData.
isValid()) {
78 m_bufferedData = m_resampler->resample(
frame.avFrame());
84 m_bufferedData.
byteCount() - m_bufferWritten);
88 if (m_bufferWritten >= m_bufferedData.
byteCount()) {
95 const auto remainingDuration = std::chrono::microseconds(
98 return {
false, std::min(remainingDuration + DurationBias, AudioSinkBufferTime / 2) };
128 constexpr qreal PlaybackRateDeviation = 1.;
130 auto resamplerFormat = m_format;
133 m_resampler = std::make_unique<Resampler>(
codec, resamplerFormat);
138 qCDebug(qLcAudioRenderer) <<
"Free audio output";
146 m_ioDevice =
nullptr;
150 m_deviceChanged =
false;
155 if (m_deviceChanged) {
173 m_sink = std::make_unique<QAudioSink>(m_output->
device(), m_format);
175 m_sink->setBufferSize(m_format.
bytesForDuration(AudioSinkBufferTime.count()));
176 m_ioDevice = m_sink->start();
202 const auto currentFrameDelay =
frameDelay(currentFrame);
203 auto soundDelay = currentFrameDelay + bufferLoadingTime;
205 const auto activeCompensationDelta = m_resampler->activeSampleCompensationDelta();
207 if (soundDelay > AudioSinkBufferTime) {
208 const auto targetSoundDelay = (AudioSinkBufferTime + MaxDesiredBufferTime) / 2;
210 qCDebug(qLcAudioRenderer) <<
"Change rendering time: Audio time offset."
211 <<
"Prev sound delay:" << soundDelay.count()
212 <<
"Target sound delay:" << targetSoundDelay.count()
213 <<
"New actual sound delay:"
216 soundDelay = targetSoundDelay;
219 constexpr auto AvgDesiredBufferTime = (MinDesiredBufferTime + MaxDesiredBufferTime) / 2;
221 std::optional<int> newCompensationSign;
222 if (soundDelay < MinDesiredBufferTime && activeCompensationDelta <= 0)
223 newCompensationSign = 1;
224 else if (soundDelay > MaxDesiredBufferTime && activeCompensationDelta >= 0)
225 newCompensationSign = -1;
226 else if ((soundDelay <= AvgDesiredBufferTime && activeCompensationDelta < 0)
227 || (soundDelay >= AvgDesiredBufferTime && activeCompensationDelta > 0))
228 newCompensationSign = 0;
232 if (newCompensationSign) {
233 const auto target = *newCompensationSign == 0 ? soundDelay
234 : *newCompensationSign > 0 ? MinDesiredBufferTime + SampleCompenationOffset
235 : MaxDesiredBufferTime - SampleCompenationOffset;
237 const auto interval = std::abs(delta) / CompensationAngleFactor;
239 qDebug(qLcAudioRenderer) <<
"Set audio sample compensation. Delta (samples and us):"
241 <<
"PrevDelta:" << activeCompensationDelta
242 <<
"Interval:" << interval
244 <<
"Delay(us):" << soundDelay.count()
245 <<
"SamplesProcessed:" << m_resampler->samplesProcessed();
247 m_resampler->setSampleCompensation(
static_cast<qint32>(delta),
248 static_cast<quint32>(interval));
256 return AudioSinkBufferTime *
qMax(m_sink->bufferSize() - m_sink->bytesFree(), 0)
257 / m_sink->bufferSize();
264#include "moc_qffmpegaudiorenderer_p.cpp"
bool isValid() const noexcept
Returns true if this is a valid buffer.
const T * constData() const
qsizetype byteCount() const noexcept
Returns the size of this buffer, in bytes.
QAudioFormat::ChannelConfig channelConfiguration() const
Returns the channel configuration of the device.
\qmltype AudioOutput \instantiates QAudioOutput
void mutedChanged(bool muted)
QAudioDevice device
\qmlproperty AudioDevice QtMultimedia::AudioOutput::device
float volume
\qmlproperty real QtMultimedia::AudioOutput::volume
void volumeChanged(float volume)
void updateSynchronization(const Frame ¤tFrame)
~AudioRenderer() override
RenderingResult renderInternal(Frame frame) override
std::chrono::microseconds currentBufferLoadingTime() const
AudioRenderer(const TimeController &tc, QAudioOutput *output)
void onPlaybackRateChanged() override
void setOutput(QAudioOutput *output)
void updateOutput(const Codec *codec)
void initResempler(const Codec *codec)
float playbackRate() const
std::chrono::microseconds frameDelay(const Frame &frame) const
void setOutputInternal(QPointer< Output > &actual, Output *desired, ChangeHandler &&changeHandler)
void changeRendererTime(std::chrono::microseconds offset)
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Combined button and popup list for selecting options.
int qRound(qfloat16 d) noexcept
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qMax(const T &a, const T &b)
GLenum GLsizei GLuint GLint * bytesWritten
GLenum GLenum GLsizei count
QT_BEGIN_NAMESPACE typedef uchar * output