10#include <QCoreApplication>
12#include <private/qfactoryloader_p.h>
18#include <gst/video/video.h>
19#include <gst/video/gstvideometa.h>
26#if QT_CONFIG(gstreamer_gl)
31#if QT_CONFIG(linux_dmabuf)
32#include <gst/allocators/gstdmabuf.h>
51void QGstVideoRenderer::createSurfaceCaps()
80#if QT_CONFIG(gstreamer_gl)
83#if QT_CONFIG(linux_dmabuf)
109 m_surfaceCaps =
caps;
116 return m_surfaceCaps;
124 m_frameMirrored =
false;
145 if (!waitForAsyncEvent(&locker, &m_setupCondition, 1000) && !m_startCaps.
isNull()) {
146 qWarning() <<
"Failed to start video surface due to main thread blocked.";
165 waitForAsyncEvent(&locker, &m_setupCondition, 500);
188 m_renderBuffer =
nullptr;
197 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::render";
199 m_renderReturn = GST_FLOW_OK;
202 waitForAsyncEvent(&locker, &m_renderCondition, 300);
204 m_renderBuffer =
nullptr;
206 return m_renderReturn;
211#if QT_CONFIG(gstreamer_gl)
212 if (GST_QUERY_TYPE(
query) == GST_QUERY_CONTEXT) {
214 gst_query_parse_context_type(
query, &
type);
216 if (strcmp(
type,
"gst.gl.local_context") != 0)
223 gst_query_set_context(
query, gstGlContext);
235 if (GST_EVENT_TYPE(
event) != GST_EVENT_TAG)
238 GstTagList *taglist =
nullptr;
239 gst_event_parse_tag(
event, &taglist);
243 gchar *
value =
nullptr;
244 if (!gst_tag_list_get_string(taglist, GST_TAG_IMAGE_ORIENTATION, &
value))
247 constexpr const char rotate[] =
"rotate-";
248 constexpr const char flipRotate[] =
"flip-rotate-";
249 constexpr size_t rotateLen =
sizeof(rotate) - 1;
250 constexpr size_t flipRotateLen =
sizeof(flipRotate) - 1;
252 bool mirrored =
false;
253 int rotationAngle = 0;
255 if (!strncmp(rotate,
value, rotateLen)) {
256 rotationAngle = atoi(
value + rotateLen);
257 }
else if (!strncmp(flipRotate,
value, flipRotateLen)) {
261 rotationAngle = (180 + atoi(
value + flipRotateLen)) % 360;
265 m_frameMirrored = mirrored;
266 switch (rotationAngle) {
281 while (handleEvent(&locker)) {}
297 if (m_sink && !m_flushed)
309 }
else if (!m_startCaps.
isNull()) {
312 auto startCaps = m_startCaps;
319 m_format = startCaps.formatForCaps(&m_videoInfo);
320 memoryFormat = startCaps.memoryFormat();
324 }
else if (m_active) {
329 }
else if (m_renderBuffer) {
330 GstBuffer *
buffer = m_renderBuffer;
331 m_renderBuffer =
nullptr;
332 m_renderReturn = GST_FLOW_ERROR;
334 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::handleEvent(renderBuffer)" << m_active << m_sink;
335 if (m_active && m_sink) {
342 auto meta = gst_buffer_get_video_crop_meta (
buffer);
344 QRect vp(meta->x, meta->y, meta->width, meta->height);
346 qCDebug(qLcGstVideoRenderer) <<
Q_FUNC_INFO <<
" Update viewport on Metadata: [" << meta->height <<
"x" << meta->width <<
" | " << meta->x <<
"x" << meta->y <<
"]";
352 if (m_sink->inStoppedState()) {
353 qCDebug(qLcGstVideoRenderer) <<
" sending empty video frame";
359 frame.setMirrored(m_frameMirrored);
360 frame.setRotationAngle(m_frameRotationAngle);
362 qCDebug(qLcGstVideoRenderer) <<
" sending video frame";
363 m_sink->setVideoFrame(
frame);
370 m_renderReturn = GST_FLOW_OK;
382void QGstVideoRenderer::notify()
390bool QGstVideoRenderer::waitForAsyncEvent(
394 while (handleEvent(locker)) {}
408#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast<QGstVideoRendererSink *>(s))
414 g_object_new(QGstVideoRendererSink::get_type(),
nullptr));
416 g_signal_connect(G_OBJECT(gstSink),
"notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), gstSink);
426GType QGstVideoRendererSink::get_type()
428 static const GTypeInfo
info =
442 static const GType
type = []() {
443 const auto result = g_type_register_static(
444 GST_TYPE_VIDEO_SINK,
"QGstVideoRendererSink", &
info, GTypeFlags(0));
448 gst_element_register(
nullptr,
"qtvideosink", GST_RANK_PRIMARY,
result);
456void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data)
462 GstVideoSinkClass *video_sink_class =
reinterpret_cast<GstVideoSinkClass *
>(g_class);
463 video_sink_class->show_frame = QGstVideoRendererSink::show_frame;
465 GstBaseSinkClass *base_sink_class =
reinterpret_cast<GstBaseSinkClass *
>(g_class);
466 base_sink_class->get_caps = QGstVideoRendererSink::get_caps;
467 base_sink_class->set_caps = QGstVideoRendererSink::set_caps;
468 base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation;
469 base_sink_class->stop = QGstVideoRendererSink::stop;
470 base_sink_class->unlock = QGstVideoRendererSink::unlock;
471 base_sink_class->query = QGstVideoRendererSink::query;
472 base_sink_class->event = QGstVideoRendererSink::event;
474 GstElementClass *element_class =
reinterpret_cast<GstElementClass *
>(g_class);
475 element_class->change_state = QGstVideoRendererSink::change_state;
476 gst_element_class_set_metadata(element_class,
477 "Qt built-in video renderer sink",
479 "Qt default built-in video renderer sink",
482 GObjectClass *object_class =
reinterpret_cast<GObjectClass *
>(g_class);
483 object_class->finalize = QGstVideoRendererSink::finalize;
486void QGstVideoRendererSink::base_init(gpointer g_class)
488 static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
489 "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(
491 "framerate = (fraction) [ 0, MAX ], "
492 "width = (int) [ 1, MAX ], "
493 "height = (int) [ 1, MAX ]"));
495 gst_element_class_add_pad_template(
496 GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
499void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class)
511void QGstVideoRendererSink::finalize(GObject *
object)
515 delete sink->renderer;
521void QGstVideoRendererSink::handleShowPrerollChange(GObject *
o, GParamSpec *
p, gpointer
d)
527 gboolean showPrerollFrame =
true;
528 g_object_get(G_OBJECT(
sink),
"show-preroll-frame", &showPrerollFrame,
nullptr);
530 if (!showPrerollFrame) {
531 GstState
state = GST_STATE_VOID_PENDING;
532 GstClockTime
timeout = 10000000;
537 if (
state == GST_STATE_PAUSED)
538 sink->renderer->flush();
542GstStateChangeReturn QGstVideoRendererSink::change_state(
543 GstElement *element, GstStateChange transition)
547 gboolean showPrerollFrame =
true;
548 g_object_get(G_OBJECT(element),
"show-preroll-frame", &showPrerollFrame,
nullptr);
553 if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame)
554 sink->renderer->flush();
559GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *
base, GstCaps *
filter)
567 gst_caps_ref(caps.
get());
571gboolean QGstVideoRendererSink::set_caps(GstBaseSink *
base, GstCaps *gcaps)
580 sink->renderer->stop();
583 }
else if (
sink->renderer->start(caps)) {
590gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *
base, GstQuery *
query)
593 return sink->renderer->proposeAllocation(
query);
596gboolean QGstVideoRendererSink::stop(GstBaseSink *
base)
599 sink->renderer->stop();
603gboolean QGstVideoRendererSink::unlock(GstBaseSink *
base)
606 sink->renderer->unlock();
610GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *
base, GstBuffer *
buffer)
616gboolean QGstVideoRendererSink::query(GstBaseSink *
base, GstQuery *
query)
625gboolean QGstVideoRendererSink::event(GstBaseSink *
base, GstEvent *
event)
634#include "moc_qgstvideorenderersink_p.cpp"
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
QByteArray toString() const
void addPixelFormats(const QList< QVideoFrameFormat::PixelFormat > &formats, const char *modifier=nullptr)
static QGstVideoRendererSink * createSink(QGstreamerVideoSink *surface)
static void setSink(QGstreamerVideoSink *surface)
bool query(GstQuery *query)
bool proposeAllocation(GstQuery *query)
void gstEvent(GstEvent *event)
GstFlowReturn render(GstBuffer *buffer)
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
bool start(const QGstCaps &caps)
GstContext * gstGlLocalContext() const
QFunctionPointer eglImageTargetTexture2D() const
Qt::HANDLE eglDisplay() const
void unlock() noexcept
Unlocks this mutex locker.
void relock() noexcept
Relocks an unlocked mutex locker.
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
QThread * thread() const
Returns the thread in which the object lives.
\inmodule QtCore\reentrant
Implementation backend() const
static QThread * currentThread()
The QVideoFrame class represents a frame of video data.
void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer)
Combined button and popup list for selecting options.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static thread_local QGstreamerVideoSink * gvrs_current_sink
static GstVideoSinkClass * gvrs_sink_parent_class
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLbitfield GLuint64 timeout
[4]
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLsizei GLenum GLboolean sink
QFileInfo info(fileName)
[8]