This commit is contained in:
Oleg Shishlyannikov 2025-12-15 15:52:03 +03:00
parent 36e702bfbb
commit 88a35b2316
4 changed files with 152 additions and 86 deletions

View File

@ -8,17 +8,37 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/param.h> #include <sys/param.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-client-protocol.h>
#include <wayland-util.h>
#include "glib-object.h"
#include "glib.h"
#include "gst/gstbuffer.h"
#include "gst/gstinfo.h" #include "gst/gstinfo.h"
#include "gst/gstparamspecs.h"
#include "gst/gstvalue.h"
#include "gst/video/video-format.h"
#include "gstwestonimagesrc.h" #include "gstwestonimagesrc.h"
// Gtreamer plugin related defines and declarations // Gtreamer plugin related defines and declarations
GST_DEBUG_CATEGORY_STATIC(gst_westonimagesrc_debug); GST_DEBUG_CATEGORY_STATIC(gst_westonimagesrc_debug);
#define GST_CAT_DEFAULT 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_start(GstBaseSrc *basesrc);
static gboolean gst_westonimagesrc_stop(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_init(GstWestonImageSrc *westonimagesrc);
static void gst_westonimagesrc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); 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 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); 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 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 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 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 wl_output_listener output_listener = {weston_display_handle_geometry, weston_display_handle_mode};
static const struct weston_screenshooter_listener screenshooter_listener = {weston_screenshot_done}; 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->start = GST_DEBUG_FUNCPTR(gst_westonimagesrc_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_stop); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_stop);
gstbasesrc_class->create = GST_DEBUG_FUNCPTR(gst_westonimagesrc_create); 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 = GST_DEBUG_FUNCPTR(gst_westonimagesrc_unlock);
gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_unlock_stop); gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR(gst_westonimagesrc_unlock_stop);
// Регистрация свойств gst_element_class_set_static_metadata(gstelement_class, "Weston Image Source", "Source/Video", "Generates XRGB screenshots",
g_object_class_install_property( "OlegShishlyannikov oleg.shishlyannikov@piklema.com");
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 <your.email@example.com>");
gst_element_class_add_static_pad_template(gstelement_class, &src_template); 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) { static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) {
@ -184,7 +205,8 @@ static void gst_westonimagesrc_init(GstWestonImageSrc *westonimagesrc) {
westonimagesrc->running = FALSE; westonimagesrc->running = FALSE;
westonimagesrc->timeout_id = 0; westonimagesrc->timeout_id = 0;
westonimagesrc->offset = 0; westonimagesrc->offset = 0;
westonimagesrc->framerate = 1.f; westonimagesrc->framerate_n = 30;
westonimagesrc->framerate_d = 1;
g_mutex_init(&westonimagesrc->lock); g_mutex_init(&westonimagesrc->lock);
g_cond_init(&westonimagesrc->cond); 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_screenshooter_add_listener(sh_data.screenshooter, &screenshooter_listener, &sh_data);
weston_screenshot_set_buffer_size(&buff_size, &sh_data.output_list); 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) { static void gst_westonimagesrc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(object); GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(object);
GST_OBJECT_LOCK(westonimagesrc); GST_OBJECT_LOCK(westonimagesrc);
switch (prop_id) { switch (prop_id) {
case PROP_FRAMERATE: { default: {
gdouble new_framerate = g_value_get_double(value);
if (new_framerate != westonimagesrc->framerate) {
westonimagesrc->framerate = new_framerate;
g_object_notify(object, "framerate");
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
} break;
} }
GST_OBJECT_UNLOCK(westonimagesrc); GST_OBJECT_UNLOCK(westonimagesrc);
@ -238,9 +286,6 @@ static void gst_westonimagesrc_get_property(GObject *object, guint prop_id, GVal
GST_OBJECT_LOCK(westonimagesrc); GST_OBJECT_LOCK(westonimagesrc);
switch (prop_id) { switch (prop_id) {
case PROP_FRAMERATE:
g_value_set_double(value, westonimagesrc->framerate);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break; break;
@ -273,66 +318,65 @@ static gboolean gst_westonimagesrc_stop(GstBaseSrc *basesrc) {
return TRUE; return TRUE;
} }
static GstFlowReturn gst_westonimagesrc_create(GstBaseSrc *basesrc, guint64 offset, guint size, GstBuffer **buf) { static void weston_capture_frame() {
// Gstreamer related
GstWestonImageSrc *westonimagesrc = GST_WESTONIMAGESRC(basesrc);
GstMapInfo map;
gchar *message;
gint message_len;
GstClockTime timestamp;
GstBuffer *buffer;
g_mutex_lock(&westonimagesrc->lock);
while (westonimagesrc->running) {
GstClockTime wait_until = g_get_monotonic_time() + (1 / westonimagesrc->framerate) * G_TIME_SPAN_SECOND;
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) { 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); weston_screenshooter_shoot(sh_data.screenshooter, output->output, output->buffer);
sh_data.buffer_copy_done = 0; 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) {
GstWestonImageSrc *src = GST_WESTONIMAGESRC(basesrc);
GstBuffer *buffer;
GstMapInfo map;
GstClockTime dur;
g_mutex_lock(&src->lock);
if (!src->running) {
g_mutex_unlock(&src->lock);
return GST_FLOW_FLUSHING;
}
g_mutex_unlock(&src->lock);
// Захват кадра
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) while (!sh_data.buffer_copy_done)
wl_display_roundtrip(display); wl_display_roundtrip(display);
} }
message = g_strdup_printf("westonimagesrc world #%lu\n", (gulong)westonimagesrc->offset); // Размер кадра
message_len = strlen(message); 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;
buffer = gst_buffer_new_allocate(NULL, message_len, NULL); if (!gst_buffer_map(buffer, &map, GST_MAP_WRITE)) {
gst_buffer_map(buffer, &map, GST_MAP_WRITE); gst_buffer_unref(buffer);
memcpy(map.data, message, message_len); return GST_FLOW_ERROR;
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;
} }
g_mutex_unlock(&westonimagesrc->lock); struct screenshooter_output *output = wl_container_of(sh_data.output_list.next, output, link);
return GST_FLOW_FLUSHING; 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) { static gboolean gst_westonimagesrc_unlock(GstBaseSrc *basesrc) {

View File

@ -3,7 +3,7 @@
#include <gst/base/gstbasesrc.h> #include <gst/base/gstbasesrc.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/video/video.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
@ -37,12 +37,32 @@ typedef struct _GstWestonImageSrcClass GstWestonImageSrcClass;
struct _GstWestonImageSrc { struct _GstWestonImageSrc {
GstBaseSrc element; 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; GMutex lock;
GCond cond; GCond cond;
gboolean running; gboolean running;
guint timeout_id; guint timeout_id;
guint64 offset; // Счетчик отправленных сообщений guint64 offset;
gdouble framerate; guint64 frame_number;
gboolean do_timestamp;
// Stat
GstClockTime last_timestamp;
}; };
struct _GstWestonImageSrcClass { struct _GstWestonImageSrcClass {

View File

@ -363,7 +363,10 @@ if get_option('shell-desktop')
fallback : ['gstreamer', 'gst_dep']) fallback : ['gstreamer', 'gst_dep'])
gstbase_dep = dependency('gstreamer-base-1.0', version : '>=1.18', 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_plugin_c_args = ['-DHAVE_CONFIG_H']
@ -387,7 +390,7 @@ if get_option('shell-desktop')
weston_screenshooter_protocol_c, weston_screenshooter_protocol_c,
include_directories: common_inc, include_directories: common_inc,
c_args: gst_westonimagesrc_plugin_c_args, 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 : true,
install_dir : plugins_install_dir, install_dir : plugins_install_dir,
) )

View File

@ -70,7 +70,6 @@ struct screenshooter_data {
int buffer_copy_done; int buffer_copy_done;
}; };
static void static void
display_handle_geometry(void *data, display_handle_geometry(void *data,
struct wl_output *wl_output, struct wl_output *wl_output,