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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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