Commit 22465514 authored by Andrew Voznytsa's avatar Andrew Voznytsa

Repackage video sink into GstQgcVideoSinkBin

parent a6d15868
......@@ -282,123 +282,15 @@ VideoManager::setfullScreen(bool f)
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
gboolean
VideoManager::_videoSinkQuery(GstPad* pad, GstObject* parent, GstQuery* query)
{
GstElement* element;
switch (GST_QUERY_TYPE(query)) {
case GST_QUERY_CAPS:
element = gst_bin_get_by_name(GST_BIN(parent), "glupload");
break;
case GST_QUERY_CONTEXT:
element = gst_bin_get_by_name(GST_BIN(parent), "qmlglsink");
break;
default:
return gst_pad_query_default (pad, parent, query);
}
if (element == nullptr) {
qWarning() << "VideoManager::_videoSinkQuery(): No element found";
return FALSE;
}
GstPad* sinkpad = gst_element_get_static_pad(element, "sink");
if (sinkpad == nullptr) {
qWarning() << "VideoManager::_videoSinkQuery(): No sink pad found";
return FALSE;
}
const gboolean ret = gst_pad_query(sinkpad, query);
gst_object_unref(sinkpad);
sinkpad = nullptr;
return ret;
}
GstElement*
VideoManager::_makeVideoSink(gpointer widget)
{
GstElement* glupload = nullptr;
GstElement* glcolorconvert = nullptr;
GstElement* qmlglsink = nullptr;
GstElement* bin = nullptr;
GstElement* sink = nullptr;
do {
if ((glupload = gst_element_factory_make("glupload", "glupload")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('glupload')";
break;
}
if ((glcolorconvert = gst_element_factory_make("glcolorconvert", "glcolorconvert")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('glcolorconvert')";
break;
}
if ((qmlglsink = gst_element_factory_make("qmlglsink", "qmlglsink")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('qmlglsink')";
break;
}
g_object_set(qmlglsink, "widget", widget, NULL);
if ((bin = gst_bin_new("videosink")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_bin_new('videosink')";
break;
}
GstPad* pad;
if ((pad = gst_element_get_static_pad(glupload, "sink")) == nullptr) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_get_static_pad(glupload, 'sink')";
break;
}
gst_bin_add_many(GST_BIN(bin), glupload, glcolorconvert, qmlglsink, nullptr);
gboolean ret = gst_element_link_many(glupload, glcolorconvert, qmlglsink, nullptr);
qmlglsink = glcolorconvert = glupload = nullptr;
if (!ret) {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_link_many()";
break;
}
GstPad* ghostpad = gst_ghost_pad_new("sink", pad);
GstElement* sink;
gst_pad_set_query_function(ghostpad, _videoSinkQuery);
gst_element_add_pad(bin, ghostpad);
gst_object_unref(pad);
pad = nullptr;
sink = bin;
bin = nullptr;
} while(0);
if (bin != nullptr) {
gst_object_unref(bin);
bin = nullptr;
}
if (qmlglsink != nullptr) {
gst_object_unref(qmlglsink);
qmlglsink = nullptr;
}
if (glcolorconvert != nullptr) {
gst_object_unref(glcolorconvert);
glcolorconvert = nullptr;
}
if (glupload != nullptr) {
gst_object_unref(glupload);
glupload = nullptr;
if ((sink = gst_element_factory_make("qgcvideosinkbin", nullptr)) != nullptr) {
g_object_set(sink, "widget", widget, NULL);
} else {
qCritical() << "VideoManager::_makeVideoSink() failed. Error with gst_element_factory_make('qgcvideosinkbin')";
}
return sink;
......
......@@ -67,6 +67,7 @@ static void gst_android_log(GstDebugCategory * category,
#endif
#endif
GST_PLUGIN_STATIC_DECLARE(qmlgl);
GST_PLUGIN_STATIC_DECLARE(qgc);
G_END_DECLS
#endif
......@@ -168,6 +169,8 @@ void initializeVideoStreaming(int &argc, char* argv[], char* logpath, char* debu
} else {
qCritical() << "unable to find qmlglsink - you need to build it yourself and add to GST_PLUGIN_PATH";
}
GST_PLUGIN_STATIC_REGISTER(qgc);
#else
qmlRegisterType<GLVideoItemStub>("org.freedesktop.gstreamer.GLVideoItem", 1, 0, "GstGLVideoItem");
Q_UNUSED(argc)
......
......@@ -127,6 +127,10 @@ VideoEnabled {
$$PWD/iOS
}
SOURCES += \
$$PWD/gstqgcvideosinkbin.c \
$$PWD/gstqgc.c
include($$PWD/../../qmlglsink.pri)
} else {
LinuxBuild|MacBuild|iOSBuild|WindowsBuild|AndroidBuild {
......
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief GStreamer plugin for QGC's Video Receiver
* @author Andrew Voznyts <andrew.voznytsa@gmail.com>
* @author Tomaz Canabrava <tcanabrava@kde.org>
*/
#include <gst/gst.h>
gboolean gst_qgc_video_sink_bin_plugin_init(GstPlugin *plugin);
static gboolean
plugin_init(GstPlugin* plugin)
{
if (!gst_qgc_video_sink_bin_plugin_init(plugin)) {
return FALSE;
}
return TRUE;
}
#define PACKAGE "QGC Video Receiver"
#define PACKAGE_VERSION "current"
#define GST_LICENSE "LGPL"
#define GST_PACKAGE_NAME "GStreamer plugin for QGC's Video Receiver"
#define GST_PACKAGE_ORIGIN "http://qgroundcontrol.com/"
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
qgc, "QGC Video Receiver plugin",
plugin_init, PACKAGE_VERSION,
GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief GStreamer plugin for QGC's Video Receiver
* @author Andrew Voznyts <andrew.voznytsa@gmail.com>
* @author Tomaz Canabrava <tcanabrava@kde.org>
*/
#include <glib-object.h>
#include <gst/gst.h>
GST_DEBUG_CATEGORY_STATIC(gst_qgc_video_sink_bin_debug);
#define GST_CAT_DEFAULT gst_qgc_video_sink_bin_debug
typedef struct _GstQgcVideoSinkElement GstQgcVideoSinkElement;
typedef struct _GstQgcVideoSinkBin {
GstBin bin;
GstElement* glupload;
GstElement* qmlglsink;
} GstQgcVideoSinkBin;
typedef struct _GstQgcVideoSinkBinClass {
GstBinClass parent_class;
} GstQgcVideoSinkBinClass;
#define GST_TYPE_VIDEO_SINK_BIN (_vsb_get_type())
#define GST_QGC_VIDEO_SINK_BIN_CAST(obj) ((GstQgcVideoSinkBin *)(obj))
#define GST_QGC_VIDEO_SINK_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_VIDEO_SINK_BIN, GstQgcVideoSinkBin))
#define GST_QGC_VIDEO_SINK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VIDEO_SINK_BIN, GstQgcVideoSinkBinClass))
#define GST_IS_VIDEO_SINK_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VIDEO_SINK_BIN))
#define GST_IS_VIDEO_SINK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VIDEO_SINK_BIN))
enum {
PROP_0,
PROP_ENABLE_LAST_SAMPLE,
PROP_LAST_SAMPLE,
PROP_WIDGET,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
};
#define PROP_ENABLE_LAST_SAMPLE_NAME "enable-last-sample"
#define PROP_LAST_SAMPLE_NAME "last-sample"
#define PROP_WIDGET_NAME "widget"
#define PROP_FORCE_ASPECT_RATIO_NAME "force-aspect-ratio"
#define PROP_PIXEL_ASPECT_RATIO_NAME "pixel-aspect-ratio"
#define DEFAULT_ENABLE_LAST_SAMPLE TRUE
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_PAR_N 0
#define DEFAULT_PAR_D 1
static GstBinClass *parent_class;
static void _vsb_init(GstQgcVideoSinkBin *vsb);
static void _vsb_dispose(GObject *object);
static void _vsb_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void _vsb_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
static GType _vsb_get_type(void);
static void _vsb_class_init(GstQgcVideoSinkBinClass *klass);
static gboolean
_vsb_sink_pad_query(GstPad* pad, GstObject* parent, GstQuery* query)
{
GstQgcVideoSinkBin *vsb;
GstElement* element;
vsb = GST_QGC_VIDEO_SINK_BIN(parent);
switch (GST_QUERY_TYPE(query)) {
case GST_QUERY_CAPS:
element = vsb->glupload;
break;
case GST_QUERY_CONTEXT:
element = vsb->qmlglsink;
break;
default:
return gst_pad_query_default (pad, parent, query);
}
if (element == NULL) {
GST_ERROR_OBJECT(vsb, "No element found");
return FALSE;
}
GstPad* sinkpad = gst_element_get_static_pad(element, "sink");
if (sinkpad == NULL) {
GST_ERROR_OBJECT(vsb, "No sink pad found");
return FALSE;
}
const gboolean ret = gst_pad_query(sinkpad, query);
gst_object_unref(sinkpad);
sinkpad = NULL;
return ret;
}
static void
_vsb_init(GstQgcVideoSinkBin *vsb)
{
gboolean initialized = FALSE;
GstElement* glcolorconvert = NULL;
GstPad* pad = NULL;
do {
if ((vsb->glupload = gst_element_factory_make("glupload", NULL)) == NULL) {
GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glupload') failed");
break;
}
if ((vsb->qmlglsink = gst_element_factory_make("qmlglsink", NULL)) == NULL) {
GST_ERROR_OBJECT(vsb, "gst_element_factory_make('qmlglsink') failed");
break;
}
if ((glcolorconvert = gst_element_factory_make("glcolorconvert", NULL)) == NULL) {
GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glcolorconvert' failed)");
break;
}
if ((pad = gst_element_get_static_pad(vsb->glupload, "sink")) == NULL) {
GST_ERROR_OBJECT(vsb, "gst_element_get_static_pad(glupload, 'sink') failed");
break;
}
gst_object_ref(vsb->glupload);
gst_object_ref(vsb->qmlglsink);
gst_bin_add_many(GST_BIN(vsb), vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL);
gboolean ret = gst_element_link_many(vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL);
glcolorconvert = NULL;
if (!ret) {
GST_ERROR_OBJECT(vsb, "gst_element_link_many() failed");
break;
}
GstPad* ghostpad;
if ((ghostpad = gst_ghost_pad_new("sink", pad)) == NULL) {
GST_ERROR_OBJECT(vsb, "gst_ghost_pad_new('sink') failed");
break;
}
gst_pad_set_query_function(ghostpad, _vsb_sink_pad_query);
if (!gst_element_add_pad(GST_ELEMENT(vsb), ghostpad)) {
GST_ERROR_OBJECT(vsb, "gst_element_add_pad() failed");
break;
}
initialized = TRUE;
} while(0);
if (pad != NULL) {
gst_object_unref(pad);
pad = NULL;
}
if (glcolorconvert != NULL) {
gst_object_unref(glcolorconvert);
glcolorconvert = NULL;
}
if (!initialized) {
if (vsb->qmlglsink != NULL) {
gst_object_unref(vsb->qmlglsink);
vsb->qmlglsink = NULL;
}
if (vsb->glupload != NULL) {
gst_object_unref(vsb->glupload);
vsb->glupload = NULL;
}
}
}
static void
_vsb_dispose(GObject *object)
{
GstQgcVideoSinkBin *vsb;
vsb = GST_QGC_VIDEO_SINK_BIN(object);
if (vsb->qmlglsink != NULL) {
gst_object_unref(vsb->qmlglsink);
vsb->qmlglsink = NULL;
}
if (vsb->glupload != NULL) {
gst_object_unref(vsb->glupload);
vsb->glupload = NULL;
}
G_OBJECT_CLASS(parent_class)->dispose(object);
}
static void
_vsb_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
GstQgcVideoSinkBin *vsb;
vsb = GST_QGC_VIDEO_SINK_BIN(object);
switch (prop_id) {
case PROP_ENABLE_LAST_SAMPLE:
do {
gboolean enable = FALSE;
g_object_get(G_OBJECT(vsb->qmlglsink), PROP_ENABLE_LAST_SAMPLE_NAME, &enable, NULL);
g_value_set_boolean(value, enable);
} while(0);
break;
case PROP_LAST_SAMPLE:
do {
GstSample *sample = NULL;
g_object_get(G_OBJECT(vsb->qmlglsink), PROP_LAST_SAMPLE_NAME, &sample, NULL);
gst_value_set_sample(value, sample);
if (sample != NULL) {
gst_sample_unref(sample);
sample = NULL;
}
} while(0);
break;
case PROP_WIDGET:
do {
gpointer widget = NULL;
g_object_get(G_OBJECT(vsb->qmlglsink), PROP_WIDGET_NAME, &widget, NULL);
g_value_set_pointer(value, widget);
} while(0);
break;
case PROP_FORCE_ASPECT_RATIO:
do {
gboolean enable = FALSE;
g_object_get(G_OBJECT(vsb->qmlglsink), PROP_FORCE_ASPECT_RATIO_NAME, &enable, NULL);
g_value_set_boolean(value, enable);
} while(0);
break;
case PROP_PIXEL_ASPECT_RATIO:
do {
gint num = 0, den = 1;
g_object_get(G_OBJECT(vsb->qmlglsink), PROP_PIXEL_ASPECT_RATIO_NAME, &num, &den, NULL);
gst_value_set_fraction(value, num, den);
} while(0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
_vsb_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
GstQgcVideoSinkBin *vsb;
vsb = GST_QGC_VIDEO_SINK_BIN(object);
switch (prop_id) {
case PROP_ENABLE_LAST_SAMPLE:
g_object_set(G_OBJECT(vsb->qmlglsink), PROP_ENABLE_LAST_SAMPLE_NAME, g_value_get_boolean(value), NULL);
break;
case PROP_WIDGET:
g_object_set(G_OBJECT(vsb->qmlglsink), PROP_WIDGET_NAME, g_value_get_pointer(value), NULL);
break;
case PROP_FORCE_ASPECT_RATIO:
g_object_set(G_OBJECT(vsb->qmlglsink), PROP_FORCE_ASPECT_RATIO_NAME, g_value_get_boolean(value), NULL);
break;
case PROP_PIXEL_ASPECT_RATIO:
g_object_set(G_OBJECT(vsb->qmlglsink), PROP_PIXEL_ASPECT_RATIO_NAME, gst_value_get_fraction_numerator(value), gst_value_get_fraction_denominator(value), NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static GType
_vsb_get_type(void)
{
static GType _vsb_type = 0;
if (!_vsb_type) {
static const GTypeInfo _vsb_info = {
sizeof(GstQgcVideoSinkBinClass),
NULL,
NULL,
(GClassInitFunc)_vsb_class_init,
NULL,
NULL,
sizeof(GstQgcVideoSinkBin),
0,
(GInstanceInitFunc)_vsb_init,
NULL};
_vsb_type = g_type_register_static(GST_TYPE_BIN, "GstQgcVideoSinkBin", &_vsb_info, (GTypeFlags)0);
}
return _vsb_type;
}
static void
_vsb_class_init(GstQgcVideoSinkBinClass *klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
gobject_klass = (GObjectClass *)klass;
gstelement_klass = (GstElementClass *)klass;
parent_class = g_type_class_peek_parent(klass);
gobject_klass->dispose = _vsb_dispose;
gobject_klass->get_property = _vsb_get_property;
gobject_klass->set_property = _vsb_set_property;
g_object_class_install_property(gobject_klass, PROP_ENABLE_LAST_SAMPLE,
g_param_spec_boolean(PROP_ENABLE_LAST_SAMPLE_NAME, "Enable Last Buffer",
"Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_klass, PROP_LAST_SAMPLE,
g_param_spec_boxed(PROP_LAST_SAMPLE_NAME, "Last Sample",
"The last sample received in the sink", GST_TYPE_SAMPLE,
(GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_klass, PROP_WIDGET,
g_param_spec_pointer(PROP_WIDGET_NAME, "QQuickItem",
"The QQuickItem to place in the object hierarchy",
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_klass, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean(PROP_FORCE_ASPECT_RATIO_NAME, "Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
DEFAULT_FORCE_ASPECT_RATIO,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_klass, PROP_PIXEL_ASPECT_RATIO,
gst_param_spec_fraction(PROP_PIXEL_ASPECT_RATIO_NAME, "Pixel Aspect Ratio",
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
G_MAXINT, 1, 1, 1,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
gst_element_class_set_static_metadata(gstelement_klass,
"QGC Video Sink Bin", "Sink/Video/Bin",
"Video rendering for QGC",
"Andrew Voznytsa <andrew.voznytsa@gmail.com>, Tomaz Canabrava <tcanabrava@kde.org>");
}
gboolean
gst_qgc_video_sink_bin_plugin_init(GstPlugin *plugin)
{
GST_DEBUG_CATEGORY_INIT(gst_qgc_video_sink_bin_debug, "qgcvideosinkbin", 0, "QGC Video Sink Bin");
return gst_element_register(plugin, "qgcvideosinkbin", GST_RANK_NONE, GST_TYPE_VIDEO_SINK_BIN);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment