commit 50a1edb3b7e8c2341e4a48ba7e485d1d19b5fd52
parent 45a846f8cdca1bc05fc1278a333f969c002f2e1c
Author: TheJackiMonster <thejackimonster@gmail.com>
Date: Sat, 12 Nov 2022 22:46:30 +0100
Improve gstreamer video pipeline for contact qr code scanning
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
Diffstat:
4 files changed, 56 insertions(+), 179 deletions(-)
diff --git a/README.md b/README.md
@@ -63,6 +63,25 @@ Here is a list of some useful build targets in the Makefile:
If you want to change the installation location, use the `--prefix=` parameter in the `configure` script. Also you can enable debugging builds by adding `--enable-debug` as parameter when running the `configure` script.
+## Runtime
+
+The application will utilize gstreamer to scan a video feed from your camera for QR codes to add new contacts conveniently. This feature requires some gstreamer plugins to be installed:
+
+ - [aspectratiocrop](https://gstreamer.freedesktop.org/documentation/videocrop/aspectratiocrop.html?gi-language=c)
+ - [audioconvert](https://gstreamer.freedesktop.org/documentation/audioconvert/index.html?gi-language=c)
+ - [autoaudiosink](https://gstreamer.freedesktop.org/documentation/autodetect/autoaudiosink.html?gi-language=c)
+ - [autoaudiosrc](https://gstreamer.freedesktop.org/documentation/autodetect/autoaudiosrc.html?gi-language=c)
+ - [gtksink](https://gstreamer.freedesktop.org/documentation/gtk/gtksink.html?gi-language=c)
+ - [oggdemux](https://gstreamer.freedesktop.org/documentation/ogg/oggdemux.html?gi-language=c)
+ - [oggmux](https://gstreamer.freedesktop.org/documentation/ogg/oggmux.html?gi-language=c)
+ - [v4l2src](https://gstreamer.freedesktop.org/documentation/video4linux2/v4l2src.html?gi-language=c)
+ - [videoconvert](https://gstreamer.freedesktop.org/documentation/videoconvertscale/videoconvert.html?gi-language=c)
+ - [vorbisdec](https://gstreamer.freedesktop.org/documentation/vorbis/vorbisdec.html?gi-language=c)
+ - [vorbisenc](https://gstreamer.freedesktop.org/documentation/vorbis/vorbisenc.html?gi-language=c)
+ - [zbar](https://gstreamer.freedesktop.org/documentation/zbar/index.html?gi-language=c)
+
+Install packages depending on your distribution to be able to use those plugins.
+
## Contribution
If you want to contribute to this project as well, the following options are available:
diff --git a/resources/ui/new_contact.ui b/resources/ui/new_contact.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.38.2
+<!-- Generated with glade 3.40.0
Copyright (C) 2021‑‑2022 GNUnet e.V.
@@ -123,10 +123,14 @@ Author: Tobias Frisch
</packing>
</child>
<child>
- <object class="GtkDrawingArea" id="id_drawing_area">
+ <object class="GtkBox" id="video_box">
<property name="height-request">250</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="name">page_drawing</property>
diff --git a/src/ui/new_contact.c b/src/ui/new_contact.c
@@ -74,74 +74,12 @@ handle_dialog_destroy(UNUSED GtkWidget *window,
ui_new_contact_dialog_cleanup((UI_NEW_CONTACT_Handle*) user_data);
}
-static gboolean
-handle_id_drawing_area_draw(GtkWidget* drawing_area,
- cairo_t* cairo,
- gpointer user_data)
-{
- UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) user_data;
-
- GtkStyleContext* context = gtk_widget_get_style_context(drawing_area);
-
- if (!context)
- return FALSE;
-
- const guint width = gtk_widget_get_allocated_width(drawing_area);
- const guint height = gtk_widget_get_allocated_height(drawing_area);
-
- gtk_render_background(context, cairo, 0, 0, width, height);
-
- if (!(handle->image))
- return FALSE;
-
- uint w, h;
- w = gdk_pixbuf_get_width(handle->image);
- h = gdk_pixbuf_get_height(handle->image);
-
- uint min_size = (w < h? w : h);
-
- double ratio_width = 1.0 * width / min_size;
- double ratio_height = 1.0 * height / min_size;
-
- const double ratio = ratio_width < ratio_height? ratio_width : ratio_height;
-
- w = (uint) (min_size * ratio);
- h = (uint) (min_size * ratio);
-
- double dx = (width - w) * 0.5;
- double dy = (height - h) * 0.5;
-
- const int interp_type = (ratio >= 1.0?
- GDK_INTERP_NEAREST :
- GDK_INTERP_BILINEAR
- );
-
- GdkPixbuf* scaled = gdk_pixbuf_scale_simple(
- handle->image,
- w,
- h,
- interp_type
- );
-
- gtk_render_icon(context, cairo, scaled, dx, dy);
-
- cairo_fill(cairo);
-
- g_object_unref(scaled);
- return FALSE;
-}
-
static void
_disable_video_processing(UI_NEW_CONTACT_Handle *handle,
gboolean drop_pipeline)
{
gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box);
- if (0 != handle->idle_processing)
- g_source_remove(handle->idle_processing);
-
- handle->idle_processing = 0;
-
if ((!(handle->pipeline)) || (!drop_pipeline))
return;
@@ -149,80 +87,6 @@ _disable_video_processing(UI_NEW_CONTACT_Handle *handle,
}
static void
-_handle_video_sample(UI_NEW_CONTACT_Handle *handle,
- GstAppSink *appsink)
-{
- GstSample *sample = gst_app_sink_try_pull_sample(appsink, 10);
-
- if (!sample)
- return;
-
- GstCaps *caps = gst_sample_get_caps(sample);
-
- GstStructure *s = gst_caps_get_structure(caps, 0);
-
- gint width, height;
- gst_structure_get_int(s, "width", &width);
- gst_structure_get_int(s, "height", &height);
-
- uint x, y, min_size;
- min_size = (width < height? width : height);
- x = (width - min_size) / 2;
- y = (height - min_size) / 2;
-
- GstBuffer *buffer = gst_sample_get_buffer(sample);
- GstMapInfo map;
-
- gst_buffer_map(buffer, &map, GST_MAP_READ);
-
- if (handle->image)
- g_object_unref(handle->image);
-
- const void* data = (const void*) (
- (const char*) (map.data) + (x + y * width) * 3
- );
-
- handle->image = gdk_pixbuf_new_from_data(
- data,
- GDK_COLORSPACE_RGB,
- FALSE,
- 8,
- min_size,
- min_size,
- width * 3,
- NULL,
- NULL
- );
-
- gst_buffer_unmap(buffer, &map);
-
- if (handle->id_drawing_area)
- gtk_widget_queue_draw(GTK_WIDGET(handle->id_drawing_area));
-
- gst_sample_unref(sample);
-}
-
-static gboolean
-idle_video_processing(gpointer user_data)
-{
- UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) user_data;
-
- if (0 == handle->idle_processing)
- return FALSE;
-
- GstAppSink *appsink = GST_APP_SINK(handle->sink);
-
- if (!appsink)
- {
- _disable_video_processing(handle, TRUE);
- return FALSE;
- }
-
- _handle_video_sample(handle, appsink);
- return TRUE;
-}
-
-static void
msg_error_cb(UNUSED GstBus *bus,
GstMessage *msg,
gpointer *data)
@@ -267,18 +131,13 @@ msg_state_changed_cb(UNUSED GstBus *bus,
(new_state == old_state) || (!(handle->preview_stack)))
return;
- if (GST_STATE_PLAYING == new_state)
- {
+ if ((GST_STATE_PAUSED == new_state) || (!(handle->sink)))
+ _disable_video_processing(handle, FALSE);
+ else if (GST_STATE_PLAYING == new_state)
gtk_stack_set_visible_child(
handle->preview_stack,
- GTK_WIDGET(handle->id_drawing_area)
+ handle->video_box
);
-
- if (0 == handle->idle_processing)
- handle->idle_processing = g_idle_add(idle_video_processing, handle);
- }
- else if (GST_STATE_PAUSED == new_state)
- _disable_video_processing(handle, FALSE);
}
static void
@@ -313,7 +172,9 @@ _setup_gst_pipeline(UI_NEW_CONTACT_Handle *handle)
{
handle->pipeline = gst_parse_launch(
"v4l2src name=source ! videoconvert ! zbar name=scanner"
- " ! videoconvert ! video/x-raw,format=RGB ! videoconvert ! appsink name=sink",
+ " ! videoconvert ! aspectratiocrop aspect-ratio=1/1"
+ " ! videoconvert ! video/x-raw,format=RGB"
+ " ! videoconvert ! gtksink name=sink",
NULL
);
@@ -329,8 +190,6 @@ _setup_gst_pipeline(UI_NEW_CONTACT_Handle *handle)
GST_BIN(handle->pipeline), "sink"
);
- gst_app_sink_set_drop(GST_APP_SINK(handle->sink), TRUE);
-
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(handle->pipeline));
gst_bus_add_signal_watch(bus);
@@ -391,8 +250,6 @@ ui_new_contact_dialog_init(MESSENGER_Application *app,
{
_setup_gst_pipeline(handle);
- handle->image = NULL;
-
pthread_create(
&(handle->video_tid),
NULL,
@@ -421,16 +278,31 @@ ui_new_contact_dialog_init(MESSENGER_Application *app,
gtk_builder_get_object(handle->builder, "fail_box")
);
- handle->id_drawing_area = GTK_DRAWING_AREA(
- gtk_builder_get_object(handle->builder, "id_drawing_area")
+ handle->video_box = GTK_WIDGET(
+ gtk_builder_get_object(handle->builder, "video_box")
);
- handle->id_draw_signal = g_signal_connect(
- handle->id_drawing_area,
- "draw",
- G_CALLBACK(handle_id_drawing_area_draw),
- handle
- );
+ GtkWidget *widget;
+ if (handle->sink)
+ g_object_get(handle->sink, "widget", &widget, NULL);
+ else
+ widget = NULL;
+
+ if (widget)
+ {
+ gtk_box_pack_start(
+ GTK_BOX(handle->video_box),
+ widget,
+ true,
+ true,
+ 0
+ );
+
+ g_object_unref(widget);
+ gtk_widget_realize(widget);
+
+ gtk_widget_show_all(handle->video_box);
+ }
handle->id_entry = GTK_ENTRY(
gtk_builder_get_object(handle->builder, "id_entry")
@@ -464,8 +336,6 @@ ui_new_contact_dialog_init(MESSENGER_Application *app,
G_CALLBACK(handle_dialog_destroy),
handle
);
-
- handle->idle_processing = 0;
}
void
@@ -473,17 +343,6 @@ ui_new_contact_dialog_cleanup(UI_NEW_CONTACT_Handle *handle)
{
pthread_join(handle->video_tid, NULL);
- if (0 != handle->idle_processing)
- g_source_remove(handle->idle_processing);
-
- g_signal_handler_disconnect(
- handle->id_drawing_area,
- handle->id_draw_signal
- );
-
- if (handle->image)
- g_object_unref(handle->image);
-
g_object_unref(handle->builder);
if (handle->pipeline)
diff --git a/src/ui/new_contact.h b/src/ui/new_contact.h
@@ -41,24 +41,19 @@ typedef struct UI_NEW_CONTACT_Handle
guint bus_watch;
- GdkPixbuf *image;
-
GtkBuilder *builder;
GtkDialog *dialog;
GtkStack *preview_stack;
GtkWidget *fail_box;
- GtkDrawingArea *id_drawing_area;
+ GtkWidget *video_box;
GtkEntry *id_entry;
- gulong id_draw_signal;
-
GtkButton *cancel_button;
GtkButton *confirm_button;
pthread_t video_tid;
- guint idle_processing;
} UI_NEW_CONTACT_Handle;
/**