diff options
Diffstat (limited to 'src/ui/new_contact.c')
-rw-r--r-- | src/ui/new_contact.c | 374 |
1 files changed, 234 insertions, 140 deletions
diff --git a/src/ui/new_contact.c b/src/ui/new_contact.c index 094dd67..b4de414 100644 --- a/src/ui/new_contact.c +++ b/src/ui/new_contact.c | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | #include "new_contact.h" | 25 | #include "new_contact.h" |
26 | 26 | ||
27 | #include <gstreamer-1.0/gst/app/gstappsink.h> | ||
28 | |||
27 | #include "../application.h" | 29 | #include "../application.h" |
28 | 30 | ||
29 | static void | 31 | static void |
@@ -89,61 +91,25 @@ handle_id_drawing_area_draw(GtkWidget* drawing_area, | |||
89 | 91 | ||
90 | gtk_render_background(context, cairo, 0, 0, width, height); | 92 | gtk_render_background(context, cairo, 0, 0, width, height); |
91 | 93 | ||
92 | GdkPixbuf *image = NULL; | ||
93 | |||
94 | if (!(handle->image)) | 94 | if (!(handle->image)) |
95 | return FALSE; | 95 | return FALSE; |
96 | 96 | ||
97 | uint w, h; | 97 | uint w, h; |
98 | zbar_image_get_size(handle->image, &w, &h); | 98 | w = gdk_pixbuf_get_width(handle->image); |
99 | 99 | h = gdk_pixbuf_get_height(handle->image); | |
100 | uint x, y, min_size; | ||
101 | min_size = (w < h? w : h); | ||
102 | x = (w - min_size) / 2; | ||
103 | y = (h - min_size) / 2; | ||
104 | |||
105 | const void* data = (const void*) ( | ||
106 | (const char*) zbar_image_get_data(handle->image) + | ||
107 | (x + y * w) * 3 | ||
108 | ); | ||
109 | |||
110 | image = gdk_pixbuf_new_from_data( | ||
111 | data, | ||
112 | GDK_COLORSPACE_RGB, | ||
113 | FALSE, | ||
114 | 8, | ||
115 | min_size, | ||
116 | min_size, | ||
117 | w * 3, | ||
118 | NULL, | ||
119 | NULL | ||
120 | ); | ||
121 | 100 | ||
122 | GString *scan_result = (GString*) zbar_image_get_userdata(handle->image); | 101 | uint min_size = (w < h? w : h); |
123 | 102 | ||
124 | if (!scan_result) | 103 | double ratio_width = 1.0 * width / min_size; |
125 | goto render_image; | 104 | double ratio_height = 1.0 * height / min_size; |
126 | |||
127 | gtk_entry_set_text(handle->id_entry, scan_result->str); | ||
128 | g_string_free(scan_result, TRUE); | ||
129 | |||
130 | render_image: | ||
131 | if (!image) | ||
132 | return FALSE; | ||
133 | |||
134 | int dwidth = gdk_pixbuf_get_width(image); | ||
135 | int dheight = gdk_pixbuf_get_height(image); | ||
136 | |||
137 | double ratio_width = 1.0 * width / dwidth; | ||
138 | double ratio_height = 1.0 * height / dheight; | ||
139 | 105 | ||
140 | const double ratio = ratio_width < ratio_height? ratio_width : ratio_height; | 106 | const double ratio = ratio_width < ratio_height? ratio_width : ratio_height; |
141 | 107 | ||
142 | dwidth = (int) (dwidth * ratio); | 108 | w = (uint) (min_size * ratio); |
143 | dheight = (int) (dheight * ratio); | 109 | h = (uint) (min_size * ratio); |
144 | 110 | ||
145 | double dx = (width - dwidth) * 0.5; | 111 | double dx = (width - w) * 0.5; |
146 | double dy = (height - dheight) * 0.5; | 112 | double dy = (height - h) * 0.5; |
147 | 113 | ||
148 | const int interp_type = (ratio >= 1.0? | 114 | const int interp_type = (ratio >= 1.0? |
149 | GDK_INTERP_NEAREST : | 115 | GDK_INTERP_NEAREST : |
@@ -151,9 +117,9 @@ render_image: | |||
151 | ); | 117 | ); |
152 | 118 | ||
153 | GdkPixbuf* scaled = gdk_pixbuf_scale_simple( | 119 | GdkPixbuf* scaled = gdk_pixbuf_scale_simple( |
154 | image, | 120 | handle->image, |
155 | dwidth, | 121 | w, |
156 | dheight, | 122 | h, |
157 | interp_type | 123 | interp_type |
158 | ); | 124 | ); |
159 | 125 | ||
@@ -162,36 +128,78 @@ render_image: | |||
162 | cairo_fill(cairo); | 128 | cairo_fill(cairo); |
163 | 129 | ||
164 | g_object_unref(scaled); | 130 | g_object_unref(scaled); |
165 | g_object_unref(image); | ||
166 | |||
167 | zbar_image_destroy(handle->image); | ||
168 | handle->image = NULL; | ||
169 | |||
170 | return FALSE; | 131 | return FALSE; |
171 | } | 132 | } |
172 | 133 | ||
173 | static void | 134 | static void |
174 | _disable_video_processing(UI_NEW_CONTACT_Handle *handle) | 135 | _disable_video_processing(UI_NEW_CONTACT_Handle *handle, |
136 | gboolean drop_pipeline) | ||
175 | { | 137 | { |
176 | gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box); | 138 | gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box); |
177 | 139 | ||
178 | if (!(handle->video)) | 140 | if (0 != handle->idle_processing) |
141 | g_source_remove(handle->idle_processing); | ||
142 | |||
143 | handle->idle_processing = 0; | ||
144 | |||
145 | if ((!(handle->pipeline)) || (!drop_pipeline)) | ||
179 | return; | 146 | return; |
180 | 147 | ||
181 | const zbar_error_t error_code = zbar_video_get_error_code(handle->video); | 148 | gst_element_set_state(handle->pipeline, GST_STATE_NULL); |
149 | } | ||
182 | 150 | ||
183 | if (ZBAR_OK != error_code) | 151 | static void |
184 | { | 152 | _handle_video_sample(UI_NEW_CONTACT_Handle *handle, |
185 | const char *error_msg = zbar_video_error_string(handle->video, 0); | 153 | GstAppSink *appsink) |
154 | { | ||
155 | GstSample *sample = gst_app_sink_try_pull_sample(appsink, 10); | ||
186 | 156 | ||
187 | if (error_msg) | 157 | if (!sample) |
188 | fprintf(stderr, "%s", error_msg); | 158 | return; |
189 | else | ||
190 | fprintf(stderr, "ERROR: Unknown error with zbar (%d)\n", | ||
191 | (int) error_code); | ||
192 | } | ||
193 | 159 | ||
194 | handle->idle_processing = 0; | 160 | GstCaps *caps = gst_sample_get_caps(sample); |
161 | |||
162 | GstStructure *s = gst_caps_get_structure(caps, 0); | ||
163 | |||
164 | gint width, height; | ||
165 | gst_structure_get_int(s, "width", &width); | ||
166 | gst_structure_get_int(s, "height", &height); | ||
167 | |||
168 | uint x, y, min_size; | ||
169 | min_size = (width < height? width : height); | ||
170 | x = (width - min_size) / 2; | ||
171 | y = (height - min_size) / 2; | ||
172 | |||
173 | GstBuffer *buffer = gst_sample_get_buffer(sample); | ||
174 | GstMapInfo map; | ||
175 | |||
176 | gst_buffer_map(buffer, &map, GST_MAP_READ); | ||
177 | |||
178 | if (handle->image) | ||
179 | g_object_unref(handle->image); | ||
180 | |||
181 | const void* data = (const void*) ( | ||
182 | (const char*) (map.data) + (x + y * width) * 3 | ||
183 | ); | ||
184 | |||
185 | handle->image = gdk_pixbuf_new_from_data( | ||
186 | data, | ||
187 | GDK_COLORSPACE_RGB, | ||
188 | FALSE, | ||
189 | 8, | ||
190 | min_size, | ||
191 | min_size, | ||
192 | width * 3, | ||
193 | NULL, | ||
194 | NULL | ||
195 | ); | ||
196 | |||
197 | gst_buffer_unmap(buffer, &map); | ||
198 | |||
199 | if (handle->id_drawing_area) | ||
200 | gtk_widget_queue_draw(GTK_WIDGET(handle->id_drawing_area)); | ||
201 | |||
202 | gst_sample_unref(sample); | ||
195 | } | 203 | } |
196 | 204 | ||
197 | static gboolean | 205 | static gboolean |
@@ -202,72 +210,168 @@ idle_video_processing(gpointer user_data) | |||
202 | if (0 == handle->idle_processing) | 210 | if (0 == handle->idle_processing) |
203 | return FALSE; | 211 | return FALSE; |
204 | 212 | ||
205 | zbar_image_t *image = zbar_video_next_image(handle->video); | 213 | GstAppSink *appsink = GST_APP_SINK(handle->sink); |
206 | 214 | ||
207 | if (!image) | 215 | if (!appsink) |
208 | { | 216 | { |
209 | _disable_video_processing(handle); | 217 | _disable_video_processing(handle, TRUE); |
210 | return FALSE; | 218 | return FALSE; |
211 | } | 219 | } |
212 | 220 | ||
213 | GString *scan_result = NULL; | 221 | _handle_video_sample(handle, appsink); |
222 | return TRUE; | ||
223 | } | ||
214 | 224 | ||
215 | zbar_image_t *y8 = zbar_image_convert( | 225 | static void |
216 | image, | 226 | msg_error_cb(UNUSED GstBus *bus, |
217 | zbar_fourcc('Y', '8', '0', '0') | 227 | GstMessage *msg, |
218 | ); | 228 | gpointer *data) |
229 | { | ||
230 | UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data; | ||
231 | |||
232 | GError* error; | ||
233 | gst_message_parse_error(msg, &error, NULL); | ||
234 | |||
235 | if (!error) | ||
236 | fprintf(stderr, "ERROR: Unknown error\n"); | ||
237 | else if (error->message) | ||
238 | fprintf(stderr, "ERROR: %s (%d)", error->message, error->code); | ||
239 | else | ||
240 | fprintf(stderr, "ERROR: Unknown error (%d)\n", error->code); | ||
219 | 241 | ||
220 | if (zbar_scan_image(handle->scanner, y8) <= 0) | 242 | gst_element_set_state(handle->pipeline, GST_STATE_READY); |
221 | goto cleanup_scan; | 243 | |
244 | if (!(handle->preview_stack)) | ||
245 | return; | ||
222 | 246 | ||
223 | const zbar_symbol_set_t* set = zbar_image_scanner_get_results( | 247 | gtk_stack_set_visible_child( |
224 | handle->scanner | 248 | handle->preview_stack, |
249 | handle->fail_box | ||
225 | ); | 250 | ); |
251 | } | ||
226 | 252 | ||
227 | const zbar_symbol_t* symbol = zbar_symbol_set_first_symbol(set); | 253 | static void |
254 | msg_eos_cb(UNUSED GstBus *bus, | ||
255 | UNUSED GstMessage *msg, | ||
256 | gpointer *data) | ||
257 | { | ||
258 | UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data; | ||
259 | |||
260 | if (GST_MESSAGE_SRC(msg) == GST_OBJECT(handle->pipeline)) | ||
261 | _disable_video_processing(handle, TRUE); | ||
262 | } | ||
263 | |||
264 | static void | ||
265 | msg_state_changed_cb(UNUSED GstBus *bus, | ||
266 | GstMessage *msg, | ||
267 | gpointer *data) | ||
268 | { | ||
269 | UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data; | ||
228 | 270 | ||
229 | if (!symbol) | 271 | GstState old_state, new_state, pending_state; |
230 | goto cleanup_scan; | 272 | gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state); |
231 | 273 | ||
232 | uint data_len = 0; | 274 | if ((GST_MESSAGE_SRC(msg) != GST_OBJECT(handle->pipeline)) || |
233 | const char *data = NULL; | 275 | (new_state == old_state) || (!(handle->preview_stack))) |
276 | return; | ||
234 | 277 | ||
235 | for (; symbol; symbol = zbar_symbol_next(symbol)) | 278 | if (GST_STATE_PLAYING == new_state) |
236 | { | 279 | { |
237 | if (zbar_symbol_get_count(symbol)) | 280 | gtk_stack_set_visible_child( |
238 | continue; | 281 | handle->preview_stack, |
282 | GTK_WIDGET(handle->id_drawing_area) | ||
283 | ); | ||
239 | 284 | ||
240 | data_len = zbar_symbol_get_data_length(symbol); | 285 | if (0 == handle->idle_processing) |
241 | data = zbar_symbol_get_data(symbol); | 286 | handle->idle_processing = g_idle_add(idle_video_processing, handle); |
242 | } | 287 | } |
288 | else if (GST_STATE_PAUSED == new_state) | ||
289 | _disable_video_processing(handle, FALSE); | ||
290 | } | ||
243 | 291 | ||
244 | if ((data_len > 0) && (data)) | 292 | static void |
245 | scan_result = g_string_new_len(data, data_len); | 293 | msg_barcode_cb(UNUSED GstBus *bus, |
294 | GstMessage *msg, | ||
295 | gpointer *data) | ||
296 | { | ||
297 | UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) data; | ||
298 | GstMessageType msg_type = GST_MESSAGE_TYPE(msg); | ||
299 | |||
300 | if ((GST_MESSAGE_SRC(msg) != GST_OBJECT(handle->scanner)) || | ||
301 | (GST_MESSAGE_ELEMENT != msg_type)) | ||
302 | return; | ||
303 | |||
304 | const GstStructure *s = gst_message_get_structure(msg); | ||
246 | 305 | ||
247 | cleanup_scan: | 306 | if (!s) |
248 | zbar_image_destroy(y8); | 307 | return; |
308 | |||
309 | const gchar *type = gst_structure_get_string(s, "type"); | ||
310 | const gchar *symbol = gst_structure_get_string(s, "symbol"); | ||
311 | |||
312 | if ((!type) || (!symbol) || (0 != g_strcmp0(type, "QR-Code"))) | ||
313 | return; | ||
249 | 314 | ||
250 | zbar_image_t *rgb = zbar_image_convert( | 315 | if (handle->id_entry) |
251 | image, | 316 | gtk_entry_set_text(handle->id_entry, symbol); |
252 | zbar_fourcc('R', 'G', 'B', '3') | 317 | } |
318 | |||
319 | static void | ||
320 | _setup_gst_pipeline(UI_NEW_CONTACT_Handle *handle) | ||
321 | { | ||
322 | handle->pipeline = gst_parse_launch( | ||
323 | "v4l2src name=source ! videoconvert ! zbar name=scanner" | ||
324 | " ! videoconvert ! video/x-raw,format=RGB ! videoconvert ! appsink name=sink", | ||
325 | NULL | ||
253 | ); | 326 | ); |
254 | 327 | ||
255 | if (!rgb) | 328 | handle->source = gst_bin_get_by_name( |
256 | goto cleanup_image; | 329 | GST_BIN(handle->pipeline), "source" |
330 | ); | ||
257 | 331 | ||
258 | zbar_image_set_userdata(rgb, scan_result); | 332 | handle->scanner = gst_bin_get_by_name( |
333 | GST_BIN(handle->pipeline), "scanner" | ||
334 | ); | ||
259 | 335 | ||
260 | if (handle->image) | 336 | handle->sink = gst_bin_get_by_name( |
261 | zbar_image_destroy(handle->image); | 337 | GST_BIN(handle->pipeline), "sink" |
338 | ); | ||
262 | 339 | ||
263 | handle->image = rgb; | 340 | gst_app_sink_set_drop(GST_APP_SINK(handle->sink), TRUE); |
264 | 341 | ||
265 | if (handle->id_drawing_area) | 342 | GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(handle->pipeline)); |
266 | gtk_widget_queue_draw(GTK_WIDGET(handle->id_drawing_area)); | ||
267 | 343 | ||
268 | cleanup_image: | 344 | gst_bus_add_signal_watch(bus); |
269 | zbar_image_destroy(image); | 345 | |
270 | return TRUE; | 346 | g_signal_connect( |
347 | G_OBJECT(bus), | ||
348 | "message::error", | ||
349 | (GCallback) msg_error_cb, | ||
350 | handle | ||
351 | ); | ||
352 | |||
353 | g_signal_connect( | ||
354 | G_OBJECT(bus), | ||
355 | "message::eos", | ||
356 | (GCallback) msg_eos_cb, | ||
357 | handle | ||
358 | ); | ||
359 | |||
360 | g_signal_connect( | ||
361 | G_OBJECT(bus), | ||
362 | "message::state-changed", | ||
363 | (GCallback) msg_state_changed_cb, | ||
364 | handle | ||
365 | ); | ||
366 | |||
367 | g_signal_connect( | ||
368 | G_OBJECT(bus), | ||
369 | "message", | ||
370 | (GCallback) msg_barcode_cb, | ||
371 | handle | ||
372 | ); | ||
373 | |||
374 | gst_object_unref(bus); | ||
271 | } | 375 | } |
272 | 376 | ||
273 | static void* | 377 | static void* |
@@ -275,31 +379,17 @@ _ui_new_contact_video_thread(void *args) | |||
275 | { | 379 | { |
276 | UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) args; | 380 | UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) args; |
277 | 381 | ||
278 | if (0 != zbar_video_open(handle->video, "")) | 382 | if (!(handle->pipeline)) |
279 | { | ||
280 | _disable_video_processing(handle); | ||
281 | return NULL; | 383 | return NULL; |
282 | } | ||
283 | |||
284 | if (0 != zbar_video_enable(handle->video, 1)) | ||
285 | { | ||
286 | _disable_video_processing(handle); | ||
287 | return NULL; | ||
288 | } | ||
289 | 384 | ||
290 | zbar_image_scanner_set_config( | 385 | GstStateChangeReturn ret = gst_element_set_state( |
291 | handle->scanner, | 386 | handle->pipeline, |
292 | ZBAR_QRCODE, | 387 | GST_STATE_PLAYING |
293 | ZBAR_CFG_ENABLE, | ||
294 | TRUE | ||
295 | ); | 388 | ); |
296 | 389 | ||
297 | gtk_stack_set_visible_child( | 390 | if (GST_STATE_CHANGE_FAILURE == ret) |
298 | handle->preview_stack, | 391 | _disable_video_processing(handle, TRUE); |
299 | GTK_WIDGET(handle->id_drawing_area) | ||
300 | ); | ||
301 | 392 | ||
302 | handle->idle_processing = g_idle_add(idle_video_processing, handle); | ||
303 | return NULL; | 393 | return NULL; |
304 | } | 394 | } |
305 | 395 | ||
@@ -307,8 +397,16 @@ void | |||
307 | ui_new_contact_dialog_init(MESSENGER_Application *app, | 397 | ui_new_contact_dialog_init(MESSENGER_Application *app, |
308 | UI_NEW_CONTACT_Handle *handle) | 398 | UI_NEW_CONTACT_Handle *handle) |
309 | { | 399 | { |
310 | handle->video = zbar_video_create(); | 400 | _setup_gst_pipeline(handle); |
311 | handle->scanner = zbar_image_scanner_create(); | 401 | |
402 | handle->image = NULL; | ||
403 | |||
404 | pthread_create( | ||
405 | &(handle->video_tid), | ||
406 | NULL, | ||
407 | _ui_new_contact_video_thread, | ||
408 | handle | ||
409 | ); | ||
312 | 410 | ||
313 | handle->builder = gtk_builder_new_from_resource( | 411 | handle->builder = gtk_builder_new_from_resource( |
314 | application_get_resource_path(app, "ui/new_contact.ui") | 412 | application_get_resource_path(app, "ui/new_contact.ui") |
@@ -335,13 +433,6 @@ ui_new_contact_dialog_init(MESSENGER_Application *app, | |||
335 | gtk_builder_get_object(handle->builder, "id_drawing_area") | 433 | gtk_builder_get_object(handle->builder, "id_drawing_area") |
336 | ); | 434 | ); |
337 | 435 | ||
338 | pthread_create( | ||
339 | &(handle->video_tid), | ||
340 | NULL, | ||
341 | _ui_new_contact_video_thread, | ||
342 | handle | ||
343 | ); | ||
344 | |||
345 | handle->id_draw_signal = g_signal_connect( | 436 | handle->id_draw_signal = g_signal_connect( |
346 | handle->id_drawing_area, | 437 | handle->id_drawing_area, |
347 | "draw", | 438 | "draw", |
@@ -398,13 +489,16 @@ ui_new_contact_dialog_cleanup(UI_NEW_CONTACT_Handle *handle) | |||
398 | handle->id_draw_signal | 489 | handle->id_draw_signal |
399 | ); | 490 | ); |
400 | 491 | ||
401 | g_object_unref(handle->builder); | ||
402 | |||
403 | if (handle->image) | 492 | if (handle->image) |
404 | zbar_image_destroy(handle->image); | 493 | g_object_unref(handle->image); |
405 | 494 | ||
406 | zbar_image_scanner_destroy(handle->scanner); | 495 | g_object_unref(handle->builder); |
407 | zbar_video_destroy(handle->video); | 496 | |
497 | if (handle->pipeline) | ||
498 | { | ||
499 | gst_element_set_state(handle->pipeline, GST_STATE_NULL); | ||
500 | gst_object_unref(GST_OBJECT(handle->pipeline)); | ||
501 | } | ||
408 | 502 | ||
409 | memset(handle, 0, sizeof(*handle)); | 503 | memset(handle, 0, sizeof(*handle)); |
410 | } | 504 | } |