Revision control
++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ #ifndef QFFMPEGAUDIORENDERER_P_H
++ #define QFFMPEGAUDIORENDERER_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ #include <QtFFmpegMediaPluginImpl/private/qffmpegrenderer_p.h>
++
++ #include <QtMultimedia/qaudiobuffer.h>
++
++ QT_BEGIN_NAMESPACE
++
++ class QAudioOutput;
++ class QAudioBufferOutput;
++ class QAudioSink;
++ class QFFmpegResampler;
++
++ namespace QFFmpeg {
++
++ struct AbstractAudioFrameConverter;
++
++ class AudioRenderer : public Renderer
++ {
++ Q_OBJECT
++ public:
++ AudioRenderer(const PlaybackEngineObjectID &id, const TimeController &tc, QAudioOutput *output,
++ QAudioBufferOutput *bufferOutput, bool pitchCompensation);
++
++ void setOutput(QAudioOutput *output);
++
++ void setOutput(QAudioBufferOutput *bufferOutput);
++
++ void setPitchCompensation(bool enabled);
++
++ ~AudioRenderer() override;
++
++ protected:
++ using Microseconds = std::chrono::microseconds;
++ struct SynchronizationStamp
++ {
++ QAudio::State audioSinkState = QAudio::IdleState;
++ qsizetype audioSinkBytesFree = 0;
++ qsizetype bufferBytesWritten = 0;
++ TimePoint timePoint = TimePoint::max();
++ };
++
++ struct BufferLoadingInfo
++ {
++ enum Type { Low, Moderate, High };
++ Type type = Moderate;
++ TimePoint timePoint = TimePoint::max();
++ Microseconds delay = Microseconds(0);
++ };
++
++ struct AudioTimings
++ {
++ Microseconds actualBufferDuration = Microseconds(0);
++ Microseconds maxSoundDelay = Microseconds(0);
++ Microseconds minSoundDelay = Microseconds(0);
++ };
++
++ struct BufferedDataWithOffset
++ {
++ QAudioBuffer buffer;
++ qsizetype offset = 0;
++
++ bool isValid() const { return buffer.isValid(); }
++ qsizetype size() const { return buffer.byteCount() - offset; }
++ const char *data() const { return buffer.constData<char>() + offset; }
++ };
++
++ void seekInternal() override;
++
++ RenderingResult renderInternal(Frame frame) override;
++
++ RenderingResult pushFrameToOutput(const Frame &frame);
++
++ void pushFrameToBufferOutput(const Frame &frame);
++
++ void onPlaybackRateChanged() override;
++
++ TimePoint nextTimePoint() const override;
++
++ void onPauseChanged() override;
++
++ void freeOutput();
++
++ void updateOutputs(const Frame &frame);
++
++ void initAudioFrameConverter(const Frame &frame);
++
++ void onDeviceChanged();
++
++ void updateVolume();
++
++ void updateSynchronization(const SynchronizationStamp &stamp, const Frame &frame);
++
++ Microseconds bufferLoadingTime(const SynchronizationStamp &syncStamp) const;
++
++ void onAudioSinkStateChanged(QAudio::State state);
++
++ Microseconds durationForBytes(qsizetype bytes) const;
++
++ private:
++ QPointer<QAudioOutput> m_output;
++ QPointer<QAudioBufferOutput> m_bufferOutput;
++ std::unique_ptr<QAudioSink> m_sink;
++ AudioTimings m_timings;
++ BufferLoadingInfo m_bufferLoadingInfo;
++ std::unique_ptr<QFFmpegResampler> m_bufferOutputResampler;
++ QAudioFormat m_sinkFormat;
++
++ QPointer<QIODevice> m_ioDevice;
++
++ bool m_deviceChanged = false;
++ bool m_bufferOutputChanged = false;
++
++ bool m_firstFrameToSink = true;
++
++ // pitch compensation
++ bool m_pitchCompensation = false;
++ std::unique_ptr<AbstractAudioFrameConverter> m_audioFrameConverter;
++
++ struct SessionContext
++ {
++ // TODO: add other members after clarifying the best session cleanup strategy
++ BufferedDataWithOffset bufferedData;
++ bool lastFramePushDone = true;
++ bool drained = false;
++ };
++
++ SessionContext m_sessionCtx;
++ };
++
++ } // namespace QFFmpeg
++
++ QT_END_NAMESPACE
++
++ #endif // QFFMPEGAUDIORENDERER_P_H