1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <qabstractvideosurface.h>
41 #include <qvideoframe.h>
42 #include <QDebug>
43 #include <QMap>
44 #include <QDebug>
45 #include <QThread>
46 
47 #include <private/qmediapluginloader_p.h>
48 #include "qgstvideobuffer_p.h"
49 
50 #include "qgstutils_p.h"
51 #include "qvideosurfacegstsink_p.h"
52 
53 #if GST_VERSION_MAJOR >=1
54 #include <gst/video/video.h>
55 #endif
56 
57 //#define DEBUG_VIDEO_SURFACE_SINK
58 
59 QT_BEGIN_NAMESPACE
60 
61 Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, bufferPoolLoader,
62         (QGstBufferPoolInterface_iid, QLatin1String("video/bufferpool"), Qt::CaseInsensitive))
63 
64 
QVideoSurfaceGstDelegate(QAbstractVideoSurface * surface)65 QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(
66     QAbstractVideoSurface *surface)
67     : m_surface(surface)
68 {
69     if (m_surface) {
70         const auto instances = bufferPoolLoader()->instances(QGstBufferPoolPluginKey);
71         for (QObject *instance : instances) {
72             auto plugin = qobject_cast<QGstBufferPoolInterface*>(instance);
73 
74             if (plugin) {
75                 m_pools.append(plugin);
76             }
77         }
78 
79         updateSupportedFormats();
80         connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats()));
81     }
82 }
83 
~QVideoSurfaceGstDelegate()84 QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate()
85 {
86 }
87 
supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const88 QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
89 {
90     QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
91 
92     if (!m_surface)
93         return QList<QVideoFrame::PixelFormat>();
94     else if (handleType == QAbstractVideoBuffer::NoHandle)
95         return m_supportedPixelFormats;
96     else if (handleType == m_pool->handleType())
97         return m_supportedPoolPixelFormats;
98     else
99         return m_surface->supportedPixelFormats(handleType);
100 }
101 
surfaceFormat() const102 QVideoSurfaceFormat QVideoSurfaceGstDelegate::surfaceFormat() const
103 {
104     QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
105     return m_format;
106 }
107 
start(const QVideoSurfaceFormat & format,int bytesPerLine)108 bool QVideoSurfaceGstDelegate::start(const QVideoSurfaceFormat &format, int bytesPerLine)
109 {
110     if (!m_surface)
111         return false;
112 
113     QMutexLocker locker(&m_mutex);
114 
115     m_format = format;
116     m_bytesPerLine = bytesPerLine;
117 
118     if (QThread::currentThread() == thread()) {
119         m_started = !m_surface.isNull() ? m_surface->start(m_format) : false;
120     } else {
121         m_started = false;
122         m_startCanceled = false;
123         QMetaObject::invokeMethod(this, "queuedStart", Qt::QueuedConnection);
124 
125         /*
126         Waiting for start() to be invoked in the main thread may block
127         if gstreamer blocks the main thread until this call is finished.
128         This situation is rare and usually caused by setState(Null)
129         while pipeline is being prerolled.
130 
131         The proper solution to this involves controlling gstreamer pipeline from
132         other thread than video surface.
133 
134         Currently start() fails if wait() timed out.
135         */
136         if (!m_setupCondition.wait(&m_mutex, 1000)) {
137             qWarning() << "Failed to start video surface due to main thread blocked.";
138             m_startCanceled = true;
139         }
140     }
141 
142     m_format = m_surface->surfaceFormat();
143 
144     return m_started;
145 }
146 
stop()147 void QVideoSurfaceGstDelegate::stop()
148 {
149     if (!m_surface)
150         return;
151 
152     QMutexLocker locker(&m_mutex);
153 
154     if (QThread::currentThread() == thread()) {
155         if (!m_surface.isNull())
156             m_surface->stop();
157     } else {
158         QMetaObject::invokeMethod(this, "queuedStop", Qt::QueuedConnection);
159 
160         // Waiting for stop() to be invoked in the main thread may block
161         // if gstreamer blocks the main thread until this call is finished.
162         m_setupCondition.wait(&m_mutex, 500);
163     }
164 
165     m_started = false;
166 }
167 
unlock()168 void QVideoSurfaceGstDelegate::unlock()
169 {
170     QMutexLocker locker(&m_mutex);
171 
172     m_startCanceled = true;
173     m_setupCondition.wakeAll();
174     m_renderCondition.wakeAll();
175 }
176 
isActive()177 bool QVideoSurfaceGstDelegate::isActive()
178 {
179     QMutexLocker locker(&m_mutex);
180     return !m_surface.isNull() && m_surface->isActive();
181 }
182 
clearPoolBuffers()183 void QVideoSurfaceGstDelegate::clearPoolBuffers()
184 {
185     QMutexLocker locker(&m_poolMutex);
186     if (m_pool)
187         m_pool->clear();
188 }
189 
flush()190 void QVideoSurfaceGstDelegate::flush()
191 {
192     QMutexLocker locker(&m_mutex);
193 
194     m_frame = QVideoFrame();
195     m_renderCondition.wakeAll();
196 
197     if (QThread::currentThread() == thread()) {
198         if (!m_surface.isNull())
199             m_surface->present(m_frame);
200     } else {
201         QMetaObject::invokeMethod(this, "queuedFlush", Qt::QueuedConnection);
202     }
203 }
204 
render(GstBuffer * buffer)205 GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer)
206 {
207     if (!m_surface) {
208         qWarning() << "Rendering video frame to deleted surface, skip.";
209         //return GST_FLOW_NOT_NEGOTIATED;
210         return GST_FLOW_OK;
211     }
212 
213     QMutexLocker locker(&m_mutex);
214 
215     QAbstractVideoBuffer *videoBuffer = 0;
216 
217     if (m_pool)
218         videoBuffer = m_pool->prepareVideoBuffer(buffer, m_bytesPerLine);
219 
220     if (!videoBuffer)
221         videoBuffer = new QGstVideoBuffer(buffer, m_bytesPerLine);
222 
223     m_frame = QVideoFrame(
224             videoBuffer,
225             m_format.frameSize(),
226             m_format.pixelFormat());
227 
228     QGstUtils::setFrameTimeStamps(&m_frame, buffer);
229 
230     m_renderReturn = GST_FLOW_OK;
231 
232     if (QThread::currentThread() == thread()) {
233         if (!m_surface.isNull())
234             m_surface->present(m_frame);
235         else
236             qWarning() << "m_surface.isNull().";
237     } else {
238         QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection);
239         m_renderCondition.wait(&m_mutex, 300);
240     }
241 
242     m_frame = QVideoFrame();
243     return m_renderReturn;
244 }
245 
queuedStart()246 void QVideoSurfaceGstDelegate::queuedStart()
247 {
248     QMutexLocker locker(&m_mutex);
249 
250     if (!m_startCanceled) {
251         m_started = m_surface->start(m_format);
252         m_setupCondition.wakeAll();
253     }
254 }
255 
queuedStop()256 void QVideoSurfaceGstDelegate::queuedStop()
257 {
258     QMutexLocker locker(&m_mutex);
259 
260     m_surface->stop();
261 
262     m_setupCondition.wakeAll();
263 }
264 
queuedFlush()265 void QVideoSurfaceGstDelegate::queuedFlush()
266 {
267     QMutexLocker locker(&m_mutex);
268 
269     if (!m_surface.isNull())
270         m_surface->present(QVideoFrame());
271 }
272 
queuedRender()273 void QVideoSurfaceGstDelegate::queuedRender()
274 {
275     QMutexLocker locker(&m_mutex);
276 
277     if (!m_frame.isValid())
278         return;
279 
280     if (m_surface.isNull()) {
281         qWarning() << "Rendering video frame to deleted surface, skip the frame";
282         m_renderReturn = GST_FLOW_OK;
283     } else if (m_surface->present(m_frame)) {
284         m_renderReturn = GST_FLOW_OK;
285     } else {
286         switch (m_surface->error()) {
287         case QAbstractVideoSurface::NoError:
288             m_renderReturn = GST_FLOW_OK;
289             break;
290         case QAbstractVideoSurface::StoppedError:
291             //It's likely we are in process of changing video output
292             //and the surface is already stopped, ignore the frame
293             m_renderReturn = GST_FLOW_OK;
294             break;
295         default:
296             qWarning() << "Failed to render video frame:" << m_surface->error();
297             m_renderReturn = GST_FLOW_OK;
298             break;
299         }
300     }
301 
302     m_renderCondition.wakeAll();
303 }
304 
updateSupportedFormats()305 void QVideoSurfaceGstDelegate::updateSupportedFormats()
306 {
307     QGstBufferPoolInterface *newPool = 0;
308     for (QGstBufferPoolInterface *pool : qAsConst(m_pools)) {
309         if (!m_surface->supportedPixelFormats(pool->handleType()).isEmpty()) {
310             newPool = pool;
311             break;
312         }
313     }
314 
315     if (newPool != m_pool) {
316         QMutexLocker lock(&m_poolMutex);
317 
318         if (m_pool)
319             m_pool->clear();
320         m_pool = newPool;
321     }
322 
323     QMutexLocker locker(&m_mutex);
324 
325     m_supportedPixelFormats.clear();
326     m_supportedPoolPixelFormats.clear();
327     if (m_surface) {
328         m_supportedPixelFormats = m_surface->supportedPixelFormats();
329         if (m_pool)
330             m_supportedPoolPixelFormats = m_surface->supportedPixelFormats(m_pool->handleType());
331     }
332 }
333 
334 static GstVideoSinkClass *sink_parent_class;
335 
336 #define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast<QVideoSurfaceGstSink *>(s))
337 
createSink(QAbstractVideoSurface * surface)338 QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *surface)
339 {
340     QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(
341             g_object_new(QVideoSurfaceGstSink::get_type(), 0));
342 
343     sink->delegate = new QVideoSurfaceGstDelegate(surface);
344 
345     g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink);
346 
347     return sink;
348 }
349 
get_type()350 GType QVideoSurfaceGstSink::get_type()
351 {
352     static GType type = 0;
353 
354     if (type == 0) {
355         static const GTypeInfo info =
356         {
357             sizeof(QVideoSurfaceGstSinkClass),                    // class_size
358             base_init,                                         // base_init
359             nullptr,                                           // base_finalize
360             class_init,                                        // class_init
361             nullptr,                                           // class_finalize
362             nullptr,                                           // class_data
363             sizeof(QVideoSurfaceGstSink),                         // instance_size
364             0,                                                 // n_preallocs
365             instance_init,                                     // instance_init
366             0                                                  // value_table
367         };
368 
369         type = g_type_register_static(
370                 GST_TYPE_VIDEO_SINK, "QVideoSurfaceGstSink", &info, GTypeFlags(0));
371     }
372 
373     return type;
374 }
375 
class_init(gpointer g_class,gpointer class_data)376 void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data)
377 {
378     Q_UNUSED(class_data);
379 
380     sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class));
381 
382     GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class);
383     base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps;
384     base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps;
385     base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc;
386     base_sink_class->start = QVideoSurfaceGstSink::start;
387     base_sink_class->stop = QVideoSurfaceGstSink::stop;
388     base_sink_class->unlock = QVideoSurfaceGstSink::unlock;
389 
390 #if GST_CHECK_VERSION(0, 10, 25)
391     GstVideoSinkClass *video_sink_class = reinterpret_cast<GstVideoSinkClass *>(g_class);
392     video_sink_class->show_frame = QVideoSurfaceGstSink::show_frame;
393 #else
394     base_sink_class->preroll = QVideoSurfaceGstSink::preroll;
395     base_sink_class->render = QVideoSurfaceGstSink::render;
396 #endif
397 
398     GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class);
399     element_class->change_state = QVideoSurfaceGstSink::change_state;
400 
401     GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class);
402     object_class->finalize = QVideoSurfaceGstSink::finalize;
403 }
404 
base_init(gpointer g_class)405 void QVideoSurfaceGstSink::base_init(gpointer g_class)
406 {
407     static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
408             "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(
409                     "video/x-raw-rgb, "
410                     "framerate = (fraction) [ 0, MAX ], "
411                     "width = (int) [ 1, MAX ], "
412                     "height = (int) [ 1, MAX ]; "
413                     "video/x-raw-yuv, "
414                     "framerate = (fraction) [ 0, MAX ], "
415                     "width = (int) [ 1, MAX ], "
416                     "height = (int) [ 1, MAX ]"));
417 
418     gst_element_class_add_pad_template(
419             GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
420 }
421 
instance_init(GTypeInstance * instance,gpointer g_class)422 void QVideoSurfaceGstSink::instance_init(GTypeInstance *instance, gpointer g_class)
423 {
424     VO_SINK(instance);
425 
426     Q_UNUSED(g_class);
427 
428     sink->delegate = 0;
429 
430     sink->lastRequestedCaps = 0;
431     sink->lastBufferCaps = 0;
432     sink->lastSurfaceFormat = new QVideoSurfaceFormat;
433 }
434 
finalize(GObject * object)435 void QVideoSurfaceGstSink::finalize(GObject *object)
436 {
437     VO_SINK(object);
438 
439     delete sink->lastSurfaceFormat;
440     sink->lastSurfaceFormat = 0;
441 
442     if (sink->lastBufferCaps)
443         gst_caps_unref(sink->lastBufferCaps);
444     sink->lastBufferCaps = 0;
445 
446     if (sink->lastRequestedCaps)
447         gst_caps_unref(sink->lastRequestedCaps);
448     sink->lastRequestedCaps = 0;
449 
450     delete sink->delegate;
451 
452     // Chain up
453     G_OBJECT_CLASS(sink_parent_class)->finalize(object);
454 }
455 
handleShowPrerollChange(GObject * o,GParamSpec * p,gpointer d)456 void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d)
457 {
458     Q_UNUSED(o);
459     Q_UNUSED(p);
460     QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(d);
461 
462     gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default
463     g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr);
464 
465     if (!showPrerollFrame) {
466         GstState state = GST_STATE_VOID_PENDING;
467         gst_element_get_state(GST_ELEMENT(sink), &state, nullptr, GST_CLOCK_TIME_NONE);
468         // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means
469         // the QMediaPlayer was stopped from the paused state.
470         // We need to flush the current frame.
471         if (state == GST_STATE_PAUSED)
472             sink->delegate->flush();
473     }
474 }
475 
change_state(GstElement * element,GstStateChange transition)476 GstStateChangeReturn QVideoSurfaceGstSink::change_state(GstElement *element, GstStateChange transition)
477 {
478     QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(element);
479 
480     gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default
481     g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, nullptr);
482 
483     // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to
484     // GST_STATE_PAUSED, it means the QMediaPlayer was stopped.
485     // We need to flush the current frame.
486     if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame)
487         sink->delegate->flush();
488 
489     return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition);
490 }
491 
get_caps(GstBaseSink * base)492 GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base)
493 {
494     VO_SINK(base);
495 
496     // Find the supported pixel formats
497     // with buffer pool specific formats listed first
498     QList<QVideoFrame::PixelFormat> supportedFormats;
499 
500     QList<QVideoFrame::PixelFormat> poolHandleFormats;
501     sink->delegate->poolMutex()->lock();
502     QGstBufferPoolInterface *pool = sink->delegate->pool();
503 
504     if (pool)
505         poolHandleFormats = sink->delegate->supportedPixelFormats(pool->handleType());
506     sink->delegate->poolMutex()->unlock();
507 
508     supportedFormats = poolHandleFormats;
509     const auto supportedPixelFormats = sink->delegate->supportedPixelFormats();
510     for (QVideoFrame::PixelFormat format : supportedPixelFormats) {
511         if (!poolHandleFormats.contains(format))
512             supportedFormats.append(format);
513     }
514 
515     return QGstUtils::capsForFormats(supportedFormats);
516 }
517 
set_caps(GstBaseSink * base,GstCaps * caps)518 gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps)
519 {
520     VO_SINK(base);
521 
522 #ifdef DEBUG_VIDEO_SURFACE_SINK
523     qDebug() << "set_caps:";
524     qDebug() << gst_caps_to_string(caps);
525 #endif
526 
527     if (!caps) {
528         sink->delegate->stop();
529 
530         return TRUE;
531     } else {
532         int bytesPerLine = 0;
533         QGstBufferPoolInterface *pool = sink->delegate->pool();
534         QAbstractVideoBuffer::HandleType handleType =
535                 pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle;
536 
537         QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine, handleType);
538 
539         if (sink->delegate->isActive()) {
540             QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat();
541 
542             if (format.pixelFormat() == surfaceFormst.pixelFormat() &&
543                 format.frameSize() == surfaceFormst.frameSize())
544                 return TRUE;
545             else
546                 sink->delegate->stop();
547         }
548 
549         if (sink->lastRequestedCaps)
550             gst_caps_unref(sink->lastRequestedCaps);
551         sink->lastRequestedCaps = 0;
552 
553 #ifdef DEBUG_VIDEO_SURFACE_SINK
554         qDebug() << "Starting video surface, format:";
555         qDebug() << format;
556         qDebug() << "bytesPerLine:" << bytesPerLine;
557 #endif
558 
559         if (sink->delegate->start(format, bytesPerLine))
560             return TRUE;
561         else
562             qWarning() << "Failed to start video surface";
563     }
564 
565     return FALSE;
566 }
567 
buffer_alloc(GstBaseSink * base,guint64 offset,guint size,GstCaps * caps,GstBuffer ** buffer)568 GstFlowReturn QVideoSurfaceGstSink::buffer_alloc(
569         GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer)
570 {
571     VO_SINK(base);
572 
573     Q_UNUSED(offset);
574     Q_UNUSED(size);
575 
576     if (!buffer)
577         return GST_FLOW_ERROR;
578 
579     *buffer = nullptr;
580 
581     if (!sink->delegate->pool())
582         return GST_FLOW_OK;
583 
584     QMutexLocker poolLock(sink->delegate->poolMutex());
585     QGstBufferPoolInterface *pool = sink->delegate->pool();
586 
587     if (!pool)
588         return GST_FLOW_OK;
589 
590     if (sink->lastRequestedCaps && gst_caps_is_equal(sink->lastRequestedCaps, caps)) {
591         //qDebug() << "reusing last caps";
592         *buffer = GST_BUFFER(pool->takeBuffer(*sink->lastSurfaceFormat, sink->lastBufferCaps));
593         return GST_FLOW_OK;
594     }
595 
596     if (sink->delegate->supportedPixelFormats(pool->handleType()).isEmpty()) {
597         //qDebug() << "sink doesn't support native pool buffers, skip buffers allocation";
598         return GST_FLOW_OK;
599     }
600 
601     poolLock.unlock();
602 
603     GstCaps *intersection = gst_caps_intersect(get_caps(GST_BASE_SINK(sink)), caps);
604 
605     if (gst_caps_is_empty (intersection)) {
606         gst_caps_unref(intersection);
607         return GST_FLOW_NOT_NEGOTIATED;
608     }
609 
610     if (sink->delegate->isActive()) {
611         //if format was changed, restart the surface
612         QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection);
613         QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat();
614 
615         if (format.pixelFormat() != surfaceFormat.pixelFormat() ||
616             format.frameSize() != surfaceFormat.frameSize()) {
617 #ifdef DEBUG_VIDEO_SURFACE_SINK
618             qDebug() << "new format requested, restart video surface";
619 #endif
620             sink->delegate->stop();
621         }
622     }
623 
624     if (!sink->delegate->isActive()) {
625         int bytesPerLine = 0;
626         QGstBufferPoolInterface *pool = sink->delegate->pool();
627         QAbstractVideoBuffer::HandleType handleType =
628                 pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle;
629 
630         QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection, &bytesPerLine, handleType);
631 
632         if (!sink->delegate->start(format, bytesPerLine)) {
633             qWarning() << "failed to start video surface";
634             return GST_FLOW_NOT_NEGOTIATED;
635         }
636     }
637 
638     poolLock.relock();
639     pool = sink->delegate->pool();
640 
641     QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat();
642 
643     if (!pool->isFormatSupported(surfaceFormat)) {
644         qDebug() << "sink doesn't support native pool format, skip custom buffers allocation";
645         return GST_FLOW_OK;
646     }
647 
648     if (sink->lastRequestedCaps)
649         gst_caps_unref(sink->lastRequestedCaps);
650     sink->lastRequestedCaps = caps;
651     gst_caps_ref(sink->lastRequestedCaps);
652 
653     if (sink->lastBufferCaps)
654         gst_caps_unref(sink->lastBufferCaps);
655     sink->lastBufferCaps = intersection;
656     gst_caps_ref(sink->lastBufferCaps);
657 
658     *sink->lastSurfaceFormat = surfaceFormat;
659 
660     *buffer =  GST_BUFFER(pool->takeBuffer(surfaceFormat, intersection));
661 
662     return GST_FLOW_OK;
663 }
664 
start(GstBaseSink * base)665 gboolean QVideoSurfaceGstSink::start(GstBaseSink *base)
666 {
667     Q_UNUSED(base);
668     return TRUE;
669 }
670 
stop(GstBaseSink * base)671 gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base)
672 {
673     VO_SINK(base);
674     sink->delegate->clearPoolBuffers();
675 
676     return TRUE;
677 }
678 
unlock(GstBaseSink * base)679 gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base)
680 {
681     VO_SINK(base);
682     sink->delegate->unlock();
683     return TRUE;
684 }
685 
686 #if GST_CHECK_VERSION(0, 10, 25)
show_frame(GstVideoSink * base,GstBuffer * buffer)687 GstFlowReturn QVideoSurfaceGstSink::show_frame(GstVideoSink *base, GstBuffer *buffer)
688 {
689     VO_SINK(base);
690     return sink->delegate->render(buffer);
691 }
692 #else
preroll(GstBaseSink * base,GstBuffer * buffer)693 GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer)
694 {
695     VO_SINK(base);
696     gboolean showPrerollFrame = true;
697     g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr);
698 
699     if (showPrerollFrame)
700         return sink->delegate->render(buffer);
701 
702     return GST_FLOW_OK;
703 }
704 
render(GstBaseSink * base,GstBuffer * buffer)705 GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer)
706 {
707     VO_SINK(base);
708     return sink->delegate->render(buffer);
709 }
710 #endif
711 
712 QT_END_NAMESPACE
713