summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Vogelgesang <matthias.vogelgesang@kit.edu>2014-02-13 14:54:34 +0100
committerMatthias Vogelgesang <matthias.vogelgesang@kit.edu>2014-02-14 16:17:16 +0100
commit961e60a89bc60e760707892539ccd7c6d283ef01 (patch)
tree6e002e95258e4e736c58f5187ff93cf83579541b
parentf15d21389a81f8df36b00113aed5c81d27143861 (diff)
downloaduca-961e60a89bc60e760707892539ccd7c6d283ef01.tar.gz
uca-961e60a89bc60e760707892539ccd7c6d283ef01.tar.bz2
uca-961e60a89bc60e760707892539ccd7c6d283ef01.tar.xz
uca-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.c2
-rw-r--r--bin/tools/grab.c1
-rw-r--r--src/uca-camera.c204
-rw-r--r--src/uca-camera.h3
-rw-r--r--src/uca-ring-buffer.c15
-rw-r--r--src/uca-ring-buffer.h1
-rw-r--r--test/test-ring-buffer.c4
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);