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
3#include "qffmpegresampler_p.h"
4#include "playbackengine/qffmpegcodec_p.h"
5#include "qffmpegmediaformatinfo_p.h"
6#include <qloggingcategory.h>
7
8static Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
9
10QT_BEGIN_NAMESPACE
11
12using namespace QFFmpeg;
13
14QFFmpegResampler::QFFmpegResampler(const QAudioFormat &inputFormat, const QAudioFormat &outputFormat) :
15 m_inputFormat(inputFormat), m_outputFormat(outputFormat)
16{
17 Q_ASSERT(inputFormat.isValid());
18 Q_ASSERT(outputFormat.isValid());
19
20 m_resampler =
21 createResampleContext(inputFormat: AVAudioFormat(m_inputFormat), outputFormat: AVAudioFormat(m_outputFormat));
22}
23
24QFFmpegResampler::QFFmpegResampler(const Codec *codec, const QAudioFormat &outputFormat,
25 qint64 startTime)
26 : m_outputFormat(outputFormat), m_startTime(startTime)
27{
28 Q_ASSERT(codec);
29
30 qCDebug(qLcResampler) << "createResampler";
31 const AVStream *audioStream = codec->stream();
32
33 if (!m_outputFormat.isValid())
34 // want the native format
35 m_outputFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(codecPar: audioStream->codecpar);
36
37 m_resampler = createResampleContext(inputFormat: AVAudioFormat(audioStream->codecpar),
38 outputFormat: AVAudioFormat(m_outputFormat));
39}
40
41QFFmpegResampler::~QFFmpegResampler() = default;
42
43QAudioBuffer QFFmpegResampler::resample(const char* data, size_t size)
44{
45 if (!m_inputFormat.isValid())
46 return {};
47
48 return resample(inputData: reinterpret_cast<const uint8_t **>(&data),
49 inputSamplesCount: m_inputFormat.framesForBytes(byteCount: static_cast<qint32>(size)));
50}
51
52QAudioBuffer QFFmpegResampler::resample(const AVFrame *frame)
53{
54 return resample(inputData: const_cast<const uint8_t **>(frame->extended_data), inputSamplesCount: frame->nb_samples);
55}
56
57QAudioBuffer QFFmpegResampler::resample(const uint8_t **inputData, int inputSamplesCount)
58{
59 const int maxOutSamples = adjustMaxOutSamples(inputSamplesCount);
60
61 QByteArray samples(m_outputFormat.bytesForFrames(frameCount: maxOutSamples), Qt::Uninitialized);
62 auto *out = reinterpret_cast<uint8_t *>(samples.data());
63 const int outSamples =
64 swr_convert(s: m_resampler.get(), out: &out, out_count: maxOutSamples, in: inputData, in_count: inputSamplesCount);
65
66 samples.resize(size: m_outputFormat.bytesForFrames(frameCount: outSamples));
67
68 const qint64 startTime = m_outputFormat.durationForFrames(frameCount: m_samplesProcessed) + m_startTime;
69 m_samplesProcessed += outSamples;
70
71 qCDebug(qLcResampler) << " new frame" << startTime << "in_samples" << inputSamplesCount
72 << outSamples << maxOutSamples;
73 return QAudioBuffer(samples, m_outputFormat, startTime);
74}
75
76int QFFmpegResampler::adjustMaxOutSamples(int inputSamplesCount)
77{
78 int maxOutSamples = swr_get_out_samples(s: m_resampler.get(), in_samples: inputSamplesCount);
79
80 const auto remainingCompensationDistance = m_endCompensationSample - m_samplesProcessed;
81
82 if (remainingCompensationDistance > 0 && maxOutSamples > remainingCompensationDistance) {
83 // If the remaining compensation distance less than output frame,
84 // the ffmpeg resampler bufferises the rest of frames that makes
85 // unexpected delays on large frames.
86 // The hack might cause some compensation bias on large frames,
87 // however it's not significant for our logic, in fact.
88 // TODO: probably, it will need some improvements
89 setSampleCompensation(delta: 0, distance: 0);
90 maxOutSamples = swr_get_out_samples(s: m_resampler.get(), in_samples: inputSamplesCount);
91 }
92
93 return maxOutSamples;
94}
95
96void QFFmpegResampler::setSampleCompensation(qint32 delta, quint32 distance)
97{
98 const int res = swr_set_compensation(s: m_resampler.get(), sample_delta: delta, compensation_distance: static_cast<int>(distance));
99 if (res < 0)
100 qCWarning(qLcResampler) << "swr_set_compensation fail:" << res;
101 else {
102 m_sampleCompensationDelta = delta;
103 m_endCompensationSample = m_samplesProcessed + distance;
104 }
105}
106
107qint32 QFFmpegResampler::activeSampleCompensationDelta() const
108{
109 return m_samplesProcessed < m_endCompensationSample ? m_sampleCompensationDelta : 0;
110}
111
112QT_END_NAMESPACE
113

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp