8#include "private/qplatformaudiooutput_p.h"
9#include <QtCore/qloggingcategory.h>
20using namespace std::chrono_literals;
21using namespace std::chrono;
24constexpr auto DesiredBufferTime = 110000u
s;
25constexpr auto MinDesiredBufferTime = 22000u
s;
26constexpr auto MaxDesiredBufferTime = 64000u
s;
27constexpr auto MinDesiredFreeBufferTime = 10000u
s;
31constexpr auto BufferLoadingMeasureTime = 400ms;
33constexpr auto DurationBias = 2ms;
35qreal sampleRateFactor() {
52 const auto sampleRateFactorStr =
qEnvironmentVariable(
"QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR");
54 const auto result = sampleRateFactorStr.toDouble(&
ok);
64 frame.codec()->stream()->codecpar);
67std::unique_ptr<QFFmpegResampler> createResampler(
const Frame &
frame,
70 return std::make_unique<QFFmpegResampler>(
frame.codec(), outputFormat,
frame.pts());
106 m_sink->setVolume(m_output->
isMuted() ? 0.f : m_output->
volume());
111 m_deviceChanged =
true;
123 if (m_lastFramePushDone)
127 m_lastFramePushDone =
result.done;
134 if (!m_ioDevice || !m_resampler)
139 auto firstFrameFlagGuard =
qScopeGuard([&]() { m_firstFrameToSink =
false; });
142 m_bufferedData.
offset, Clock::now() };
144 if (!m_bufferedData.
isValid()) {
145 if (!
frame.isValid()) {
146 if (std::exchange(m_drained,
true))
151 qCDebug(qLcAudioRenderer) <<
"Draining AudioRenderer, time:" <<
time;
156 m_bufferedData = { m_resampler->resample(
frame.avFrame()) };
159 if (m_bufferedData.
isValid()) {
167 if (m_bufferedData.
size() <= 0) {
189 if (
frame.isValid()) {
205 constexpr auto MaxFixableInterval = 50;
210 || interval > MaxFixableInterval)
218 m_firstFrameToSink =
true;
226 auto resamplerFormat = m_sinkFormat;
229 m_resampler = createResampler(
frame, resamplerFormat);
234 qCDebug(qLcAudioRenderer) <<
"Free audio output";
242 m_ioDevice =
nullptr;
245 m_deviceChanged =
false;
248 m_bufferLoadingInfo = {};
253 if (m_deviceChanged) {
258 if (m_bufferOutput) {
259 if (m_bufferOutputChanged) {
260 m_bufferOutputChanged =
false;
261 m_bufferOutputResampler.reset();
264 if (!m_bufferOutputResampler) {
267 outputFormat = audioFormatFromFrame(
frame);
268 m_bufferOutputResampler = createResampler(
frame, outputFormat);
276 m_sinkFormat = audioFormatFromFrame(
frame);
282 m_sink = std::make_unique<QAudioSink>(m_output->
device(), m_sinkFormat);
284 m_sink->setBufferSize(m_sinkFormat.
bytesForDuration(DesiredBufferTime.count()));
285 m_ioDevice = m_sink->start();
286 m_firstFrameToSink =
true;
306 if (!
frame.isValid())
316 auto synchronize = [&](microseconds fixedDelay, microseconds targetSoundDelay) {
320 if (qLcAudioRenderer().isDebugEnabled()) {
323 <<
"Change rendering time:"
324 <<
"\n First frame:" << m_firstFrameToSink
325 <<
"\n Delay (frame+buffer-written):" << currentFrameDelay <<
"+"
327 << writtenTime <<
"="
329 <<
"\n Fixed delay:" << fixedDelay
330 <<
"\n Target delay:" << targetSoundDelay
331 <<
"\n Buffer durations (min/max/limit):" << m_timings.
minSoundDelay
334 <<
"\n Audio sink state:" << stamp.audioSinkState;
343 if (loadingType != m_bufferLoadingInfo.
type) {
347 m_bufferLoadingInfo = { loadingType, stamp.
timePoint, soundDelay };
352 const auto shouldHandleIdle = stamp.audioSinkState ==
QAudio::IdleState && !isHigh;
354 auto &fixedDelay = m_bufferLoadingInfo.
delay;
356 fixedDelay = shouldHandleIdle ? soundDelay
357 : isHigh ?
qMin(soundDelay, fixedDelay)
358 :
qMax(soundDelay, fixedDelay);
360 if (stamp.timePoint - m_bufferLoadingInfo.
timePoint > BufferLoadingMeasureTime
361 || (m_firstFrameToSink && isHigh) || shouldHandleIdle) {
362 const auto targetDelay = isHigh
366 synchronize(fixedDelay, targetDelay);
377 return microseconds(0);
379 const auto bytes =
qMax(m_sink->bufferSize() - syncStamp.audioSinkBytesFree, 0);
407#include "moc_qffmpegaudiorenderer_p.cpp"
QAudioFormat format() const
Gets the audio format specified in the constructor.
void audioBufferReceived(const QAudioBuffer &buffer)
Signals that a new audio buffer has been received from \l QMediaPlayer.
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.
AudioRenderer(const TimeController &tc, QAudioOutput *output, QAudioBufferOutput *bufferOutput)
RenderingResult pushFrameToOutput(const Frame &frame)
Microseconds durationForBytes(qsizetype bytes) const
~AudioRenderer() override
RenderingResult renderInternal(Frame frame) override
void updateOutputs(const Frame &frame)
int timerInterval() const override
void initResempler(const Frame &frame)
void updateSynchronization(const SynchronizationStamp &stamp, const Frame &frame)
void onAudioSinkStateChanged(QAudio::State state)
void onPlaybackRateChanged() override
void setOutput(QAudioOutput *output)
void pushFrameToBufferOutput(const Frame &frame)
Microseconds bufferLoadingTime(const SynchronizationStamp &syncStamp) const
void onPauseChanged() override
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 qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
QT_BEGIN_NAMESPACE 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