Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qffmpegresampler.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4#include "playbackengine/qffmpegcodeccontext_p.h"
6#include <qloggingcategory.h>
7
8Q_STATIC_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
9Q_STATIC_LOGGING_CATEGORY(qLcResamplerTrace, "qt.multimedia.ffmpeg.resampler.trace")
10
11QT_BEGIN_NAMESPACE
12
13using namespace QFFmpeg;
14
15QFFmpegResampler::QFFmpegResampler(const QAudioFormat &inputFormat,
16 const QAudioFormat &outputFormat, qint64 startTime)
17 : m_inputFormat(inputFormat), m_outputFormat(outputFormat), m_startTime(startTime)
18{
19 Q_ASSERT(inputFormat.isValid());
20 Q_ASSERT(outputFormat.isValid());
21
22 const auto inputAvFormat = AVAudioFormat(m_inputFormat);
23 const auto outputAvFormat = AVAudioFormat(m_outputFormat);
24
25 m_resampler = createResampleContext(inputAvFormat, outputAvFormat);
26
27 qCDebug(qLcResampler) << "Created QFFmpegResampler with offset" << m_startTime
28 << "us. Converting from" << inputAvFormat << "to" << outputAvFormat;
29}
30
31QFFmpegResampler::QFFmpegResampler(const CodecContext *codecContext,
32 const QAudioFormat &outputFormat,
33 qint64 startTime)
34 : m_outputFormat(outputFormat), m_startTime(startTime)
35{
36 Q_ASSERT(codecContext);
37
38 const AVStream *audioStream = codecContext->stream();
39
40 if (!m_outputFormat.isValid())
41 // want the native format
42 m_outputFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(*audioStream->codecpar);
43
44 const auto inputAvFormat = AVAudioFormat(codecContext->context());
45 const auto outputAvFormat = AVAudioFormat(m_outputFormat);
46
47 m_resampler = createResampleContext(inputAvFormat, outputAvFormat);
48
49 qCDebug(qLcResampler).nospace() << "Created QFFmpegResampler. Offset: " << m_startTime
50 << "us. From: " << inputAvFormat << " to: " << outputAvFormat;
51}
52
53template <typename... Args>
54std::unique_ptr<QFFmpegResampler> QFFmpegResampler::createImpl(Args... args)
55{
56 std::unique_ptr<QFFmpegResampler> resampler{
57 new QFFmpegResampler(std::forward<Args>(args)...),
58 };
59 if (resampler->isInitialized())
60 return resampler;
61 return nullptr;
62}
63
64std::unique_ptr<QFFmpegResampler>
65QFFmpegResampler::createFromInputFormat(const QAudioFormat &inputFormat,
66 const QAudioFormat &outputFormat, qint64 startTime)
67{
68 return createImpl(inputFormat, outputFormat, startTime);
69}
70
71std::unique_ptr<QFFmpegResampler>
72QFFmpegResampler::createFromCodecContext(const CodecContext *codecContext,
73 const QAudioFormat &outputFormat, qint64 startTime)
74{
75 return createImpl(codecContext, outputFormat, startTime);
76}
77
79{
80 return m_resampler != nullptr;
81}
82
84
85QAudioBuffer QFFmpegResampler::resample(const char* data, size_t size)
86{
87 if (!m_inputFormat.isValid())
88 return {};
89
90 return resample(reinterpret_cast<const uint8_t **>(&data),
91 m_inputFormat.framesForBytes(static_cast<qint32>(size)));
92}
93
95{
96 return resample(const_cast<const uint8_t **>(frame->extended_data), frame->nb_samples);
97}
98
99QAudioBuffer QFFmpegResampler::resample(const uint8_t **inputData, int inputSamplesCount)
100{
101 const int maxOutSamples = adjustMaxOutSamples(inputSamplesCount);
102
103 QByteArray samples(m_outputFormat.bytesForFrames(maxOutSamples), Qt::Uninitialized);
104 auto *out = reinterpret_cast<uint8_t *>(samples.data());
105 const int outSamples =
106 swr_convert(m_resampler.get(), &out, maxOutSamples, inputData, inputSamplesCount);
107
108 samples.resize(m_outputFormat.bytesForFrames(outSamples));
109
110 const qint64 startTime = m_outputFormat.durationForFrames(m_samplesProcessed) + m_startTime;
111 m_samplesProcessed += outSamples;
112
113 qCDebug(qLcResamplerTrace).nospace()
114 << "Created output buffer. Time stamp: " << startTime
115 << "us. Samples in: " << inputSamplesCount
116 << ", Samples out: " << outSamples << ", Max samples: " << maxOutSamples;
117 return QAudioBuffer(samples, m_outputFormat, startTime);
118}
119
120int QFFmpegResampler::adjustMaxOutSamples(int inputSamplesCount)
121{
122 int maxOutSamples = swr_get_out_samples(m_resampler.get(), inputSamplesCount);
123
124 const auto remainingCompensationDistance = m_endCompensationSample - m_samplesProcessed;
125
126 if (remainingCompensationDistance > 0 && maxOutSamples > remainingCompensationDistance) {
127 // If the remaining compensation distance less than output frame,
128 // the ffmpeg resampler bufferises the rest of frames that makes
129 // unexpected delays on large frames.
130 // The hack might cause some compensation bias on large frames,
131 // however it's not significant for our logic, in fact.
132 // TODO: probably, it will need some improvements
133 setSampleCompensation(0, 0);
134 maxOutSamples = swr_get_out_samples(m_resampler.get(), inputSamplesCount);
135 }
136
137 return maxOutSamples;
138}
139
140void QFFmpegResampler::setSampleCompensation(qint32 delta, quint32 distance)
141{
142 const int res = swr_set_compensation(m_resampler.get(), delta, static_cast<int>(distance));
143 if (res < 0)
144 qCWarning(qLcResampler) << "swr_set_compensation fail:" << res;
145 else {
146 m_sampleCompensationDelta = delta;
147 m_endCompensationSample = m_samplesProcessed + distance;
148 }
149}
150
152{
153 return m_samplesProcessed < m_endCompensationSample ? m_sampleCompensationDelta : 0;
154}
155
156QT_END_NAMESPACE
The QAudioFormat class stores audio stream parameter information.
constexpr bool isValid() const noexcept
Returns true if all of the parameters are valid.
QAudioBuffer resample(const char *data, size_t size) override
~QFFmpegResampler() override
void setSampleCompensation(qint32 delta, quint32 distance)
QAudioBuffer resample(const AVFrame *frame)
bool isInitialized() const
qint32 activeSampleCompensationDelta() const
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)