6#include <QtMultimedia/qvideoframe.h>
7#include <QtMultimedia/qvideosink.h>
8#include <QtCore/private/qfactoryloader_p.h>
9#include <QtCore/private/quniquehandle_p.h>
10#include <QtCore/qcoreapplication.h>
11#include <QtCore/qdebug.h>
12#include <QtCore/qdebug.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qmap.h>
15#include <QtCore/qthread.h>
16#include <QtGui/qevent.h>
23#include <gst/video/video.h>
24#include <gst/video/gstvideometa.h>
28#if QT_CONFIG(gstreamer_gl)
33#if QT_CONFIG(linux_dmabuf)
34#include <gst/allocators/gstdmabuf.h>
42 : m_sink(
sink), m_surfaceCaps(createSurfaceCaps(
sink))
60 auto formats = QList<QVideoFrameFormat::PixelFormat>()
81#if QT_CONFIG(gstreamer_gl)
85#if QT_CONFIG(linux_dmabuf)
86 if (
sink->eglDisplay() &&
sink->eglImageTargetTexture2D()) {
89 auto singlePlaneFormats = QList<QVideoFrameFormat::PixelFormat>()
115 return m_surfaceCaps;
120 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::start" <<
caps;
125 if (optionalFormatAndVideoInfo) {
126 std::tie(m_format, m_videoInfo) = std::move(*optionalFormatAndVideoInfo);
139 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::stop";
149 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::unlock";
154 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::proposeAllocation";
160 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::render";
162 GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(
buffer);
164 QRect vp(meta->x, meta->y, meta->width, meta->height);
167 <<
Q_FUNC_INFO <<
" Update viewport on Metadata: [" << meta->height <<
"x"
168 << meta->width <<
" | " << meta->x <<
"x" << meta->y <<
"]";
174 struct RenderBufferState
183 RenderBufferState
state{
186 .memoryFormat = m_memoryFormat,
187 .mirrored = m_frameMirrored,
188 .rotationAngle = m_frameRotationAngle,
191 qCDebug(qLcGstVideoRenderer) <<
" sending video frame";
202 m_currentVideoFrame = std::move(
frame);
207 qCDebug(qLcGstVideoRenderer) <<
" showing empty video frame";
208 m_currentVideoFrame = {};
219#if QT_CONFIG(gstreamer_gl)
220 if (GST_QUERY_TYPE(
query) == GST_QUERY_CONTEXT) {
222 gst_query_parse_context_type(
query, &
type);
224 if (strcmp(
type,
"gst.gl.local_context") != 0)
235 gst_query_set_context(
query, gstGlContext);
247 switch (GST_EVENT_TYPE(
event)) {
249 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::gstEvent: Tag";
250 return gstEventHandleTag(
event);
252 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::gstEvent: EOS";
253 return gstEventHandleEOS(
event);
256 qCDebug(qLcGstVideoRenderer) <<
"QGstVideoRenderer::gstEvent: unhandled event - " <<
event;
261void QGstVideoRenderer::gstEventHandleTag(GstEvent *
event)
263 GstTagList *taglist =
nullptr;
264 gst_event_parse_tag(
event, &taglist);
269 if (!gst_tag_list_get_string(taglist, GST_TAG_IMAGE_ORIENTATION, &
value))
272 constexpr const char rotate[] =
"rotate-";
273 constexpr const char flipRotate[] =
"flip-rotate-";
274 constexpr size_t rotateLen =
sizeof(rotate) - 1;
275 constexpr size_t flipRotateLen =
sizeof(flipRotate) - 1;
277 bool mirrored =
false;
278 int rotationAngle = 0;
280 if (!strncmp(rotate,
value.get(), rotateLen)) {
281 rotationAngle = atoi(
value.get() + rotateLen);
282 }
else if (!strncmp(flipRotate,
value.get(), flipRotateLen)) {
286 rotationAngle = (180 + atoi(
value.get() + flipRotateLen)) % 360;
289 m_frameMirrored = mirrored;
290 switch (rotationAngle) {
308void QGstVideoRenderer::gstEventHandleEOS(GstEvent *)
316#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast<QGstVideoRendererSink *>(s))
322 g_object_new(QGstVideoRendererSink::get_type(),
nullptr));
332GType QGstVideoRendererSink::get_type()
334 static const GTypeInfo
info =
348 static const GType
type = g_type_register_static(GST_TYPE_VIDEO_SINK,
"QGstVideoRendererSink",
349 &
info, GTypeFlags(0));
354void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data)
360 GstVideoSinkClass *video_sink_class =
reinterpret_cast<GstVideoSinkClass *
>(g_class);
361 video_sink_class->show_frame = QGstVideoRendererSink::show_frame;
363 GstBaseSinkClass *base_sink_class =
reinterpret_cast<GstBaseSinkClass *
>(g_class);
364 base_sink_class->get_caps = QGstVideoRendererSink::get_caps;
365 base_sink_class->set_caps = QGstVideoRendererSink::set_caps;
366 base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation;
367 base_sink_class->stop = QGstVideoRendererSink::stop;
368 base_sink_class->unlock = QGstVideoRendererSink::unlock;
369 base_sink_class->query = QGstVideoRendererSink::query;
370 base_sink_class->event = QGstVideoRendererSink::event;
372 GstElementClass *element_class =
reinterpret_cast<GstElementClass *
>(g_class);
373 element_class->change_state = QGstVideoRendererSink::change_state;
374 gst_element_class_set_metadata(element_class,
375 "Qt built-in video renderer sink",
377 "Qt default built-in video renderer sink",
380 GObjectClass *object_class =
reinterpret_cast<GObjectClass *
>(g_class);
381 object_class->finalize = QGstVideoRendererSink::finalize;
384void QGstVideoRendererSink::base_init(gpointer g_class)
386 static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
387 "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(
389 "framerate = (fraction) [ 0, MAX ], "
390 "width = (int) [ 1, MAX ], "
391 "height = (int) [ 1, MAX ]"));
393 gst_element_class_add_pad_template(
394 GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
397void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class)
409void QGstVideoRendererSink::finalize(GObject *
object)
413 delete sink->renderer;
419GstStateChangeReturn QGstVideoRendererSink::change_state(
420 GstElement *element, GstStateChange transition)
425GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *
base, GstCaps *
filter)
436gboolean QGstVideoRendererSink::set_caps(GstBaseSink *
base, GstCaps *gcaps)
441 qCDebug(qLcGstVideoRenderer) <<
"set_caps:" << caps;
444 sink->renderer->stop();
448 return sink->renderer->start(caps);
451gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *
base, GstQuery *
query)
454 return sink->renderer->proposeAllocation(
query);
457gboolean QGstVideoRendererSink::stop(GstBaseSink *
base)
460 sink->renderer->stop();
464gboolean QGstVideoRendererSink::unlock(GstBaseSink *
base)
467 sink->renderer->unlock();
471GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *
base, GstBuffer *
buffer)
477gboolean QGstVideoRendererSink::query(GstBaseSink *
base, GstQuery *
query)
486gboolean QGstVideoRendererSink::event(GstBaseSink *
base, GstEvent *
event)
void addPixelFormats(const QList< QVideoFrameFormat::PixelFormat > &formats, const char *modifier=nullptr)
MemoryFormat memoryFormat() const
std::optional< std::pair< QVideoFrameFormat, GstVideoInfo > > formatAndVideoInfo() const
static QGstVideoRendererSink * createSink(QGstreamerVideoSink *surface)
static void setSink(QGstreamerVideoSink *surface)
GstFlowReturn render(GstBuffer *)
bool start(const QGstCaps &)
void gstEvent(GstEvent *)
bool proposeAllocation(GstQuery *)
bool inStoppedState() const
GstContext * gstGlLocalContext() const
void aboutToBeDestroyed()
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
\inmodule QtCore\reentrant
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Implementation backend() const
The QVideoFrame class represents a frame of video data.
void setFrameTimeStampsFromBuffer(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,...)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLint GLsizei GLsizei GLenum format
GLsizei GLenum GLboolean sink