diff options
author | Jøger Hansegård <joger.hansegard@qt.io> | 2024-04-01 16:23:00 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2024-04-02 02:15:16 +0000 |
commit | ed9807159c24278bd87205fa171af738c84ade01 (patch) | |
tree | 1508d8b80b1b7d27271e016264abea805bf2cf38 | |
parent | 3e1bd6cf8e59a4802a4261b984a4c5627a7e0ee6 (diff) |
Move FFmpeg VideoEncoder to its own files
This helps readability because qffmpegvideoencoder_p.h file is quite
big and contains multiple classes.
Task-number: QTBUG-121792
Pick-to: 6.5
Change-Id: Ia7295daa879375547e0b4db37c270f9aad768fc3
Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
(cherry picked from commit ba1da65044863c79a23bb21ea0126de572d917cd)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 0e9a4bc4c83f4c4d1597dbd65e2a54d52a432edc)
5 files changed, 162 insertions, 122 deletions
diff --git a/src/plugins/multimedia/ffmpeg/CMakeLists.txt b/src/plugins/multimedia/ffmpeg/CMakeLists.txt index c94fa3e1f..62956556c 100644 --- a/src/plugins/multimedia/ffmpeg/CMakeLists.txt +++ b/src/plugins/multimedia/ffmpeg/CMakeLists.txt @@ -64,6 +64,8 @@ qt_internal_add_plugin(QFFmpegMediaPlugin recordingengine/qffmpegmuxer.cpp recordingengine/qffmpegrecordingengine_p.h recordingengine/qffmpegrecordingengine.cpp + recordingengine/qffmpegvideoencoder_p.h + recordingengine/qffmpegvideoencoder.cpp recordingengine/qffmpegvideoencoderutils_p.h recordingengine/qffmpegvideoencoderutils.cpp recordingengine/qffmpegvideoframeencoder_p.h diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp index b6fce596f..fb9c4bd3c 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp @@ -12,6 +12,7 @@ #include <private/qplatformcamera_p.h> #include <private/qplatformvideosource_p.h> #include "qffmpegvideobuffer_p.h" +#include "qffmpegvideoencoder_p.h" #include "qffmpegmediametadata_p.h" #include "qffmpegmuxer_p.h" #include <qloggingcategory.h> @@ -179,92 +180,6 @@ void RecordingEngine::addMediaFrameHandler(Args &&...args) m_connections.append(connection); } -VideoEncoder::VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings &settings, - const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat) - : EncoderThread(encoder) -{ - setObjectName(QLatin1String("VideoEncoder")); - - AVPixelFormat swFormat = QFFmpegVideoBuffer::toAVPixelFormat(format.pixelFormat()); - AVPixelFormat ffmpegPixelFormat = - hwFormat && *hwFormat != AV_PIX_FMT_NONE ? *hwFormat : swFormat; - auto frameRate = format.frameRate(); - if (frameRate <= 0.) { - qWarning() << "Invalid frameRate" << frameRate << "; Using the default instead"; - - // set some default frame rate since ffmpeg has UB if it's 0. - frameRate = 30.; - } - - m_frameEncoder = - VideoFrameEncoder::create(settings, format.frameSize(), frameRate, ffmpegPixelFormat, - swFormat, encoder->avFormatContext()); -} - -VideoEncoder::~VideoEncoder() = default; - -bool VideoEncoder::isValid() const -{ - return m_frameEncoder != nullptr; -} - -void VideoEncoder::addFrame(const QVideoFrame &frame) -{ - QMutexLocker locker(&m_queueMutex); - - // Drop frames if encoder can not keep up with the video source data rate - const bool queueFull = m_videoFrameQueue.size() >= m_maxQueueSize; - - if (queueFull) { - qCDebug(qLcFFmpegEncoder) << "RecordingEngine frame queue full. Frame lost."; - } else if (!m_paused.loadRelaxed()) { - m_videoFrameQueue.push(frame); - - locker.unlock(); // Avoid context switch on wake wake-up - - dataReady(); - } -} - -QVideoFrame VideoEncoder::takeFrame() -{ - QMutexLocker locker(&m_queueMutex); - return dequeueIfPossible(m_videoFrameQueue); -} - -void VideoEncoder::retrievePackets() -{ - if (!m_frameEncoder) - return; - while (auto packet = m_frameEncoder->retrievePacket()) - m_encoder->m_muxer->addPacket(std::move(packet)); -} - -void VideoEncoder::init() -{ - qCDebug(qLcFFmpegEncoder) << "VideoEncoder::init started video device thread."; - bool ok = m_frameEncoder->open(); - if (!ok) - emit m_encoder->error(QMediaRecorder::ResourceError, "Could not initialize encoder"); -} - -void VideoEncoder::cleanup() -{ - while (!m_videoFrameQueue.empty()) - processOne(); - if (m_frameEncoder) { - while (m_frameEncoder->sendFrame(nullptr) == AVERROR(EAGAIN)) - retrievePackets(); - retrievePackets(); - } -} - -bool VideoEncoder::hasData() const -{ - QMutexLocker locker(&m_queueMutex); - return !m_videoFrameQueue.empty(); -} - struct QVideoFrameHolder { QVideoFrame f; diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h index ca8fbf465..02f3e7d51 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h @@ -131,42 +131,6 @@ protected: RecordingEngine *m_encoder = nullptr; }; -class VideoEncoder : public EncoderThread -{ -public: - VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings &settings, - const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat); - ~VideoEncoder() override; - - bool isValid() const; - - void addFrame(const QVideoFrame &frame); - - void setPaused(bool b) override - { - EncoderThread::setPaused(b); - if (b) - m_baseTime.storeRelease(-1); - } - -private: - QVideoFrame takeFrame(); - void retrievePackets(); - - void init() override; - void cleanup() override; - bool hasData() const override; - void processOne() override; - -private: - mutable QMutex m_queueMutex; - std::queue<QVideoFrame> m_videoFrameQueue; - const size_t m_maxQueueSize = 10; // Arbitrarily chosen to limit memory usage (332 MB @ 4K) - - std::unique_ptr<VideoFrameEncoder> m_frameEncoder; - QAtomicInteger<qint64> m_baseTime = std::numeric_limits<qint64>::min(); - qint64 m_lastFrameTime = 0; -}; } QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp new file mode 100644 index 000000000..8a3883264 --- /dev/null +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp @@ -0,0 +1,104 @@ +// Copyright (C) 2024 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 "qffmpegvideoencoder_p.h" +#include "qffmpegmuxer_p.h" +#include "qffmpegvideobuffer_p.h" +#include "qffmpegvideoframeencoder_p.h" +#include <QtCore/qloggingcategory.h> + +QT_BEGIN_NAMESPACE + +namespace QFFmpeg { + +static Q_LOGGING_CATEGORY(qLcFFmpegVideoEncoder, "qt.multimedia.ffmpeg.videoencoder"); + + +VideoEncoder::VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings &settings, + const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat) + : EncoderThread(encoder) +{ + setObjectName(QLatin1String("VideoEncoder")); + + AVPixelFormat swFormat = QFFmpegVideoBuffer::toAVPixelFormat(format.pixelFormat()); + AVPixelFormat ffmpegPixelFormat = + hwFormat && *hwFormat != AV_PIX_FMT_NONE ? *hwFormat : swFormat; + auto frameRate = format.frameRate(); + if (frameRate <= 0.) { + qWarning() << "Invalid frameRate" << frameRate << "; Using the default instead"; + + // set some default frame rate since ffmpeg has UB if it's 0. + frameRate = 30.; + } + + m_frameEncoder = + VideoFrameEncoder::create(settings, format.frameSize(), frameRate, ffmpegPixelFormat, + swFormat, encoder->avFormatContext()); +} + +VideoEncoder::~VideoEncoder() = default; + +bool VideoEncoder::isValid() const +{ + return m_frameEncoder != nullptr; +} + +void VideoEncoder::addFrame(const QVideoFrame &frame) +{ + QMutexLocker locker(&m_queueMutex); + + // Drop frames if encoder can not keep up with the video source data rate + const bool queueFull = m_videoFrameQueue.size() >= m_maxQueueSize; + + if (queueFull) { + qCDebug(qLcFFmpegVideoEncoder) << "RecordingEngine frame queue full. Frame lost."; + } else if (!m_paused.loadRelaxed()) { + m_videoFrameQueue.push(frame); + + locker.unlock(); // Avoid context switch on wake wake-up + + dataReady(); + } +} + +QVideoFrame VideoEncoder::takeFrame() +{ + QMutexLocker locker(&m_queueMutex); + return dequeueIfPossible(m_videoFrameQueue); +} + +void VideoEncoder::retrievePackets() +{ + if (!m_frameEncoder) + return; + while (auto packet = m_frameEncoder->retrievePacket()) + m_encoder->m_muxer->addPacket(std::move(packet)); +} + +void VideoEncoder::init() +{ + qCDebug(qLcFFmpegVideoEncoder) << "VideoEncoder::init started video device thread."; + bool ok = m_frameEncoder->open(); + if (!ok) + emit m_encoder->error(QMediaRecorder::ResourceError, "Could not initialize encoder"); +} + +void VideoEncoder::cleanup() +{ + while (!m_videoFrameQueue.empty()) + processOne(); + if (m_frameEncoder) { + while (m_frameEncoder->sendFrame(nullptr) == AVERROR(EAGAIN)) + retrievePackets(); + retrievePackets(); + } +} + +bool VideoEncoder::hasData() const +{ + QMutexLocker locker(&m_queueMutex); + return !m_videoFrameQueue.empty(); +} + +} // namespace QFFmpeg + +QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h new file mode 100644 index 000000000..dd7d5014a --- /dev/null +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h @@ -0,0 +1,55 @@ +// Copyright (C) 2024 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 +#ifndef QFFMPEGVIDEOENCODER_P_H +#define QFFMPEGVIDEOENCODER_P_H + +#include "qffmpegrecordingengine_p.h" +#include "qffmpeg_p.h" +#include <QtCore/QtGlobal> + +QT_BEGIN_NAMESPACE + +namespace QFFmpeg { + +class VideoEncoder : public EncoderThread +{ +public: + VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings &settings, + const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat); + ~VideoEncoder() override; + + bool isValid() const; + + void addFrame(const QVideoFrame &frame); + + void setPaused(bool b) override + { + EncoderThread::setPaused(b); + if (b) + m_baseTime.storeRelease(-1); + } + +private: + QVideoFrame takeFrame(); + void retrievePackets(); + + void init() override; + void cleanup() override; + bool hasData() const override; + void processOne() override; + +private: + mutable QMutex m_queueMutex; + std::queue<QVideoFrame> m_videoFrameQueue; + const size_t m_maxQueueSize = 10; // Arbitrarily chosen to limit memory usage (332 MB @ 4K) + + std::unique_ptr<VideoFrameEncoder> m_frameEncoder; + QAtomicInteger<qint64> m_baseTime = std::numeric_limits<qint64>::min(); + qint64 m_lastFrameTime = 0; +}; + +} // namespace QFFmpeg + +QT_END_NAMESPACE + +#endif |