From ef7ff789e9c15f4f4b8c65585de7e7fa4f9c9438 Mon Sep 17 00:00:00 2001 From: Matthias Vogelgesang Date: Fri, 12 Oct 2012 14:47:07 +0200 Subject: Add ring buffer recording for assessment --- tools/gui/CMakeLists.txt | 1 + tools/gui/control.c | 190 ++++++++++++++++++++++++++++++----------------- tools/gui/control.glade | 33 +++++++- tools/gui/ring-buffer.c | 64 ++++++++++++++++ tools/gui/ring-buffer.h | 28 +++++++ 5 files changed, 243 insertions(+), 73 deletions(-) create mode 100644 tools/gui/ring-buffer.c create mode 100644 tools/gui/ring-buffer.h (limited to 'tools/gui') diff --git a/tools/gui/CMakeLists.txt b/tools/gui/CMakeLists.txt index ff5e9f6..1000ac6 100644 --- a/tools/gui/CMakeLists.txt +++ b/tools/gui/CMakeLists.txt @@ -25,6 +25,7 @@ if (GTK2_FOUND) add_executable(control control.c + ring-buffer.c egg-property-cell-renderer.c egg-property-tree-view.c egg-histogram-view.c) diff --git a/tools/gui/control.c b/tools/gui/control.c index 214e4da..84799cb 100644 --- a/tools/gui/control.c +++ b/tools/gui/control.c @@ -22,11 +22,17 @@ #include #include "config.h" +#include "ring-buffer.h" #include "uca-camera.h" #include "uca-plugin-manager.h" #include "egg-property-tree-view.h" #include "egg-histogram-view.h" +typedef enum { + IDLE, + RUNNING, + RECORDING +} State; typedef struct { UcaCamera *camera; @@ -35,13 +41,14 @@ typedef struct { GtkWidget *start_button; GtkWidget *stop_button; GtkWidget *record_button; - GtkWidget *histogram_view; + + GtkWidget *histogram_view; GtkToggleButton *histogram_button; + GtkAdjustment *frame_slider; - guchar *buffer; + RingBuffer *buffer; guchar *pixels; - gboolean running; - gboolean store; + State state; int timestamp; int width; @@ -53,70 +60,66 @@ static UcaPluginManager *plugin_manager; static void -convert_8bit_to_rgb (guchar *output, guchar *input, gint width, gint height, gdouble min, gdouble max) +convert_grayscale_to_rgb (ThreadData *data, gpointer buffer) { - gdouble factor = 255.0 / (max - min); - - for (int i = 0, j = 0; i < width*height; i++) { - guchar val = (guchar) ((input[i] - min) * factor); - output[j++] = val; - output[j++] = val; - output[j++] = val; + gdouble min; + gdouble max; + gdouble factor; + guint8 *output; + + egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min, &max); + factor = 255.0 / (max - min); + output = data->pixels; + + if (data->pixel_size == 1) { + guint8 *input = (guint8 *) buffer; + + for (int i = 0, j = 0; i < data->width * data->height; i++) { + guchar val = (guchar) ((input[i] - min) * factor); + output[j++] = val; + output[j++] = val; + output[j++] = val; + } + } + else if (data->pixel_size == 2) { + guint16 *input = (guint16 *) buffer; + + for (int i = 0, j = 0; i < data->width * data->height; i++) { + guchar val = (guint8) ((input[i] - min) * factor); + output[j++] = val; + output[j++] = val; + output[j++] = val; + } } } static void -convert_16bit_to_rgb (guchar *output, guchar *input, gint width, gint height, gdouble min, gdouble max) +update_pixbuf (ThreadData *data) { - gdouble factor = 255.0 / (max - min); + gdk_flush (); + gtk_image_clear (GTK_IMAGE (data->image)); + gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); + gtk_widget_queue_draw_area (data->image, 0, 0, data->width, data->height); - for (int i = 0, j = 0; i < width*height; i++) { - guchar val = (guint8) ((input[i] - min) * factor); - output[j++] = val; - output[j++] = val; - output[j++] = val; - } + if (gtk_toggle_button_get_active (data->histogram_button)) + gtk_widget_queue_draw (data->histogram_view); } -static void * -grab_thread (void *args) +static gpointer +preview_frames (void *args) { ThreadData *data = (ThreadData *) args; - gchar filename[FILENAME_MAX] = {0,}; gint counter = 0; - while (data->running) { - gdouble min_value; - gdouble max_value; + while (data->state == RUNNING) { + gpointer buffer; - uca_camera_grab (data->camera, (gpointer) &data->buffer, NULL); - - if (data->store) { - snprintf (filename, FILENAME_MAX, "frame-%i-%08i.raw", data->timestamp, counter); - FILE *fp = fopen (filename, "wb"); - fwrite (data->buffer, data->width*data->height, data->pixel_size, fp); - fclose (fp); - } - - /* FIXME: We should actually check if this is really a new frame and - * just do nothing if it is an already displayed one. */ - egg_histogram_get_visible_range (EGG_HISTOGRAM_VIEW (data->histogram_view), &min_value, &max_value); - - if (data->pixel_size == 1) - convert_8bit_to_rgb (data->pixels, data->buffer, data->width, data->height, min_value, max_value); - else if (data->pixel_size == 2) - convert_16bit_to_rgb (data->pixels, data->buffer, data->width, data->height, min_value, max_value); + buffer = ring_buffer_get_current_pointer (data->buffer); + uca_camera_grab (data->camera, &buffer, NULL); + convert_grayscale_to_rgb (data, buffer); gdk_threads_enter (); - - gdk_flush (); - gtk_image_clear (GTK_IMAGE (data->image)); - gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), data->pixbuf); - gtk_widget_queue_draw_area (data->image, 0, 0, data->width, data->height); - - if (gtk_toggle_button_get_active (data->histogram_button)) - gtk_widget_queue_draw (data->histogram_view); - + update_pixbuf (data); gdk_threads_leave (); counter++; @@ -124,6 +127,33 @@ grab_thread (void *args) return NULL; } +static gpointer +record_frames (gpointer args) +{ + ThreadData *data; + gpointer buffer; + guint n_frames = 0; + + data = (ThreadData *) args; + ring_buffer_reset (data->buffer); + + while (data->state == RECORDING) { + buffer = ring_buffer_get_current_pointer (data->buffer); + uca_camera_grab (data->camera, &buffer, NULL); + ring_buffer_proceed (data->buffer); + n_frames++; + } + + n_frames = ring_buffer_get_num_blocks (data->buffer); + + gdk_threads_enter (); + gtk_adjustment_set_upper (data->frame_slider, n_frames - 1); + gtk_adjustment_set_value (data->frame_slider, n_frames - 1); + gdk_threads_leave (); + + return NULL; +} + gboolean on_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { @@ -134,17 +164,39 @@ void on_destroy (GtkWidget *widget, gpointer data) { ThreadData *td = (ThreadData *) data; - td->running = FALSE; + + td->state = IDLE; g_object_unref (td->camera); + ring_buffer_free (td->buffer); + gtk_main_quit (); } static void set_tool_button_state (ThreadData *data) { - gtk_widget_set_sensitive (data->start_button, !data->running); - gtk_widget_set_sensitive (data->stop_button, data->running); - gtk_widget_set_sensitive (data->record_button, !data->running); + gtk_widget_set_sensitive (data->start_button, + data->state == IDLE); + gtk_widget_set_sensitive (data->stop_button, + data->state == RUNNING || data->state == RECORDING); + gtk_widget_set_sensitive (data->record_button, + data->state == IDLE); +} + +static void +on_frame_slider_changed (GtkAdjustment *adjustment, gpointer user_data) +{ + ThreadData *data = (ThreadData *) user_data; + + if (data->state == IDLE) { + gpointer buffer; + gint index; + + index = (gint) gtk_adjustment_get_value (adjustment); + buffer = ring_buffer_get_pointer (data->buffer, index); + convert_grayscale_to_rgb (data, buffer); + update_pixbuf (data); + } } static void @@ -153,7 +205,7 @@ on_start_button_clicked (GtkWidget *widget, gpointer args) ThreadData *data = (ThreadData *) args; GError *error = NULL; - data->running = TRUE; + data->state = RUNNING; set_tool_button_state (data); uca_camera_start_recording (data->camera, &error); @@ -163,7 +215,7 @@ on_start_button_clicked (GtkWidget *widget, gpointer args) return; } - if (!g_thread_create (grab_thread, data, FALSE, &error)) { + if (!g_thread_create (preview_frames, data, FALSE, &error)) { g_printerr ("Failed to create thread: %s\n", error->message); return; } @@ -175,8 +227,7 @@ on_stop_button_clicked (GtkWidget *widget, gpointer args) ThreadData *data = (ThreadData *) args; GError *error = NULL; - data->running = FALSE; - data->store = FALSE; + data->state = IDLE; set_tool_button_state (data); uca_camera_stop_recording (data->camera, &error); @@ -192,13 +243,12 @@ on_record_button_clicked (GtkWidget *widget, gpointer args) GError *error = NULL; data->timestamp = (int) time (0); - data->store = TRUE; - data->running = TRUE; + data->state = RECORDING; set_tool_button_state (data); uca_camera_start_recording (data->camera, &error); - if (!g_thread_create (grab_thread, data, FALSE, &error)) + if (!g_thread_create (record_frames, data, FALSE, &error)) g_printerr ("Failed to create thread: %s\n", error->message); } @@ -240,18 +290,19 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name) td.pixel_size = bits_per_sample > 8 ? 2 : 1; td.image = image; td.pixbuf = pixbuf; - td.buffer = (guchar *) g_malloc (td.pixel_size * td.width * td.height); + td.buffer = ring_buffer_new (td.pixel_size * td.width * td.height, 256); td.pixels = gdk_pixbuf_get_pixels (pixbuf); - td.running = FALSE; - td.store = FALSE; + td.state = IDLE; td.camera = camera; td.histogram_view = egg_histogram_view_new (); td.histogram_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "histogram-checkbutton")); + td.frame_slider = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "frames-adjustment")); histogram_box = GTK_BOX (gtk_builder_get_object (builder, "histogram-box")); gtk_box_pack_start (histogram_box, td.histogram_view, TRUE, TRUE, 6); egg_histogram_view_set_data (EGG_HISTOGRAM_VIEW (td.histogram_view), - td.buffer, td.width * td.height, bits_per_sample, 256); + ring_buffer_get_current_pointer (td.buffer), + td.width * td.height, bits_per_sample, 256); window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); g_signal_connect (window, "destroy", G_CALLBACK (on_destroy), &td); @@ -261,16 +312,17 @@ create_main_window (GtkBuilder *builder, const gchar* camera_name) td.record_button = GTK_WIDGET (gtk_builder_get_object (builder, "record-button")); set_tool_button_state (&td); - g_object_bind_property (gtk_builder_get_object (builder, "min_bin_value_adjustment"), "value", + g_object_bind_property (gtk_builder_get_object (builder, "min-bin-value-adjustment"), "value", td.histogram_view, "minimum-bin-value", G_BINDING_DEFAULT); - max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max_bin_value_adjustment")); + max_bin_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "max-bin-value-adjustment")); gtk_adjustment_set_value (max_bin_adjustment, pow (2, bits_per_sample) - 1); g_object_bind_property (max_bin_adjustment, "value", td.histogram_view, "maximum-bin-value", G_BINDING_DEFAULT); + g_signal_connect (td.frame_slider, "value-changed", G_CALLBACK (on_frame_slider_changed), &td); g_signal_connect (td.start_button, "clicked", G_CALLBACK (on_start_button_clicked), &td); g_signal_connect (td.stop_button, "clicked", G_CALLBACK (on_stop_button_clicked), &td); g_signal_connect (td.record_button, "clicked", G_CALLBACK (on_record_button_clicked), &td); diff --git a/tools/gui/control.glade b/tools/gui/control.glade index ebe2cc8..c3008ec 100644 --- a/tools/gui/control.glade +++ b/tools/gui/control.glade @@ -230,7 +230,7 @@ True True - min_bin_value_adjustment + min-bin-value-adjustment 1 @@ -260,7 +260,7 @@ True True - max_bin_value_adjustment + max-bin-value-adjustment 1 @@ -309,6 +309,27 @@ False + + + True + True + frames-adjustment + 0 + + + 1 + + + + + True + Preview + + + 1 + False + + True @@ -425,15 +446,19 @@ - + 65535 1 10 - + 256 65535 1 10 + + 1 + 10 + diff --git a/tools/gui/ring-buffer.c b/tools/gui/ring-buffer.c new file mode 100644 index 0000000..56c7620 --- /dev/null +++ b/tools/gui/ring-buffer.c @@ -0,0 +1,64 @@ + +#include +#include "ring-buffer.h" + +RingBuffer * +ring_buffer_new (gsize block_size, + gsize n_blocks) +{ + RingBuffer *buffer; + + buffer = g_new0 (RingBuffer, 1); + buffer->block_size = block_size; + buffer->n_blocks_total = n_blocks; + buffer->n_blocks_used = 0; + buffer->start_index = 0; + buffer->data = g_malloc0 (n_blocks * buffer->block_size); + + return buffer; +} + +void +ring_buffer_free (RingBuffer *buffer) +{ + g_free (buffer->data); + g_free (buffer); +} + +void +ring_buffer_reset (RingBuffer *buffer) +{ + buffer->n_blocks_used = 0; + buffer->start_index = 0; +} + +gpointer +ring_buffer_get_current_pointer (RingBuffer *buffer) +{ + return ring_buffer_get_pointer (buffer, 0); +} + +gpointer +ring_buffer_get_pointer (RingBuffer *buffer, + guint index) +{ + g_assert (index < buffer->n_blocks_total); + return buffer->data + ((buffer->start_index + index) % buffer->n_blocks_total) * buffer->block_size; +} + +guint +ring_buffer_get_num_blocks (RingBuffer *buffer) +{ + return buffer->n_blocks_used; +} + +void +ring_buffer_proceed (RingBuffer *buffer) +{ + buffer->start_index++; + + if (buffer->n_blocks_used < buffer->n_blocks_total) + buffer->n_blocks_used++; + else + buffer->start_index = buffer->start_index % buffer->n_blocks_total; +} diff --git a/tools/gui/ring-buffer.h b/tools/gui/ring-buffer.h new file mode 100644 index 0000000..22cbde7 --- /dev/null +++ b/tools/gui/ring-buffer.h @@ -0,0 +1,28 @@ +#ifndef RING_BUFFER_H +#define RING_BUFFER_H + +#include + +G_BEGIN_DECLS + +typedef struct { + guchar *data; + gsize block_size; + guint n_blocks_total; + guint n_blocks_used; + guint start_index; +} RingBuffer; + +RingBuffer * ring_buffer_new (gsize block_size, + gsize n_blocks); +void ring_buffer_free (RingBuffer *buffer); +void ring_buffer_reset (RingBuffer *buffer); +gpointer ring_buffer_get_current_pointer (RingBuffer *buffer); +gpointer ring_buffer_get_pointer (RingBuffer *buffer, + guint index); +guint ring_buffer_get_num_blocks (RingBuffer *buffer); +void ring_buffer_proceed (RingBuffer *buffer); + +G_END_DECLS + +#endif -- cgit v1.2.3