diff --git a/clients/gstwestonimagesrc.c b/clients/gstwestonimagesrc.c index e0b6b29..61d8656 100644 --- a/clients/gstwestonimagesrc.c +++ b/clients/gstwestonimagesrc.c @@ -8,17 +8,37 @@ #include #include #include +#include +#include +#include "glib-object.h" +#include "glib.h" +#include "gst/gstbuffer.h" #include "gst/gstinfo.h" +#include "gst/gstparamspecs.h" +#include "gst/gstvalue.h" +#include "gst/video/video-format.h" #include "gstwestonimagesrc.h" // Gtreamer plugin related defines and declarations GST_DEBUG_CATEGORY_STATIC(gst_westonimagesrc_debug); #define GST_CAT_DEFAULT gst_westonimagesrc_debug -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS("text/plain; charset=utf-8")); +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-raw, " + "format = (string) { BGRA, bgra }, " + "width = (int) [ 1, 1280 ], " + "height = (int) [ 1, 800 ], " + "framerate = (fraction) [ 1/1, 30/1 ]")); -enum { PROP_0, PROP_FRAMERATE, N_PROPERTIES }; +enum { + PROP_0, + N_PROPERTIES, +}; + +static GParamSpec *properties[N_PROPERTIES] = { + NULL, +}; static gboolean gst_westonimagesrc_start(GstBaseSrc *basesrc); static gboolean gst_westonimagesrc_stop(GstBaseSrc *basesrc); @@ -30,6 +50,8 @@ static void gst_westonimagesrc_class_init(GstWestonImageSrcClass *klass); static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc); static void gst_westonimagesrc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gst_westonimagesrc_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static GstCaps *gst_westonimagesrc_get_caps(GstBaseSrc *src, GstCaps *filter); +static gboolean gst_westonimagesrc_negotiate(GstBaseSrc *basesrc); G_DEFINE_TYPE(GstWestonImageSrc, gst_westonimagesrc, GST_TYPE_BASE_SRC); @@ -43,6 +65,7 @@ static void weston_handle_global_remove(void *data, struct wl_registry *registry static void weston_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { /* XXX: unimplemented */ } static struct wl_buffer *weston_screenshot_create_shm_buffer(int width, int height, void **data_out, struct wl_shm *shm); static int weston_screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list); +static void weston_capture_frame(); static const struct wl_output_listener output_listener = {weston_display_handle_geometry, weston_display_handle_mode}; static const struct weston_screenshooter_listener screenshooter_listener = {weston_screenshot_done}; @@ -163,17 +186,15 @@ static void gst_westonimagesrc_class_init(GstWestonImageSrcClass *klass) { gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_westonimagesrc_start); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_stop); gstbasesrc_class->create = GST_DEBUG_FUNCPTR(gst_westonimagesrc_create); + gstbasesrc_class->get_caps = NULL; + gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR(gst_westonimagesrc_negotiate); gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR(gst_westonimagesrc_unlock); gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_unlock_stop); - // Регистрация свойств - g_object_class_install_property( - gobject_class, PROP_FRAMERATE, - g_param_spec_double("framerate", "Frame rate", "Number of hello world messages per second", 0.f, 24.f, 1.f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gst_element_class_set_static_metadata(gstelement_class, "WestonImageSrc World Source", "Source", "Generates westonimagesrc world messages", "Your Name "); + gst_element_class_set_static_metadata(gstelement_class, "Weston Image Source", "Source/Video", "Generates XRGB screenshots", + "OlegShishlyannikov oleg.shishlyannikov@piklema.com"); gst_element_class_add_static_pad_template(gstelement_class, &src_template); - GST_DEBUG_CATEGORY_INIT(gst_westonimagesrc_debug, "westonimagesrc", 0, "WestonImageSrc World Plugin"); + GST_DEBUG_CATEGORY_INIT(gst_westonimagesrc_debug, "westonimagesrc", 0, "Weston Image Source Plugin"); } static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) { @@ -184,7 +205,8 @@ static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) { westonimagesrc->running = FALSE; westonimagesrc->timeout_id = 0; westonimagesrc->offset = 0; - westonimagesrc->framerate = 1.f; + westonimagesrc->framerate_n = 30; + westonimagesrc->framerate_d = 1; g_mutex_init(&westonimagesrc->lock); g_cond_init(&westonimagesrc->cond); @@ -207,27 +229,53 @@ static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) { weston_screenshooter_add_listener(sh_data.screenshooter, &screenshooter_listener, &sh_data); weston_screenshot_set_buffer_size(&buff_size, &sh_data.output_list); + + wl_list_for_each(output, &sh_data.output_list, link) { + GST_INFO_OBJECT(westonimagesrc, "Creating shared mem buffer ..."); + output->buffer = weston_screenshot_create_shm_buffer(output->width, output->height, &output->data, sh_data.shm); + } +} + +static GstCaps *gst_westonimagesrc_get_caps(GstBaseSrc *src, GstCaps *filter) { + GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(src); + + GstCaps *caps = gst_caps_new_simple("video/x-raw", "format", GST_TYPE_VIDEO_FORMAT, westonimagesrc->format, "width", G_TYPE_INT, westonimagesrc->width, "height", G_TYPE_INT, + westonimagesrc->height, "framerate", GST_TYPE_FRACTION, westonimagesrc->framerate_n, westonimagesrc->framerate_d, NULL); + + if (filter) { + GstCaps *tmp = gst_caps_intersect_full(filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref(caps); + return tmp; + } + + return caps; +} + +static gboolean gst_westonimagesrc_negotiate(GstBaseSrc *basesrc) { + GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(basesrc); + GstCaps *caps; + GstStructure *s; + + caps = gst_pad_get_current_caps(GST_BASE_SRC_PAD(basesrc)); + s = gst_caps_get_structure(caps, 0); + + gst_structure_get_int(s, "width", &westonimagesrc->width); + gst_structure_get_int(s, "height", &westonimagesrc->height); + gst_structure_get_fraction(s, "framerate", &westonimagesrc->framerate_n, &westonimagesrc->framerate_d); + + gst_caps_unref(caps); + return TRUE; } static void gst_westonimagesrc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(object); - GST_OBJECT_LOCK(westonimagesrc); switch (prop_id) { - case PROP_FRAMERATE: { - gdouble new_framerate = g_value_get_double(value); - - if (new_framerate != westonimagesrc->framerate) { - westonimagesrc->framerate = new_framerate; - g_object_notify(object, "framerate"); - } - break; - } - - default: + default: { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; + + } break; } GST_OBJECT_UNLOCK(westonimagesrc); @@ -238,9 +286,6 @@ static void gst_westonimagesrc_get_property(GObject *object, guint prop_id, GVal GST_OBJECT_LOCK(westonimagesrc); switch (prop_id) { - case PROP_FRAMERATE: - g_value_set_double(value, westonimagesrc->framerate); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -273,66 +318,65 @@ static gboolean gst_westonimagesrc_stop(GstBaseSrc *basesrc) { return TRUE; } +static void weston_capture_frame() { + wl_list_for_each(output, &sh_data.output_list, link) { + weston_screenshooter_shoot(sh_data.screenshooter, output->output, output->buffer); + sh_data.buffer_copy_done = 0; + + while (!sh_data.buffer_copy_done) + wl_display_roundtrip(display); + } +} + static GstFlowReturn gst_westonimagesrc_create(GstBaseSrc *basesrc, guint64 offset, guint size, GstBuffer **buf) { - // Gstreamer related - GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(basesrc); - GstMapInfo map; - gchar *message; - gint message_len; - GstClockTime timestamp; + GstWestonImageSrc *src = GST_WESTONIMAGESRC(basesrc); GstBuffer *buffer; + GstMapInfo map; + GstClockTime dur; - g_mutex_lock(&westonimagesrc->lock); + g_mutex_lock(&src->lock); + if (!src->running) { + g_mutex_unlock(&src->lock); + return GST_FLOW_FLUSHING; + } + g_mutex_unlock(&src->lock); - while (westonimagesrc->running) { - GstClockTime wait_until = g_get_monotonic_time() + (1 / westonimagesrc->framerate) * G_TIME_SPAN_SECOND; + // Захват кадра + wl_list_for_each(output, &sh_data.output_list, link) { + weston_screenshooter_shoot(sh_data.screenshooter, output->output, output->buffer); + sh_data.buffer_copy_done = 0; - if (g_cond_wait_until(&westonimagesrc->cond, &westonimagesrc->lock, wait_until)) { - if (!westonimagesrc->running) { - g_mutex_unlock(&westonimagesrc->lock); - return GST_FLOW_FLUSHING; - } - } - - if (!westonimagesrc->running) { - g_mutex_unlock(&westonimagesrc->lock); - return GST_FLOW_FLUSHING; - } - - wl_list_for_each(output, &sh_data.output_list, link) { - output->buffer = weston_screenshot_create_shm_buffer(output->width, output->height, &output->data, sh_data.shm); - weston_screenshooter_shoot(sh_data.screenshooter, output->output, output->buffer); - sh_data.buffer_copy_done = 0; - while (!sh_data.buffer_copy_done) - wl_display_roundtrip(display); - } - - message = g_strdup_printf("westonimagesrc world #%lu\n", (gulong)westonimagesrc->offset); - message_len = strlen(message); - - buffer = gst_buffer_new_allocate(NULL, message_len, NULL); - gst_buffer_map(buffer, &map, GST_MAP_WRITE); - memcpy(map.data, message, message_len); - gst_buffer_unmap(buffer, &map); - g_free(message); - - timestamp = westonimagesrc->offset * GST_SECOND; - GST_BUFFER_PTS(buffer) = timestamp; - GST_BUFFER_DURATION(buffer) = GST_SECOND; - GST_BUFFER_OFFSET(buffer) = westonimagesrc->offset; - GST_BUFFER_OFFSET_END(buffer) = westonimagesrc->offset + 1; - - westonimagesrc->offset++; - - *buf = buffer; - g_mutex_unlock(&westonimagesrc->lock); - - GST_DEBUG_OBJECT(westonimagesrc, "created buffer %" G_GUINT64_FORMAT, westonimagesrc->offset - 1); - return GST_FLOW_OK; + while (!sh_data.buffer_copy_done) + wl_display_roundtrip(display); } - g_mutex_unlock(&westonimagesrc->lock); - return GST_FLOW_FLUSHING; + // Размер кадра + size_t frame_size = buff_size.width * buff_size.height * 4; // XRGB + /* size_t frame_size = buff_size.width * buff_size.height * 3 / 2; */ + buffer = gst_buffer_new_allocate(NULL, frame_size, NULL); + if (!buffer) + return GST_FLOW_ERROR; + + if (!gst_buffer_map(buffer, &map, GST_MAP_WRITE)) { + gst_buffer_unref(buffer); + return GST_FLOW_ERROR; + } + + struct screenshooter_output *output = wl_container_of(sh_data.output_list.next, output, link); + memcpy(map.data, output->data, frame_size); + gst_buffer_unmap(buffer, &map); + + // Добавляем мета + gst_buffer_add_video_meta(buffer, GST_VIDEO_FRAME_FLAG_NONE, GST_VIDEO_FORMAT_BGRA, buff_size.width, buff_size.height); + + // PTS и Duration + dur = gst_util_uint64_scale(GST_SECOND, src->framerate_d, src->framerate_n); + GST_BUFFER_PTS(buffer) = src->offset * dur; + GST_BUFFER_DURATION(buffer) = dur; + src->offset++; + + *buf = buffer; + return GST_FLOW_OK; } static gboolean gst_westonimagesrc_unlock(GstBaseSrc *basesrc) { diff --git a/clients/gstwestonimagesrc.h b/clients/gstwestonimagesrc.h index ec5f4f6..0c5943c 100644 --- a/clients/gstwestonimagesrc.h +++ b/clients/gstwestonimagesrc.h @@ -3,7 +3,7 @@ #include #include - +#include #include #include @@ -37,12 +37,32 @@ typedef struct _GstWestonImageSrcClass GstWestonImageSrcClass; struct _GstWestonImageSrc { GstBaseSrc element; + + // Video + GstVideoInfo video_info; + gint width; + gint height; + GstVideoFormat format; + + // Properties + gint framerate_n, framerate_d; + GstClockTime duration; + + // Image data + gpointer image_data; + gsize image_size; + + // Sync GMutex lock; GCond cond; gboolean running; guint timeout_id; - guint64 offset; // Счетчик отправленных сообщений - gdouble framerate; + guint64 offset; + guint64 frame_number; + gboolean do_timestamp; + + // Stat + GstClockTime last_timestamp; }; struct _GstWestonImageSrcClass { diff --git a/clients/meson.build b/clients/meson.build index 19c1bea..9bda738 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -363,8 +363,11 @@ if get_option('shell-desktop') fallback : ['gstreamer', 'gst_dep']) gstbase_dep = dependency('gstreamer-base-1.0', version : '>=1.18', - fallback : ['gstreamer', 'gst_base_dep']) - + fallback : ['gstreamer', 'gstbase_dep']) + + gstvideo_dep = dependency('gstreamer-video-1.0', version : '>=1.18', + fallback : ['gstreamer', 'gstvideo_dep']) + gst_westonimagesrc_plugin_c_args = ['-DHAVE_CONFIG_H'] gst_westonimagesrc_cdata = configuration_data() @@ -387,7 +390,7 @@ if get_option('shell-desktop') weston_screenshooter_protocol_c, include_directories: common_inc, c_args: gst_westonimagesrc_plugin_c_args, - dependencies : [gst_dep, gstbase_dep, dep_toytoolkit], + dependencies : [gst_dep, gstbase_dep, gstvideo_dep, dep_toytoolkit], install : true, install_dir : plugins_install_dir, ) diff --git a/clients/screenshot.c b/clients/screenshot.c index 46ecc5a..9712ed8 100644 --- a/clients/screenshot.c +++ b/clients/screenshot.c @@ -70,7 +70,6 @@ struct screenshooter_data { int buffer_copy_done; }; - static void display_handle_geometry(void *data, struct wl_output *wl_output,