7#include "private/qplatformaudiooutput_p.h"
8#include <QtCore/qloggingcategory.h>
19using namespace std::chrono_literals;
20using namespace std::chrono;
23constexpr auto DesiredBufferTime = 110000u
s;
24constexpr auto MinDesiredBufferTime = 22000u
s;
25constexpr auto MaxDesiredBufferTime = 64000u
s;
26constexpr auto MinDesiredFreeBufferTime = 10000u
s;
30constexpr auto BufferLoadingMeasureTime = 400ms;
32constexpr auto DurationBias = 2ms;
34qreal sampleRateFactor() {
51 const auto sampleRateFactorStr =
qEnvironmentVariable(
"QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR");
53 const auto result = sampleRateFactorStr.toDouble(&
ok);
85 m_sink->setVolume(m_output->
isMuted() ? 0.f : m_output->
volume());
90 m_deviceChanged =
true;
98 if (!m_ioDevice || !m_resampler)
103 auto firstFrameFlagGuard =
qScopeGuard([&]() { m_firstFrame =
false; });
106 m_bufferedData.
offset, Clock::now() };
108 if (!m_bufferedData.
isValid()) {
109 if (!
frame.isValid()) {
110 if (std::exchange(m_drained,
true))
115 qCDebug(qLcAudioRenderer) <<
"Draining AudioRenderer, time:" <<
time;
120 m_bufferedData = { m_resampler->resample(
frame.avFrame()) };
123 if (m_bufferedData.
isValid()) {
131 if (m_bufferedData.
size() <= 0) {
153 constexpr auto MaxFixableInterval = 50;
158 || interval > MaxFixableInterval)
186 auto resamplerFormat = m_format;
189 m_resampler = std::make_unique<QFFmpegResampler>(
codec, resamplerFormat);
194 qCDebug(qLcAudioRenderer) <<
"Free audio output";
202 m_ioDevice =
nullptr;
205 m_deviceChanged =
false;
207 m_bufferLoadingInfo = {};
212 if (m_deviceChanged) {
229 m_sink = std::make_unique<QAudioSink>(m_output->
device(), m_format);
231 m_sink->setBufferSize(m_format.
bytesForDuration(DesiredBufferTime.count()));
232 m_ioDevice = m_sink->start();
254 if (!
frame.isValid())
264 auto synchronize = [&](microseconds fixedDelay, microseconds targetSoundDelay) {
268 if (qLcAudioRenderer().isDebugEnabled()) {
271 <<
"Change rendering time:"
272 <<
"\n First frame:" << m_firstFrame
273 <<
"\n Delay (frame+buffer-written):" << currentFrameDelay <<
"+"
275 << writtenTime <<
"="
277 <<
"\n Fixed delay:" << fixedDelay
278 <<
"\n Target delay:" << targetSoundDelay
279 <<
"\n Buffer durations (min/max/limit):" << m_timings.
minSoundDelay
282 <<
"\n Audio sink state:" << stamp.audioSinkState;
291 if (loadingType != m_bufferLoadingInfo.
type) {
295 m_bufferLoadingInfo = { loadingType, stamp.
timePoint, soundDelay };
300 const auto shouldHandleIdle = stamp.audioSinkState ==
QAudio::IdleState && !isHigh;
302 auto &fixedDelay = m_bufferLoadingInfo.
delay;
304 fixedDelay = shouldHandleIdle ? soundDelay
305 : isHigh ?
qMin(soundDelay, fixedDelay)
306 :
qMax(soundDelay, fixedDelay);
308 if (stamp.timePoint - m_bufferLoadingInfo.
timePoint > BufferLoadingMeasureTime
309 || (m_firstFrame && isHigh) || shouldHandleIdle) {
310 const auto targetDelay = isHigh
314 synchronize(fixedDelay, targetDelay);
325 return microseconds(0);
327 const auto bytes =
qMax(m_sink->bufferSize() - syncStamp.audioSinkBytesFree, 0);
355#include "moc_qffmpegaudiorenderer_p.cpp"
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 stateChanged(QAudio::State state)
This signal is emitted when the device state has changed.
Microseconds durationForBytes(qsizetype bytes) const
~AudioRenderer() override
RenderingResult renderInternal(Frame frame) override
int timerInterval() const override
AudioRenderer(const TimeController &tc, QAudioOutput *output)
void updateSynchronization(const SynchronizationStamp &stamp, const Frame &frame)
void onAudioSinkStateChanged(QAudio::State state)
void onPlaybackRateChanged() override
void setOutput(QAudioOutput *output)
Microseconds bufferLoadingTime(const SynchronizationStamp &syncStamp) const
void updateOutput(const Codec *codec)
void onPauseChanged() override
void initResempler(const Codec *codec)
void scheduleNextStep(bool allowDoImmediatelly=true)
int timerInterval() const override
void onPauseChanged() override
float playbackRate() const
std::chrono::microseconds frameDelay(const Frame &frame, TimePoint timePoint=Clock::now()) 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 & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
GLenum GLsizei GLuint GLint * bytesWritten
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
QT_BEGIN_NAMESPACE typedef uchar * output
Microseconds minSoundDelay
Microseconds maxSoundDelay
Microseconds actualBufferDuration
const char * data() const