diff options
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp')
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp new file mode 100644 index 000000000..f708f5021 --- /dev/null +++ b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp @@ -0,0 +1,202 @@ +// Copyright (C) 2021 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 "qffmpegsurfacecapturegrabber_p.h" + +#include <qelapsedtimer.h> +#include <qloggingcategory.h> +#include <qthread.h> +#include <qtimer.h> + +QT_BEGIN_NAMESPACE + +static Q_LOGGING_CATEGORY(qLcScreenCaptureGrabber, "qt.multimedia.ffmpeg.surfacecapturegrabber"); + +namespace { + +class GrabbingProfiler +{ +public: + auto measure() + { + m_elapsedTimer.start(); + return qScopeGuard([&]() { + const auto nsecsElapsed = m_elapsedTimer.nsecsElapsed(); + ++m_number; + m_wholeTime += nsecsElapsed; + +#ifdef DUMP_SCREEN_CAPTURE_PROFILING + qDebug() << "screen grabbing time:" << nsecsElapsed << "avg:" << avgTime() + << "number:" << m_number; +#endif + }); + } + + qreal avgTime() const + { + return m_number ? m_wholeTime / (m_number * 1000000.) : 0.; + } + + qint64 number() const + { + return m_number; + } + +private: + QElapsedTimer m_elapsedTimer; + qint64 m_wholeTime = 0; + qint64 m_number = 0; +}; + +} // namespace + +struct QFFmpegSurfaceCaptureGrabber::GrabbingContext +{ + GrabbingProfiler profiler; + QTimer timer; + QElapsedTimer elapsedTimer; + qint64 lastFrameTime = 0; +}; + +class QFFmpegSurfaceCaptureGrabber::GrabbingThread : public QThread +{ +public: + GrabbingThread(QFFmpegSurfaceCaptureGrabber& grabber) + : m_grabber(grabber) + {} + +protected: + void run() override + { + m_grabber.initializeGrabbingContext(); + + if (!m_grabber.isGrabbingContextInitialized()) + return; + + exec(); + m_grabber.finalizeGrabbingContext(); + } + +private: + QFFmpegSurfaceCaptureGrabber& m_grabber; +}; + +QFFmpegSurfaceCaptureGrabber::QFFmpegSurfaceCaptureGrabber(ThreadPolicy threadPolicy) +{ + setFrameRate(DefaultScreenCaptureFrameRate); + + if (threadPolicy == CreateGrabbingThread) + m_thread = std::make_unique<GrabbingThread>(*this); +} + +void QFFmpegSurfaceCaptureGrabber::start() +{ + if (m_thread) + m_thread->start(); + else if (!isGrabbingContextInitialized()) + initializeGrabbingContext(); +} + +QFFmpegSurfaceCaptureGrabber::~QFFmpegSurfaceCaptureGrabber() = default; + +void QFFmpegSurfaceCaptureGrabber::setFrameRate(qreal rate) +{ + rate = qBound(MinScreenCaptureFrameRate, rate, MaxScreenCaptureFrameRate); + if (std::exchange(m_rate, rate) != rate) { + qCDebug(qLcScreenCaptureGrabber) << "Screen capture rate has been changed:" << m_rate; + + updateTimerInterval(); + } +} + +qreal QFFmpegSurfaceCaptureGrabber::frameRate() const +{ + return m_rate; +} + +void QFFmpegSurfaceCaptureGrabber::stop() +{ + if (m_thread) + { + m_thread->quit(); + m_thread->wait(); + } + else if (isGrabbingContextInitialized()) + { + finalizeGrabbingContext(); + } +} + +void QFFmpegSurfaceCaptureGrabber::updateError(QPlatformSurfaceCapture::Error error, + const QString &description) +{ + const auto prevError = std::exchange(m_prevError, error); + + if (error != QPlatformSurfaceCapture::NoError + || prevError != QPlatformSurfaceCapture::NoError) { + emit errorUpdated(error, description); + } + + updateTimerInterval(); +} + +void QFFmpegSurfaceCaptureGrabber::updateTimerInterval() +{ + const qreal rate = m_prevError && *m_prevError != QPlatformSurfaceCapture::NoError + ? MinScreenCaptureFrameRate + : m_rate; + const int interval = static_cast<int>(1000 / rate); + if (m_context && m_context->timer.interval() != interval) + m_context->timer.setInterval(interval); +} + +void QFFmpegSurfaceCaptureGrabber::initializeGrabbingContext() +{ + Q_ASSERT(!isGrabbingContextInitialized()); + qCDebug(qLcScreenCaptureGrabber) << "screen capture started"; + + m_context = std::make_unique<GrabbingContext>(); + m_context->timer.setTimerType(Qt::PreciseTimer); + updateTimerInterval(); + + m_context->elapsedTimer.start(); + + auto doGrab = [this]() { + auto measure = m_context->profiler.measure(); + + auto frame = grabFrame(); + + if (frame.isValid()) { + frame.setStartTime(m_context->lastFrameTime); + frame.setEndTime(m_context->elapsedTimer.nsecsElapsed() / 1000); + m_context->lastFrameTime = frame.endTime(); + + updateError(QPlatformSurfaceCapture::NoError); + + emit frameGrabbed(frame); + } + }; + + doGrab(); + + m_context->timer.callOnTimeout(&m_context->timer, doGrab); + m_context->timer.start(); +} + +void QFFmpegSurfaceCaptureGrabber::finalizeGrabbingContext() +{ + Q_ASSERT(isGrabbingContextInitialized()); + qCDebug(qLcScreenCaptureGrabber) + << "end screen capture thread; avg grabbing time:" << m_context->profiler.avgTime() + << "ms, grabbings number:" << m_context->profiler.number(); + m_context.reset(); +} + +bool QFFmpegSurfaceCaptureGrabber::isGrabbingContextInitialized() const +{ + return m_context != nullptr; +} + +QT_END_NAMESPACE + +#include "moc_qffmpegsurfacecapturegrabber_p.cpp" |