diff options
Diffstat (limited to 'src/gsttools/qvideosurfacegstsink.cpp')
-rw-r--r-- | src/gsttools/qvideosurfacegstsink.cpp | 712 |
1 files changed, 0 insertions, 712 deletions
diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp deleted file mode 100644 index c6951bdef..000000000 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ /dev/null @@ -1,712 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ - -#include <qabstractvideosurface.h> -#include <qvideoframe.h> -#include <QDebug> -#include <QMap> -#include <QDebug> -#include <QThread> - -#include <private/qmediapluginloader_p.h> -#include "qgstvideobuffer_p.h" - -#include "qgstutils_p.h" -#include "qvideosurfacegstsink_p.h" - -#if GST_VERSION_MAJOR >=1 -#include <gst/video/video.h> -#endif - -//#define DEBUG_VIDEO_SURFACE_SINK - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, bufferPoolLoader, - (QGstBufferPoolInterface_iid, QLatin1String("video/bufferpool"), Qt::CaseInsensitive)) - - -QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate( - QAbstractVideoSurface *surface) - : m_surface(surface) -{ - if (m_surface) { - const auto instances = bufferPoolLoader()->instances(QGstBufferPoolPluginKey); - for (QObject *instance : instances) { - auto plugin = qobject_cast<QGstBufferPoolInterface*>(instance); - - if (plugin) { - m_pools.append(plugin); - } - } - - updateSupportedFormats(); - connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); - } -} - -QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() -{ -} - -QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const -{ - QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); - - if (!m_surface) - return QList<QVideoFrame::PixelFormat>(); - else if (handleType == QAbstractVideoBuffer::NoHandle) - return m_supportedPixelFormats; - else if (handleType == m_pool->handleType()) - return m_supportedPoolPixelFormats; - else - return m_surface->supportedPixelFormats(handleType); -} - -QVideoSurfaceFormat QVideoSurfaceGstDelegate::surfaceFormat() const -{ - QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); - return m_format; -} - -bool QVideoSurfaceGstDelegate::start(const QVideoSurfaceFormat &format, int bytesPerLine) -{ - if (!m_surface) - return false; - - QMutexLocker locker(&m_mutex); - - m_format = format; - m_bytesPerLine = bytesPerLine; - - if (QThread::currentThread() == thread()) { - m_started = !m_surface.isNull() ? m_surface->start(m_format) : false; - } else { - m_started = false; - m_startCanceled = false; - QMetaObject::invokeMethod(this, "queuedStart", Qt::QueuedConnection); - - /* - Waiting for start() to be invoked in the main thread may block - if gstreamer blocks the main thread until this call is finished. - This situation is rare and usually caused by setState(Null) - while pipeline is being prerolled. - - The proper solution to this involves controlling gstreamer pipeline from - other thread than video surface. - - Currently start() fails if wait() timed out. - */ - if (!m_setupCondition.wait(&m_mutex, 1000)) { - qWarning() << "Failed to start video surface due to main thread blocked."; - m_startCanceled = true; - } - } - - m_format = m_surface->surfaceFormat(); - - return m_started; -} - -void QVideoSurfaceGstDelegate::stop() -{ - if (!m_surface) - return; - - QMutexLocker locker(&m_mutex); - - if (QThread::currentThread() == thread()) { - if (!m_surface.isNull()) - m_surface->stop(); - } else { - QMetaObject::invokeMethod(this, "queuedStop", Qt::QueuedConnection); - - // Waiting for stop() to be invoked in the main thread may block - // if gstreamer blocks the main thread until this call is finished. - m_setupCondition.wait(&m_mutex, 500); - } - - m_started = false; -} - -void QVideoSurfaceGstDelegate::unlock() -{ - QMutexLocker locker(&m_mutex); - - m_startCanceled = true; - m_setupCondition.wakeAll(); - m_renderCondition.wakeAll(); -} - -bool QVideoSurfaceGstDelegate::isActive() -{ - QMutexLocker locker(&m_mutex); - return !m_surface.isNull() && m_surface->isActive(); -} - -void QVideoSurfaceGstDelegate::clearPoolBuffers() -{ - QMutexLocker locker(&m_poolMutex); - if (m_pool) - m_pool->clear(); -} - -void QVideoSurfaceGstDelegate::flush() -{ - QMutexLocker locker(&m_mutex); - - m_frame = QVideoFrame(); - m_renderCondition.wakeAll(); - - if (QThread::currentThread() == thread()) { - if (!m_surface.isNull()) - m_surface->present(m_frame); - } else { - QMetaObject::invokeMethod(this, "queuedFlush", Qt::QueuedConnection); - } -} - -GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) -{ - if (!m_surface) { - qWarning() << "Rendering video frame to deleted surface, skip."; - //return GST_FLOW_NOT_NEGOTIATED; - return GST_FLOW_OK; - } - - QMutexLocker locker(&m_mutex); - - QAbstractVideoBuffer *videoBuffer = 0; - - if (m_pool) - videoBuffer = m_pool->prepareVideoBuffer(buffer, m_bytesPerLine); - - if (!videoBuffer) - videoBuffer = new QGstVideoBuffer(buffer, m_bytesPerLine); - - m_frame = QVideoFrame( - videoBuffer, - m_format.frameSize(), - m_format.pixelFormat()); - - QGstUtils::setFrameTimeStamps(&m_frame, buffer); - - m_renderReturn = GST_FLOW_OK; - - if (QThread::currentThread() == thread()) { - if (!m_surface.isNull()) - m_surface->present(m_frame); - else - qWarning() << "m_surface.isNull()."; - } else { - QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection); - m_renderCondition.wait(&m_mutex, 300); - } - - m_frame = QVideoFrame(); - return m_renderReturn; -} - -void QVideoSurfaceGstDelegate::queuedStart() -{ - QMutexLocker locker(&m_mutex); - - if (!m_startCanceled) { - m_started = m_surface->start(m_format); - m_setupCondition.wakeAll(); - } -} - -void QVideoSurfaceGstDelegate::queuedStop() -{ - QMutexLocker locker(&m_mutex); - - m_surface->stop(); - - m_setupCondition.wakeAll(); -} - -void QVideoSurfaceGstDelegate::queuedFlush() -{ - QMutexLocker locker(&m_mutex); - - if (!m_surface.isNull()) - m_surface->present(QVideoFrame()); -} - -void QVideoSurfaceGstDelegate::queuedRender() -{ - QMutexLocker locker(&m_mutex); - - if (!m_frame.isValid()) - return; - - if (m_surface.isNull()) { - qWarning() << "Rendering video frame to deleted surface, skip the frame"; - m_renderReturn = GST_FLOW_OK; - } else if (m_surface->present(m_frame)) { - m_renderReturn = GST_FLOW_OK; - } else { - switch (m_surface->error()) { - case QAbstractVideoSurface::NoError: - m_renderReturn = GST_FLOW_OK; - break; - case QAbstractVideoSurface::StoppedError: - //It's likely we are in process of changing video output - //and the surface is already stopped, ignore the frame - m_renderReturn = GST_FLOW_OK; - break; - default: - qWarning() << "Failed to render video frame:" << m_surface->error(); - m_renderReturn = GST_FLOW_OK; - break; - } - } - - m_renderCondition.wakeAll(); -} - -void QVideoSurfaceGstDelegate::updateSupportedFormats() -{ - QGstBufferPoolInterface *newPool = 0; - for (QGstBufferPoolInterface *pool : qAsConst(m_pools)) { - if (!m_surface->supportedPixelFormats(pool->handleType()).isEmpty()) { - newPool = pool; - break; - } - } - - if (newPool != m_pool) { - QMutexLocker lock(&m_poolMutex); - - if (m_pool) - m_pool->clear(); - m_pool = newPool; - } - - QMutexLocker locker(&m_mutex); - - m_supportedPixelFormats.clear(); - m_supportedPoolPixelFormats.clear(); - if (m_surface) { - m_supportedPixelFormats = m_surface->supportedPixelFormats(); - if (m_pool) - m_supportedPoolPixelFormats = m_surface->supportedPixelFormats(m_pool->handleType()); - } -} - -static GstVideoSinkClass *sink_parent_class; - -#define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast<QVideoSurfaceGstSink *>(s)) - -QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *surface) -{ - QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>( - g_object_new(QVideoSurfaceGstSink::get_type(), 0)); - - sink->delegate = new QVideoSurfaceGstDelegate(surface); - - g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); - - return sink; -} - -GType QVideoSurfaceGstSink::get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = - { - sizeof(QVideoSurfaceGstSinkClass), // class_size - base_init, // base_init - nullptr, // base_finalize - class_init, // class_init - nullptr, // class_finalize - nullptr, // class_data - sizeof(QVideoSurfaceGstSink), // instance_size - 0, // n_preallocs - instance_init, // instance_init - 0 // value_table - }; - - type = g_type_register_static( - GST_TYPE_VIDEO_SINK, "QVideoSurfaceGstSink", &info, GTypeFlags(0)); - } - - return type; -} - -void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data) -{ - Q_UNUSED(class_data); - - sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class)); - - GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class); - base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps; - base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps; - base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc; - base_sink_class->start = QVideoSurfaceGstSink::start; - base_sink_class->stop = QVideoSurfaceGstSink::stop; - base_sink_class->unlock = QVideoSurfaceGstSink::unlock; - -#if GST_CHECK_VERSION(0, 10, 25) - GstVideoSinkClass *video_sink_class = reinterpret_cast<GstVideoSinkClass *>(g_class); - video_sink_class->show_frame = QVideoSurfaceGstSink::show_frame; -#else - base_sink_class->preroll = QVideoSurfaceGstSink::preroll; - base_sink_class->render = QVideoSurfaceGstSink::render; -#endif - - GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); - element_class->change_state = QVideoSurfaceGstSink::change_state; - - GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class); - object_class->finalize = QVideoSurfaceGstSink::finalize; -} - -void QVideoSurfaceGstSink::base_init(gpointer g_class) -{ - static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( - "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( - "video/x-raw-rgb, " - "framerate = (fraction) [ 0, MAX ], " - "width = (int) [ 1, MAX ], " - "height = (int) [ 1, MAX ]; " - "video/x-raw-yuv, " - "framerate = (fraction) [ 0, MAX ], " - "width = (int) [ 1, MAX ], " - "height = (int) [ 1, MAX ]")); - - gst_element_class_add_pad_template( - GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); -} - -void QVideoSurfaceGstSink::instance_init(GTypeInstance *instance, gpointer g_class) -{ - VO_SINK(instance); - - Q_UNUSED(g_class); - - sink->delegate = 0; - - sink->lastRequestedCaps = 0; - sink->lastBufferCaps = 0; - sink->lastSurfaceFormat = new QVideoSurfaceFormat; -} - -void QVideoSurfaceGstSink::finalize(GObject *object) -{ - VO_SINK(object); - - delete sink->lastSurfaceFormat; - sink->lastSurfaceFormat = 0; - - if (sink->lastBufferCaps) - gst_caps_unref(sink->lastBufferCaps); - sink->lastBufferCaps = 0; - - if (sink->lastRequestedCaps) - gst_caps_unref(sink->lastRequestedCaps); - sink->lastRequestedCaps = 0; - - delete sink->delegate; - - // Chain up - G_OBJECT_CLASS(sink_parent_class)->finalize(object); -} - -void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(o); - Q_UNUSED(p); - QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(d); - - gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr); - - if (!showPrerollFrame) { - GstState state = GST_STATE_VOID_PENDING; - gst_element_get_state(GST_ELEMENT(sink), &state, nullptr, GST_CLOCK_TIME_NONE); - // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means - // the QMediaPlayer was stopped from the paused state. - // We need to flush the current frame. - if (state == GST_STATE_PAUSED) - sink->delegate->flush(); - } -} - -GstStateChangeReturn QVideoSurfaceGstSink::change_state(GstElement *element, GstStateChange transition) -{ - QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(element); - - gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, nullptr); - - // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to - // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. - // We need to flush the current frame. - if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) - sink->delegate->flush(); - - return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); -} - -GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) -{ - VO_SINK(base); - - // Find the supported pixel formats - // with buffer pool specific formats listed first - QList<QVideoFrame::PixelFormat> supportedFormats; - - QList<QVideoFrame::PixelFormat> poolHandleFormats; - sink->delegate->poolMutex()->lock(); - QGstBufferPoolInterface *pool = sink->delegate->pool(); - - if (pool) - poolHandleFormats = sink->delegate->supportedPixelFormats(pool->handleType()); - sink->delegate->poolMutex()->unlock(); - - supportedFormats = poolHandleFormats; - const auto supportedPixelFormats = sink->delegate->supportedPixelFormats(); - for (QVideoFrame::PixelFormat format : supportedPixelFormats) { - if (!poolHandleFormats.contains(format)) - supportedFormats.append(format); - } - - return QGstUtils::capsForFormats(supportedFormats); -} - -gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) -{ - VO_SINK(base); - -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << "set_caps:"; - qDebug() << gst_caps_to_string(caps); -#endif - - if (!caps) { - sink->delegate->stop(); - - return TRUE; - } else { - int bytesPerLine = 0; - QGstBufferPoolInterface *pool = sink->delegate->pool(); - QAbstractVideoBuffer::HandleType handleType = - pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle; - - QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine, handleType); - - if (sink->delegate->isActive()) { - QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat(); - - if (format.pixelFormat() == surfaceFormst.pixelFormat() && - format.frameSize() == surfaceFormst.frameSize()) - return TRUE; - else - sink->delegate->stop(); - } - - if (sink->lastRequestedCaps) - gst_caps_unref(sink->lastRequestedCaps); - sink->lastRequestedCaps = 0; - -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << "Starting video surface, format:"; - qDebug() << format; - qDebug() << "bytesPerLine:" << bytesPerLine; -#endif - - if (sink->delegate->start(format, bytesPerLine)) - return TRUE; - else - qWarning() << "Failed to start video surface"; - } - - return FALSE; -} - -GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( - GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) -{ - VO_SINK(base); - - Q_UNUSED(offset); - Q_UNUSED(size); - - if (!buffer) - return GST_FLOW_ERROR; - - *buffer = nullptr; - - if (!sink->delegate->pool()) - return GST_FLOW_OK; - - QMutexLocker poolLock(sink->delegate->poolMutex()); - QGstBufferPoolInterface *pool = sink->delegate->pool(); - - if (!pool) - return GST_FLOW_OK; - - if (sink->lastRequestedCaps && gst_caps_is_equal(sink->lastRequestedCaps, caps)) { - //qDebug() << "reusing last caps"; - *buffer = GST_BUFFER(pool->takeBuffer(*sink->lastSurfaceFormat, sink->lastBufferCaps)); - return GST_FLOW_OK; - } - - if (sink->delegate->supportedPixelFormats(pool->handleType()).isEmpty()) { - //qDebug() << "sink doesn't support native pool buffers, skip buffers allocation"; - return GST_FLOW_OK; - } - - poolLock.unlock(); - - GstCaps *intersection = gst_caps_intersect(get_caps(GST_BASE_SINK(sink)), caps); - - if (gst_caps_is_empty (intersection)) { - gst_caps_unref(intersection); - return GST_FLOW_NOT_NEGOTIATED; - } - - if (sink->delegate->isActive()) { - //if format was changed, restart the surface - QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection); - QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); - - if (format.pixelFormat() != surfaceFormat.pixelFormat() || - format.frameSize() != surfaceFormat.frameSize()) { -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << "new format requested, restart video surface"; -#endif - sink->delegate->stop(); - } - } - - if (!sink->delegate->isActive()) { - int bytesPerLine = 0; - QGstBufferPoolInterface *pool = sink->delegate->pool(); - QAbstractVideoBuffer::HandleType handleType = - pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle; - - QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection, &bytesPerLine, handleType); - - if (!sink->delegate->start(format, bytesPerLine)) { - qWarning() << "failed to start video surface"; - return GST_FLOW_NOT_NEGOTIATED; - } - } - - poolLock.relock(); - pool = sink->delegate->pool(); - - QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); - - if (!pool->isFormatSupported(surfaceFormat)) { - qDebug() << "sink doesn't support native pool format, skip custom buffers allocation"; - return GST_FLOW_OK; - } - - if (sink->lastRequestedCaps) - gst_caps_unref(sink->lastRequestedCaps); - sink->lastRequestedCaps = caps; - gst_caps_ref(sink->lastRequestedCaps); - - if (sink->lastBufferCaps) - gst_caps_unref(sink->lastBufferCaps); - sink->lastBufferCaps = intersection; - gst_caps_ref(sink->lastBufferCaps); - - *sink->lastSurfaceFormat = surfaceFormat; - - *buffer = GST_BUFFER(pool->takeBuffer(surfaceFormat, intersection)); - - return GST_FLOW_OK; -} - -gboolean QVideoSurfaceGstSink::start(GstBaseSink *base) -{ - Q_UNUSED(base); - return TRUE; -} - -gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base) -{ - VO_SINK(base); - sink->delegate->clearPoolBuffers(); - - return TRUE; -} - -gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base) -{ - VO_SINK(base); - sink->delegate->unlock(); - return TRUE; -} - -#if GST_CHECK_VERSION(0, 10, 25) -GstFlowReturn QVideoSurfaceGstSink::show_frame(GstVideoSink *base, GstBuffer *buffer) -{ - VO_SINK(base); - return sink->delegate->render(buffer); -} -#else -GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer) -{ - VO_SINK(base); - gboolean showPrerollFrame = true; - g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr); - - if (showPrerollFrame) - return sink->delegate->render(buffer); - - return GST_FLOW_OK; -} - -GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer) -{ - VO_SINK(base); - return sink->delegate->render(buffer); -} -#endif - -QT_END_NAMESPACE |