diff options
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp')
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp | 290 |
1 files changed, 130 insertions, 160 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp index 86508f24d..69820cc79 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp @@ -1,54 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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$ -** -****************************************************************************/ -//#define DEBUG_DECODER - +// Copyright (C) 2020 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 "qffmpegaudiodecoder_p.h" -#include "qffmpegdecoder_p.h" -#include "qffmpegmediaformatinfo_p.h" #include "qffmpegresampler_p.h" #include "qaudiobuffer.h" -#include <qloggingcategory.h> +#include "qffmpegplaybackengine_p.h" +#include "playbackengine/qffmpegrenderer_p.h" -Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder") +#include <qloggingcategory.h> -#define MAX_BUFFERS_IN_QUEUE 4 +static Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder") QT_BEGIN_NAMESPACE @@ -57,96 +18,67 @@ namespace QFFmpeg class SteppingAudioRenderer : public Renderer { + Q_OBJECT public: - SteppingAudioRenderer(AudioDecoder *decoder, const QAudioFormat &format); - ~SteppingAudioRenderer() + SteppingAudioRenderer(const QAudioFormat &format) : Renderer({}), m_format(format) { } + + RenderingResult renderInternal(Frame frame) override { + if (!frame.isValid()) + return {}; + + if (!m_resampler) + m_resampler = std::make_unique<QFFmpegResampler>(frame.codec(), m_format); + + emit newAudioBuffer(m_resampler->resample(frame.avFrame())); + + return {}; } - void loop() override; - AudioDecoder *m_decoder; +signals: + void newAudioBuffer(QAudioBuffer); + +private: QAudioFormat m_format; - std::unique_ptr<Resampler> resampler; - bool atEndEmitted = false; + std::unique_ptr<QFFmpegResampler> m_resampler; }; -class AudioDecoder : public Decoder +class AudioDecoder : public PlaybackEngine { Q_OBJECT public: - explicit AudioDecoder(QFFmpegAudioDecoder *audioDecoder) - : Decoder(audioDecoder) - {} + explicit AudioDecoder(const QAudioFormat &format) : m_format(format) { } - void setup(const QAudioFormat &format) + RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType) override { - connect(this, &AudioDecoder::newAudioBuffer, audioDecoder, &QFFmpegAudioDecoder::newAudioBuffer); - connect(this, &AudioDecoder::isAtEnd, audioDecoder, &QFFmpegAudioDecoder::done); - m_format = format; - audioRenderer = new SteppingAudioRenderer(this, format); - audioRenderer->start(); - auto *stream = demuxer->addStream(m_currentAVStreamIndex[QPlatformMediaPlayer::AudioStream]); - audioRenderer->setStream(stream); + if (trackType != QPlatformMediaPlayer::AudioStream) + return RendererPtr{ {}, {} }; + + auto result = createPlaybackEngineObject<SteppingAudioRenderer>(m_format); + m_audioRenderer = result.get(); + + connect(result.get(), &SteppingAudioRenderer::newAudioBuffer, this, + &AudioDecoder::newAudioBuffer); + + return result; } void nextBuffer() { - audioRenderer->setPaused(false); + Q_ASSERT(m_audioRenderer); + Q_ASSERT(!m_audioRenderer->isStepForced()); + + m_audioRenderer->doForceStep(); + // updateObjectsPausedState(); } -Q_SIGNALS: - void newAudioBuffer(const QAudioBuffer &b); - void isAtEnd(); +signals: + void newAudioBuffer(QAudioBuffer); private: + QPointer<Renderer> m_audioRenderer; QAudioFormat m_format; }; - -SteppingAudioRenderer::SteppingAudioRenderer(AudioDecoder *decoder, const QAudioFormat &format) - : Renderer(QPlatformMediaPlayer::AudioStream) - , m_decoder(decoder) - , m_format(format) -{ -} - - -void SteppingAudioRenderer::loop() -{ - if (!streamDecoder) { - qCDebug(qLcAudioDecoder) << "no stream"; - timeOut = -1; // Avoid CPU load before play() - return; - } - - Frame frame = streamDecoder->takeFrame(); - if (!frame.isValid()) { - if (streamDecoder->isAtEnd()) { - if (!atEndEmitted) - emit m_decoder->isAtEnd(); - atEndEmitted = true; - paused = true; - doneStep(); - timeOut = -1; - return; - } - timeOut = 10; - streamDecoder->wake(); - return; - } - qCDebug(qLcAudioDecoder) << " got frame"; - - doneStep(); - - if (!resampler) - resampler.reset(new Resampler(frame.codec(), m_format)); - - auto buffer = resampler->resample(frame.avFrame()); - paused = true; - timeOut = -1; - - emit m_decoder->newAudioBuffer(buffer); -} - } @@ -155,10 +87,7 @@ QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent) { } -QFFmpegAudioDecoder::~QFFmpegAudioDecoder() -{ - delete decoder; -} +QFFmpegAudioDecoder::~QFFmpegAudioDecoder() = default; QUrl QFFmpegAudioDecoder::source() const { @@ -170,11 +99,8 @@ void QFFmpegAudioDecoder::setSource(const QUrl &fileName) stop(); m_sourceDevice = nullptr; - if (m_url == fileName) - return; - m_url = fileName; - - emit sourceChanged(); + if (std::exchange(m_url, fileName) != fileName) + sourceChanged(); } QIODevice *QFFmpegAudioDecoder::sourceDevice() const @@ -186,47 +112,65 @@ void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device) { stop(); m_url.clear(); - bool isSignalRequired = (m_sourceDevice != device); - m_sourceDevice = device; - if (isSignalRequired) + if (std::exchange(m_sourceDevice, device) != device) sourceChanged(); } void QFFmpegAudioDecoder::start() { qCDebug(qLcAudioDecoder) << "start"; - delete decoder; - decoder = new QFFmpeg::AudioDecoder(this); - decoder->setMedia(m_url, m_sourceDevice); - if (error() != QAudioDecoder::NoError) - goto error; - - decoder->setup(m_audioFormat); - if (error() != QAudioDecoder::NoError) - goto error; - decoder->play(); - if (error() != QAudioDecoder::NoError) - goto error; - decoder->nextBuffer(); - if (error() != QAudioDecoder::NoError) - goto error; + auto checkNoError = [this]() { + if (error() == QAudioDecoder::NoError) + return true; + + durationChanged(-1); + positionChanged(-1); + + m_decoder.reset(); + + return false; + }; + + m_decoder = std::make_unique<AudioDecoder>(m_audioFormat); + connect(m_decoder.get(), &AudioDecoder::errorOccured, this, &QFFmpegAudioDecoder::errorSignal); + connect(m_decoder.get(), &AudioDecoder::endOfStream, this, &QFFmpegAudioDecoder::done); + connect(m_decoder.get(), &AudioDecoder::newAudioBuffer, this, + &QFFmpegAudioDecoder::newAudioBuffer); + + QFFmpeg::MediaDataHolder::Maybe media = QFFmpeg::MediaDataHolder::create(m_url, m_sourceDevice, nullptr); + + if (media) { + Q_ASSERT(media.value()); + if (media.value()->streamInfo(QPlatformMediaPlayer::AudioStream).isEmpty()) + error(QAudioDecoder::FormatError, + QLatin1String("The media doesn't contain an audio stream")); + else + m_decoder->setMedia(std::move(*media.value())); + } else { + auto [code, description] = media.error(); + errorSignal(code, description); + } - setIsDecoding(true); - return; + if (!checkNoError()) + return; + + m_decoder->setState(QMediaPlayer::PausedState); + if (!checkNoError()) + return; - error: - durationChanged(-1); - positionChanged(-1); - delete decoder; - decoder = nullptr; + m_decoder->nextBuffer(); + if (!checkNoError()) + return; + durationChanged(m_decoder->duration() / 1000); + setIsDecoding(true); } void QFFmpegAudioDecoder::stop() { qCDebug(qLcAudioDecoder) << ">>>>> stop"; - if (decoder) { - decoder->stop(); + if (m_decoder) { + m_decoder.reset(); done(); } } @@ -238,26 +182,28 @@ QAudioFormat QFFmpegAudioDecoder::audioFormat() const void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format) { - if (m_audioFormat == format) - return; - - m_audioFormat = format; - formatChanged(m_audioFormat); + if (std::exchange(m_audioFormat, format) != format) + formatChanged(m_audioFormat); } QAudioBuffer QFFmpegAudioDecoder::read() { - auto b = m_audioBuffer; - qCDebug(qLcAudioDecoder) << "reading buffer" << b.startTime(); - m_audioBuffer = {}; + auto buffer = std::exchange(m_audioBuffer, QAudioBuffer{}); + if (!buffer.isValid()) + return buffer; + qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime(); bufferAvailableChanged(false); - if (decoder) - decoder->nextBuffer(); - return b; + if (m_decoder) + m_decoder->nextBuffer(); + return buffer; } void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b) { + Q_ASSERT(b.isValid()); + Q_ASSERT(!m_audioBuffer.isValid()); + Q_ASSERT(!bufferAvailable()); + qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime(); m_audioBuffer = b; const qint64 pos = b.startTime(); @@ -272,6 +218,30 @@ void QFFmpegAudioDecoder::done() finished(); } +void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString) +{ + // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical. + // Map them. + switch (QMediaPlayer::Error(err)) { + case QMediaPlayer::NoError: + error(QAudioDecoder::NoError, errorString); + break; + case QMediaPlayer::ResourceError: + error(QAudioDecoder::ResourceError, errorString); + break; + case QMediaPlayer::FormatError: + error(QAudioDecoder::FormatError, errorString); + break; + case QMediaPlayer::NetworkError: + // fall through, Network error doesn't exist in QAudioDecoder + case QMediaPlayer::AccessDeniedError: + error(QAudioDecoder::AccessDeniedError, errorString); + break; + } +} + QT_END_NAMESPACE +#include "moc_qffmpegaudiodecoder_p.cpp" + #include "qffmpegaudiodecoder.moc" |