diff options
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp')
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp | 160 |
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 |