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