diff options
author | Matthias Vogelgesang <matthias.vogelgesang@kit.edu> | 2014-02-13 14:54:34 +0100 |
---|---|---|
committer | Matthias Vogelgesang <matthias.vogelgesang@kit.edu> | 2014-02-14 16:17:16 +0100 |
commit | 961e60a89bc60e760707892539ccd7c6d283ef01 (patch) | |
tree | 6e002e95258e4e736c58f5187ff93cf83579541b | |
parent | f15d21389a81f8df36b00113aed5c81d27143861 (diff) | |
download | libuca-961e60a89bc60e760707892539ccd7c6d283ef01.tar.gz libuca-961e60a89bc60e760707892539ccd7c6d283ef01.tar.bz2 libuca-961e60a89bc60e760707892539ccd7c6d283ef01.tar.xz libuca-961e60a89bc60e760707892539ccd7c6d283ef01.zip |
Fix #28: Add buffered recording to base class
This change adds new properties ::buffered and ::num-buffers to the base class.
If ::buffered is TRUE, uca_camera_start_recording will spawn a new thread which
will call the camera-specific grab. Any call to uca_camera_grab will return the
next item from the ring buffer.
-rw-r--r-- | bin/gui/control.c | 2 | ||||
-rw-r--r-- | bin/tools/grab.c | 1 | ||||
-rw-r--r-- | src/uca-camera.c | 204 | ||||
-rw-r--r-- | src/uca-camera.h | 3 | ||||
-rw-r--r-- | src/uca-ring-buffer.c | 15 | ||||
-rw-r--r-- | src/uca-ring-buffer.h | 1 | ||||
-rw-r--r-- | test/test-ring-buffer.c | 4 |
7 files changed, 195 insertions, 35 deletions
diff --git a/bin/gui/control.c b/bin/gui/control.c index d4191a1..b02c42d 100644 --- a/bin/gui/control.c +++ b/bin/gui/control.c @@ -752,6 +752,7 @@ record_frames (gpointer args) buffer = uca_ring_buffer_get_write_pointer (data->buffer); uca_camera_grab (data->camera, buffer, NULL); + uca_ring_buffer_write_advance (data->buffer); if (error == NULL) { n_frames++; @@ -949,6 +950,7 @@ download_frames (ThreadData *data) while (error == NULL) { buffer = uca_ring_buffer_get_write_pointer (data->buffer); uca_camera_grab (data->camera, buffer, &error); + uca_ring_buffer_write_advance (data->buffer); gdk_threads_enter (); gtk_adjustment_set_value (data->download_adjustment, current_frame++); diff --git a/bin/tools/grab.c b/bin/tools/grab.c index a3dd544..11f8a83 100644 --- a/bin/tools/grab.c +++ b/bin/tools/grab.c @@ -170,6 +170,7 @@ record_frames (UcaCamera *camera, Options *opts) gdouble elapsed; uca_camera_grab (camera, uca_ring_buffer_get_write_pointer (buffer), &error); + uca_ring_buffer_write_advance (buffer); if (error != NULL) return error; diff --git a/src/uca-camera.c b/src/uca-camera.c index ee2c432..7aa5fb0 100644 --- a/src/uca-camera.c +++ b/src/uca-camera.c @@ -24,8 +24,10 @@ */ #include <glib.h> +#include <string.h> #include "config.h" #include "uca-camera.h" +#include "uca-ring-buffer.h" #include "uca-enums.h" #define UCA_CAMERA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UCA_TYPE_CAMERA, UcaCameraPrivate)) @@ -108,7 +110,9 @@ const gchar *uca_camera_props[N_BASE_PROPERTIES] = { "recorded-frames", "transfer-asynchronously", "is-recording", - "is-readout" + "is-readout", + "buffered", + "num-buffers", }; static GParamSpec *camera_properties[N_BASE_PROPERTIES] = { NULL, }; @@ -118,6 +122,10 @@ struct _UcaCameraPrivate { gboolean is_recording; gboolean is_readout; gboolean transfer_async; + gboolean buffered; + guint num_buffers; + GThread *read_thread; + UcaRingBuffer *ring_buffer; UcaCameraTrigger trigger; GValueArray *h_binnings; GValueArray *v_binnings; @@ -157,6 +165,14 @@ uca_camera_set_property (GObject *object, guint property_id, const GValue *value priv->trigger = g_value_get_enum (value); break; + case PROP_BUFFERED: + priv->buffered = g_value_get_boolean (value); + break; + + case PROP_NUM_BUFFERS: + priv->num_buffers = g_value_get_uint (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } @@ -231,12 +247,43 @@ uca_camera_get_property(GObject *object, guint property_id, GValue *value, GPara g_value_set_uint (value, 1); break; + case PROP_BUFFERED: + g_value_set_boolean (value, priv->buffered); + break; + + case PROP_NUM_BUFFERS: + g_value_set_uint (value, priv->num_buffers); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void +uca_camera_dispose (GObject *object) +{ + UcaCameraPrivate *priv; + + priv = UCA_CAMERA_GET_PRIVATE (object); + + if (priv->is_recording) { + GError *error; + + uca_camera_stop_recording (UCA_CAMERA (object), &error); + g_warning ("Could not stop recording: %s", error->message); + g_error_free (error); + } + + if (priv->ring_buffer != NULL) { + g_object_unref (priv->ring_buffer); + priv->ring_buffer = NULL; + } + + G_OBJECT_CLASS (uca_camera_parent_class)->dispose (object); +} + +static void uca_camera_finalize (GObject *object) { UcaCameraPrivate *priv; @@ -254,6 +301,7 @@ uca_camera_class_init (UcaCameraClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->set_property = uca_camera_set_property; gobject_class->get_property = uca_camera_get_property; + gobject_class->dispose = uca_camera_dispose; gobject_class->finalize = uca_camera_finalize; klass->start_recording = NULL; @@ -451,6 +499,19 @@ uca_camera_class_init (UcaCameraClass *klass) "Is camera in readout mode", FALSE, G_PARAM_READABLE); + camera_properties[PROP_BUFFERED] = + g_param_spec_boolean(uca_camera_props[PROP_BUFFERED], + "TRUE if libuca should buffer frames", + "TRUE if libuca should buffer frames", + FALSE, G_PARAM_READWRITE); + + camera_properties[PROP_NUM_BUFFERS] = + g_param_spec_uint(uca_camera_props[PROP_NUM_BUFFERS], + "Number of frame buffers in the ring buffer ", + "Number of frame buffers in the ring buffer ", + 0, G_MAXUINT, 4, + G_PARAM_READWRITE); + for (guint id = PROP_0 + 1; id < N_BASE_PROPERTIES; id++) g_object_class_install_property(gobject_class, id, camera_properties[id]); @@ -471,6 +532,9 @@ uca_camera_init (UcaCamera *camera) camera->priv->trigger = UCA_CAMERA_TRIGGER_AUTO; camera->priv->h_binnings = g_value_array_new (1); camera->priv->v_binnings = g_value_array_new (1); + camera->priv->buffered = FALSE; + camera->priv->num_buffers = 4; + camera->priv->ring_buffer = NULL; g_value_init (&val, G_TYPE_UINT); g_value_set_uint (&val, 1); @@ -496,6 +560,28 @@ uca_camera_init (UcaCamera *camera) uca_camera_set_property_unit (camera_properties[PROP_RECORDED_FRAMES], UCA_UNIT_COUNT); } +static gpointer +buffer_thread (UcaCamera *camera) +{ + UcaCameraClass *klass; + GError *error = NULL; + + klass = UCA_CAMERA_GET_CLASS (camera); + + while (camera->priv->is_recording) { + gpointer buffer; + + buffer = uca_ring_buffer_get_write_pointer (camera->priv->ring_buffer); + + if (!(*klass->grab) (camera, buffer, &error)) + break; + + uca_ring_buffer_write_advance (camera->priv->ring_buffer); + } + + return error; +} + /** * uca_camera_start_recording: * @camera: A #UcaCamera object @@ -509,6 +595,7 @@ void uca_camera_start_recording (UcaCamera *camera, GError **error) { UcaCameraClass *klass; + UcaCameraPrivate *priv; GError *tmp_error = NULL; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; @@ -519,17 +606,19 @@ uca_camera_start_recording (UcaCamera *camera, GError **error) g_return_if_fail (klass != NULL); g_return_if_fail (klass->start_recording != NULL); + priv = camera->priv; + g_static_mutex_lock (&mutex); - if (camera->priv->is_recording) { + if (priv->is_recording) { g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_RECORDING, - "Camera is already recording"); + "Camera is already recording"); goto start_recording_unlock; } - if (camera->priv->transfer_async && (camera->grab_func == NULL)) { + if (priv->transfer_async && (camera->grab_func == NULL)) { g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NO_GRAB_FUNC, - "No grab callback function set"); + "No grab callback function set"); goto start_recording_unlock; } @@ -538,14 +627,32 @@ uca_camera_start_recording (UcaCamera *camera, GError **error) g_static_mutex_unlock (&access_lock); if (tmp_error == NULL) { - camera->priv->is_readout = FALSE; - camera->priv->is_recording = TRUE; + priv->is_readout = FALSE; + priv->is_recording = TRUE; /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ g_object_notify (G_OBJECT (camera), "is-recording"); } else g_propagate_error (error, tmp_error); + if (priv->buffered) { + guint width, height, bitdepth; + guint pixel_size; + + g_object_get (camera, + "roi-width", &width, + "roi-height", &height, + "sensor-bitdepth", &bitdepth, + NULL); + + pixel_size = bitdepth <= 8 ? 1 : 2; + priv->ring_buffer = uca_ring_buffer_new (width * height * pixel_size, + priv->num_buffers); + + /* Let's read out the frames from another thread */ + priv->read_thread = g_thread_new ("read-thread", (GThreadFunc) buffer_thread, camera); + } + start_recording_unlock: g_static_mutex_unlock (&mutex); } @@ -561,6 +668,8 @@ void uca_camera_stop_recording (UcaCamera *camera, GError **error) { UcaCameraClass *klass; + UcaCameraPrivate *priv; + GError *tmp_error = NULL; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; g_return_if_fail (UCA_IS_CAMERA (camera)); @@ -570,29 +679,40 @@ uca_camera_stop_recording (UcaCamera *camera, GError **error) g_return_if_fail (klass != NULL); g_return_if_fail (klass->stop_recording != NULL); + priv = camera->priv; + g_static_mutex_lock (&mutex); - if (!camera->priv->is_recording) { + if (!priv->is_recording) { g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, "Camera is not recording"); + goto error_stop_recording; } - else { - GError *tmp_error = NULL; - g_static_mutex_lock (&access_lock); - (*klass->stop_recording)(camera, &tmp_error); - g_static_mutex_unlock (&access_lock); + g_static_mutex_lock (&access_lock); + (*klass->stop_recording)(camera, &tmp_error); + g_static_mutex_unlock (&access_lock); - if (tmp_error == NULL) { - camera->priv->is_readout = FALSE; - camera->priv->is_recording = FALSE; - /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ - g_object_notify (G_OBJECT (camera), "is-recording"); - } - else - g_propagate_error (error, tmp_error); + if (tmp_error == NULL) { + priv->is_readout = FALSE; + priv->is_recording = FALSE; + /* TODO: we should depend on GLib 2.26 and use g_object_notify_by_pspec */ + g_object_notify (G_OBJECT (camera), "is-recording"); + } + else + g_propagate_error (error, tmp_error); + + if (priv->buffered) { + g_thread_join (priv->read_thread); + priv->read_thread = NULL; } + if (camera->priv->ring_buffer != NULL) { + g_object_unref (camera->priv->ring_buffer); + camera->priv->ring_buffer = NULL; + } + +error_stop_recording: g_static_mutex_unlock (&mutex); } @@ -786,17 +906,43 @@ uca_camera_grab (UcaCamera *camera, gpointer data, GError **error) g_return_val_if_fail (klass->grab != NULL, FALSE); g_return_val_if_fail (data != NULL, FALSE); - g_static_mutex_lock (&mutex); + if (!camera->priv->buffered) { + g_static_mutex_lock (&mutex); - if (!camera->priv->is_recording && !camera->priv->is_readout) - g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, "Camera is neither recording nor in readout mode"); - else { - g_static_mutex_lock (&access_lock); - result = (*klass->grab) (camera, data, error); - g_static_mutex_unlock (&access_lock); + if (!camera->priv->is_recording && !camera->priv->is_readout) { + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_NOT_RECORDING, + "Camera is neither recording nor in readout mode"); + } + else { + g_static_mutex_lock (&access_lock); + result = (*klass->grab) (camera, data, error); + g_static_mutex_unlock (&access_lock); + } + + g_static_mutex_unlock (&mutex); } + else { + gpointer buffer; - g_static_mutex_unlock (&mutex); + /* + * Spin-lock until we can read something. This shouldn't happen to + * often, as buffering is usually used in those cases when the camera is + * faster than the software. + */ + while (!uca_ring_buffer_available (camera->priv->ring_buffer)) + ; + + buffer = uca_ring_buffer_get_read_pointer (camera->priv->ring_buffer); + + if (buffer == NULL) { + g_set_error (error, UCA_CAMERA_ERROR, UCA_CAMERA_ERROR_END_OF_STREAM, + "Ring buffer is empty"); + } + else { + memcpy (data, buffer, uca_ring_buffer_get_block_size (camera->priv->ring_buffer)); + result = TRUE; + } + } return result; } diff --git a/src/uca-camera.h b/src/uca-camera.h index c11644d..3191577 100644 --- a/src/uca-camera.h +++ b/src/uca-camera.h @@ -93,6 +93,9 @@ enum { PROP_TRANSFER_ASYNCHRONOUSLY, PROP_IS_RECORDING, PROP_IS_READOUT, + + PROP_BUFFERED, + PROP_NUM_BUFFERS, N_BASE_PROPERTIES }; diff --git a/src/uca-ring-buffer.c b/src/uca-ring-buffer.c index 98ede8f..26558d7 100644 --- a/src/uca-ring-buffer.c +++ b/src/uca-ring-buffer.c @@ -30,7 +30,6 @@ struct _UcaRingBufferPrivate { guint read_index; guint read; guint written; - gboolean full; }; enum { @@ -62,7 +61,6 @@ uca_ring_buffer_reset (UcaRingBuffer *buffer) buffer->priv->write_index = 0; buffer->priv->read_index = 0; - buffer->priv->full = FALSE; } gsize @@ -97,6 +95,8 @@ uca_ring_buffer_get_read_pointer (UcaRingBuffer *buffer) g_return_val_if_fail (UCA_IS_RING_BUFFER (buffer), NULL); priv = buffer->priv; + + g_return_val_if_fail (priv->read_index != priv->write_index, NULL); data = priv->data + (priv->read_index % priv->n_blocks_total) * priv->block_size; priv->read_index++; return data; @@ -112,14 +112,17 @@ uca_ring_buffer_get_write_pointer (UcaRingBuffer *buffer) priv = buffer->priv; data = priv->data + (priv->write_index % priv->n_blocks_total) * priv->block_size; - priv->write_index++; - - if ((priv->write_index - priv->read_index) == priv->n_blocks_total) - priv->full = TRUE; return data; } +void +uca_ring_buffer_write_advance (UcaRingBuffer *buffer) +{ + g_return_if_fail (UCA_IS_RING_BUFFER (buffer)); + buffer->priv->write_index++; +} + gpointer uca_ring_buffer_peek_pointer (UcaRingBuffer *buffer) { diff --git a/src/uca-ring-buffer.h b/src/uca-ring-buffer.h index c90f2d2..9fcf3b6 100644 --- a/src/uca-ring-buffer.h +++ b/src/uca-ring-buffer.h @@ -37,6 +37,7 @@ gboolean uca_ring_buffer_available (UcaRingBuffer *buffer); void uca_ring_buffer_proceed (UcaRingBuffer *buffer); gpointer uca_ring_buffer_get_read_pointer (UcaRingBuffer *buffer); gpointer uca_ring_buffer_get_write_pointer (UcaRingBuffer *buffer); +void uca_ring_buffer_write_advance (UcaRingBuffer *buffer); gpointer uca_ring_buffer_get_pointer (UcaRingBuffer *buffer, guint index); gpointer uca_ring_buffer_peek_pointer (UcaRingBuffer *buffer); diff --git a/test/test-ring-buffer.c b/test/test-ring-buffer.c index 02bf1eb..b9ff210 100644 --- a/test/test-ring-buffer.c +++ b/test/test-ring-buffer.c @@ -43,12 +43,14 @@ test_ring (void) data = uca_ring_buffer_get_write_pointer (buffer); data[0] = 0xBADF00D; + uca_ring_buffer_write_advance (buffer); g_assert (uca_ring_buffer_available (buffer)); g_assert (uca_ring_buffer_get_num_blocks (buffer) == 1); data = uca_ring_buffer_get_write_pointer (buffer); data[0] = 0xDEADBEEF; + uca_ring_buffer_write_advance (buffer); g_assert (uca_ring_buffer_get_num_blocks (buffer) == 2); @@ -74,9 +76,11 @@ test_overwrite (void) data = uca_ring_buffer_get_write_pointer (buffer); data[0] = 0xBADF00D; + uca_ring_buffer_write_advance (buffer); data = uca_ring_buffer_get_write_pointer (buffer); data[0] = 0xDEADBEEF; + uca_ring_buffer_write_advance (buffer); data = uca_ring_buffer_get_read_pointer (buffer); g_assert (data[0] == 0xDEADBEEF); |