summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp')
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp160
1 files changed, 77 insertions, 83 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
index 911278f63..e5e9ca3bb 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
@@ -1,117 +1,111 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 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
#include "qffmpegresampler_p.h"
-#include "qffmpegdecoder_p.h"
+#include "playbackengine/qffmpegcodec_p.h"
#include "qffmpegmediaformatinfo_p.h"
#include <qloggingcategory.h>
-extern "C" {
-#include <libavutil/opt.h>
-}
-
-Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
+static Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
QT_BEGIN_NAMESPACE
-namespace QFFmpeg
+using namespace QFFmpeg;
+
+QFFmpegResampler::QFFmpegResampler(const QAudioFormat &inputFormat, const QAudioFormat &outputFormat) :
+ m_inputFormat(inputFormat), m_outputFormat(outputFormat)
{
+ Q_ASSERT(inputFormat.isValid());
+ Q_ASSERT(outputFormat.isValid());
+
+ m_resampler =
+ createResampleContext(AVAudioFormat(m_inputFormat), AVAudioFormat(m_outputFormat));
+}
-Resampler::Resampler(const Codec *codec, const QAudioFormat &outputFormat)
+QFFmpegResampler::QFFmpegResampler(const Codec* codec, const QAudioFormat &outputFormat)
: m_outputFormat(outputFormat)
{
+ Q_ASSERT(codec);
+
qCDebug(qLcResampler) << "createResampler";
const AVStream *audioStream = codec->stream();
- const auto *codecpar = audioStream->codecpar;
if (!m_outputFormat.isValid())
// want the native format
m_outputFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(audioStream->codecpar);
- QAudioFormat::ChannelConfig config = m_outputFormat.channelConfig();
- if (config == QAudioFormat::ChannelConfigUnknown)
- config = QAudioFormat::defaultChannelConfigForChannelCount(m_outputFormat.channelCount());
-
- auto inConfig = codecpar->channel_layout;
- if (inConfig == 0)
- inConfig = QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->channels));
-
- qCDebug(qLcResampler) << "init resampler" << m_outputFormat.sampleRate() << config << codecpar->sample_rate;
- resampler = swr_alloc_set_opts(nullptr, // we're allocating a new context
- QFFmpegMediaFormatInfo::avChannelLayout(config), // out_ch_layout
- QFFmpegMediaFormatInfo::avSampleFormat(m_outputFormat.sampleFormat()), // out_sample_fmt
- m_outputFormat.sampleRate(), // out_sample_rate
- inConfig, // in_ch_layout
- AVSampleFormat(codecpar->format), // in_sample_fmt
- codecpar->sample_rate, // in_sample_rate
- 0, // log_offset
- nullptr);
-
- // if we're not the master clock, we might need to handle clock adjustments, initialize for that
- av_opt_set_double(resampler, "async", m_outputFormat.sampleRate()/50, 0);
-
- swr_init(resampler);
+ m_resampler = createResampleContext(AVAudioFormat(audioStream->codecpar),
+ AVAudioFormat(m_outputFormat));
}
-Resampler::~Resampler()
+QFFmpegResampler::~QFFmpegResampler() = default;
+
+QAudioBuffer QFFmpegResampler::resample(const char* data, size_t size)
{
- swr_free(&resampler);
+ if (!m_inputFormat.isValid())
+ return {};
+
+ return resample(reinterpret_cast<const uint8_t **>(&data),
+ m_inputFormat.framesForBytes(static_cast<qint32>(size)));
}
-QAudioBuffer Resampler::resample(const AVFrame *frame)
+QAudioBuffer QFFmpegResampler::resample(const AVFrame *frame)
{
- const int outSamples = swr_get_out_samples(resampler, frame->nb_samples);
- QByteArray samples(m_outputFormat.bytesForFrames(outSamples), Qt::Uninitialized);
- auto **in = const_cast<const uint8_t **>(frame->extended_data);
+ return resample(const_cast<const uint8_t **>(frame->extended_data), frame->nb_samples);
+}
+
+QAudioBuffer QFFmpegResampler::resample(const uint8_t **inputData, int inputSamplesCount)
+{
+ const int maxOutSamples = adjustMaxOutSamples(inputSamplesCount);
+
+ QByteArray samples(m_outputFormat.bytesForFrames(maxOutSamples), Qt::Uninitialized);
auto *out = reinterpret_cast<uint8_t *>(samples.data());
- const int out_samples = swr_convert(resampler, &out, outSamples,
- in, frame->nb_samples);
- samples.resize(m_outputFormat.bytesForFrames(out_samples));
+ const int outSamples =
+ swr_convert(m_resampler.get(), &out, maxOutSamples, inputData, inputSamplesCount);
+
+ samples.resize(m_outputFormat.bytesForFrames(outSamples));
qint64 startTime = m_outputFormat.durationForFrames(m_samplesProcessed);
- m_samplesProcessed += out_samples;
+ m_samplesProcessed += outSamples;
- qCDebug(qLcResampler) << " new frame" << startTime << "in_samples" << frame->nb_samples << out_samples << outSamples;
- QAudioBuffer buffer(samples, m_outputFormat, startTime);
- return buffer;
+ qCDebug(qLcResampler) << " new frame" << startTime << "in_samples" << inputSamplesCount
+ << outSamples << maxOutSamples;
+ return QAudioBuffer(samples, m_outputFormat, startTime);
}
+int QFFmpegResampler::adjustMaxOutSamples(int inputSamplesCount)
+{
+ int maxOutSamples = swr_get_out_samples(m_resampler.get(), inputSamplesCount);
+
+ const auto remainingCompensationDistance = m_endCompensationSample - m_samplesProcessed;
+
+ if (remainingCompensationDistance > 0 && maxOutSamples > remainingCompensationDistance) {
+ // If the remaining compensation distance less than output frame,
+ // the ffmpeg resampler bufferises the rest of frames that makes
+ // unexpected delays on large frames.
+ // The hack might cause some compensation bias on large frames,
+ // however it's not significant for our logic, in fact.
+ // TODO: probably, it will need some improvements
+ setSampleCompensation(0, 0);
+ maxOutSamples = swr_get_out_samples(m_resampler.get(), inputSamplesCount);
+ }
+
+ return maxOutSamples;
+}
+void QFFmpegResampler::setSampleCompensation(qint32 delta, quint32 distance)
+{
+ const int res = swr_set_compensation(m_resampler.get(), delta, static_cast<int>(distance));
+ if (res < 0)
+ qCWarning(qLcResampler) << "swr_set_compensation fail:" << res;
+ else {
+ m_sampleCompensationDelta = delta;
+ m_endCompensationSample = m_samplesProcessed + distance;
+ }
+}
+
+qint32 QFFmpegResampler::activeSampleCompensationDelta() const
+{
+ return m_samplesProcessed < m_endCompensationSample ? m_sampleCompensationDelta : 0;
}
QT_END_NAMESPACE