summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli.c30
-rw-r--r--device.c2
-rw-r--r--device.h18
-rw-r--r--gui_app_window.c46
-rw-r--r--image.c88
-rw-r--r--image.h55
-rw-r--r--moticam.c102
7 files changed, 261 insertions, 80 deletions
diff --git a/cli.c b/cli.c
index 33edcee..b0d3a1b 100644
--- a/cli.c
+++ b/cli.c
@@ -34,23 +34,31 @@ cli_read_images(libusb_context *usb, struct device *device, int count,
{
device_start(device);
for (int i = 0; i < count;) {
- const struct device_image *device_image = device_read(device);
- if (device_image) {
+ struct image *image = device_read(device);
+ if (image) {
+ if (image->format != IMAGE_FORMAT_XBGR32) {
+ struct image *cimage = image_new(image->width, image->height,
+ image->width * 4, IMAGE_FORMAT_XBGR32);
+ image_convert(cimage, image);
+ image_unref(image);
+ image = cimage;
+ }
char *name = NULL;
if (asprintf(&name, out, i) < 0)
utils_fatal("can not prepare file name");
utils_info("write %s", name);
- png_image image;
- memset(&image, 0, sizeof(image));
- image.version = PNG_IMAGE_VERSION;
- image.width = device_image->width;
- image.height = device_image->height;
- image.format = PNG_FORMAT_BGRA;
- int r = png_image_write_to_file(&image, name, 0,
- device_image->pixels, device_image->stride, NULL);
+ png_image pimage;
+ memset(&pimage, 0, sizeof(pimage));
+ pimage.version = PNG_IMAGE_VERSION;
+ pimage.width = image->width;
+ pimage.height = image->height;
+ pimage.format = PNG_FORMAT_BGRA;
+ int r = png_image_write_to_file(&pimage, name, 0,
+ image->pixels, image->stride, NULL);
if (r == 0)
- utils_fatal("can not write image: %s", image.message);
+ utils_fatal("can not write image: %s", pimage.message);
free(name);
+ image_unref(image);
i++;
} else {
libusb_handle_events(usb);
diff --git a/device.c b/device.c
index 549cce8..cfc8783 100644
--- a/device.c
+++ b/device.c
@@ -104,7 +104,7 @@ device_start(struct device *device)
device->start(device);
}
-const struct device_image *
+struct image *
device_read(struct device *device)
{
return device->read(device);
diff --git a/device.h b/device.h
index 86e4b4f..01c6336 100644
--- a/device.h
+++ b/device.h
@@ -27,6 +27,8 @@
#include <libusb.h>
#include <stdint.h>
+#include "image.h"
+
struct device;
/* USB poll function. If the device is handled by this driver, it should
@@ -98,20 +100,8 @@ typedef void (*device_set_resolution_f)(
typedef void (*device_start_f)(
struct device *device);
-/* Image with attached information. */
-struct device_image {
- /* Image width. */
- int width;
- /* Image height. */
- int height;
- /* Byte offset between a line and the next line. */
- int stride;
- /* Frame buffer, ARGB format. */
- uint32_t *pixels;
-};
-
/* Return an image if one is available, else, return NULL. */
-typedef const struct device_image *(*device_read_f)(
+typedef struct image *(*device_read_f)(
struct device *device);
/* Stop streaming. */
@@ -168,7 +158,7 @@ void
device_start(struct device *device);
/* Read an image. */
-const struct device_image *
+struct image *
device_read(struct device *device);
/* Stop streaming. */
diff --git a/gui_app_window.c b/gui_app_window.c
index 4ec9e6d..2e4b7f0 100644
--- a/gui_app_window.c
+++ b/gui_app_window.c
@@ -49,6 +49,8 @@ struct _GuiAppWindowPrivate {
GdkRectangle allocation;
cairo_matrix_t transform;
cairo_surface_t *surface;
+ struct image *image;
+ struct image *image_converted;
bool rotate;
int width;
int height;
@@ -100,11 +102,43 @@ video_ready_cb(GuiApp *app, gpointer user_data)
{
GuiAppWindowPrivate *priv = user_data;
if (priv->started) {
- const struct device_image *image = device_read(priv->device);
+ struct image *image = device_read(priv->device);
if (image) {
+ /* Release old data. */
if (priv->surface) {
cairo_surface_destroy(priv->surface);
+ priv->surface = NULL;
}
+ if (priv->image) {
+ image_unref(priv->image);
+ priv->image = NULL;
+ }
+ /* Different format? Convert. */
+ if (image->format != IMAGE_FORMAT_XBGR32) {
+ if (priv->image_converted &&
+ (priv->image_converted->width != image->width
+ || priv->image_converted->height != image->height)) {
+ image_unref(priv->image_converted);
+ priv->image_converted = NULL;
+ }
+ if (!priv->image_converted) {
+ int stride =
+ cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
+ image->width);
+ priv->image_converted = image_new(image->width,
+ image->height, stride, IMAGE_FORMAT_XBGR32);
+ }
+ image_convert(priv->image_converted, image);
+ image_unref(image);
+ image = priv->image_converted;
+ } else {
+ if (priv->image_converted) {
+ image_unref(priv->image_converted);
+ priv->image_converted = NULL;
+ }
+ priv->image = image;
+ }
+ /* Make a surface. */
priv->surface = cairo_image_surface_create_for_data(
(unsigned char *) image->pixels, CAIRO_FORMAT_RGB24, image->width,
image->height, image->stride);
@@ -139,6 +173,14 @@ video_stop(GuiAppWindowPrivate *priv)
cairo_surface_destroy(priv->surface);
priv->surface = NULL;
}
+ if (priv->image) {
+ image_unref(priv->image);
+ priv->image = NULL;
+ }
+ if (priv->image_converted) {
+ image_unref(priv->image_converted);
+ priv->image_converted = NULL;
+ }
g_signal_handler_disconnect(priv->app, priv->video_ready_handler_id);
device_stop(priv->device);
priv->started = false;
@@ -318,6 +360,8 @@ gui_app_window_init(GuiAppWindow *win)
priv->allocation.width = -1;
cairo_matrix_init_identity(&priv->transform);
priv->surface = NULL;
+ priv->image = NULL;
+ priv->image_converted = NULL;
priv->rotate = true;
priv->width = -1;
priv->height = -1;
diff --git a/image.c b/image.c
index 373010b..73ac957 100644
--- a/image.c
+++ b/image.c
@@ -20,13 +20,58 @@
* Web: http://ni.fr.eu.org/
* Email: <nico at ni.fr.eu.org>
*/
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "image.h"
+#include "utils.h"
-#include <string.h>
+static void
+image_free(struct image *image, void *user_data)
+{
+ free(image);
+}
+
+struct image *
+image_new(int width, int height, int stride, enum image_format format)
+{
+ size_t struct_size_aligned = (sizeof(struct image) + 15) / 16 * 16;
+ size_t image_size_aligned = ((height * stride) + 15) / 16 * 16;
+ uint8_t *m = aligned_alloc(16,
+ struct_size_aligned + image_size_aligned);
+ if (!m) {
+ utils_fatal("memory exhausted");
+ }
+ struct image *image = (struct image *) m;
+ image->width = width;
+ image->height = height;
+ image->stride = stride;
+ image->format = format;
+ image->pixels = m + struct_size_aligned;
+ image->refs = 1;
+ image->release = image_free;
+ image->release_user_data = NULL;
+ return image;
+}
+
+void
+image_ref(struct image *image)
+{
+ image->refs++;
+}
void
-image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
- int stride)
+image_unref(struct image *image)
+{
+ image->refs--;
+ if (image->refs == 0 && image->release)
+ image->release(image, image->release_user_data);
+}
+
+static void
+image_convert_to_xbgr32_from_sgrbg8(struct image *dst,
+ const struct image *src)
{
/*
* Compute missing value using an average of its neighbours.
@@ -36,11 +81,15 @@ image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
* G R G R G R
* B G B G B G
*/
- const int in_stride = width;
- const int out_stride = stride;
+ assert(dst->width == src->width
+ && dst->height == src->height);
+ const int in_stride = src->stride;
+ const int out_stride = dst->stride;
+ int width = src->width;
+ int height = src->height;
/* Skip first line and column. */
- const uint8_t *in = bayer + in_stride + 1;
- uint8_t *out = (uint8_t *) rgb + out_stride + 4;
+ const uint8_t *in = src->pixels + in_stride + 1;
+ uint8_t *out = dst->pixels + out_stride + 4;
/* Loop over lines. */
for (int i = 1; i < height - 1; i += 2) {
/* Even lines. */
@@ -105,3 +154,28 @@ image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
out -= (height - 1) * out_stride;
memcpy(out, out + out_stride, width * 4);
}
+
+void
+image_convert(struct image *dst, const struct image *src)
+{
+ assert(dst->width == src->width
+ && dst->height == src->height);
+ if (dst->format == src->format) {
+ assert(dst->stride == src->stride);
+ memcpy(dst->pixels, src->pixels, src->stride * src->height);
+ } else {
+ switch (src->format) {
+ case IMAGE_FORMAT_SGRBG8:
+ switch (dst->format) {
+ case IMAGE_FORMAT_XBGR32:
+ image_convert_to_xbgr32_from_sgrbg8(dst, src);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+}
diff --git a/image.h b/image.h
index 4d04413..9e20859 100644
--- a/image.h
+++ b/image.h
@@ -24,8 +24,59 @@
*/
#include <stdint.h>
+/* Image format. */
+enum image_format
+{
+#define IMAGE_FORMAT_FOURCC(a, b, c, d) \
+ (((uint32_t)(a)) | ((uint32_t)(b) << 8) \
+ | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+ /* Bayer image format:
+ * G R G R
+ * B G B G */
+ IMAGE_FORMAT_SGRBG8 = IMAGE_FORMAT_FOURCC('B', 'R', 'B', 'G'),
+ /* RGB 32 bits, B in LSB. */
+ IMAGE_FORMAT_XBGR32 = IMAGE_FORMAT_FOURCC('X', 'R', '2', '4'),
+};
+
+struct image;
+
+/* Image release callback. */
+typedef void (*image_release_f)(struct image *image, void *user_data);
+
+/* Image with attached information. */
+struct image {
+ /* Image width. */
+ int width;
+ /* Image height. */
+ int height;
+ /* Byte offset between a line and the next line. */
+ int stride;
+ /* Image format. */
+ enum image_format format;
+ /* Frame buffer. */
+ uint8_t *pixels;
+ /* Number of reference to this image. */
+ int refs;
+ /* Release callback. */
+ image_release_f release;
+ /* Release callback user data. */
+ void *release_user_data;
+};
+
+/* Allocate an image using system allocator. */
+struct image *
+image_new(int width, int height, int stride, enum image_format format);
+
+/* Add a reference to an image. */
+void
+image_ref(struct image *image);
+
+/* Release a reference to an image. */
+void
+image_unref(struct image *image);
+
+/* Copy an image to another one, converting format. */
void
-image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height,
- int stride);
+image_convert(struct image *dst, const struct image *src);
#endif /* image_h */
diff --git a/moticam.c b/moticam.c
index 6e487ea..4f46478 100644
--- a/moticam.c
+++ b/moticam.c
@@ -40,12 +40,15 @@ struct moticam_device {
libusb_context *usb;
libusb_device_handle *handle;
double exposure_ms;
+ int width;
+ int height;
bool exposure_pending;
double gain;
bool gain_pending;
size_t rawbuffer_size;
- uint8_t *rawbuffer;
- struct device_image image;
+ struct image image[2];
+ struct image *current_image;
+ struct image *other_image;
struct libusb_transfer *usb_transfer;
bool usb_transfer_done;
};
@@ -212,15 +215,10 @@ moticam_set_resolution(struct device *device, int width, int height,
int stride)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(!mdev->image.pixels);
- /* Choose stride. */
- if (!stride)
- stride = width * sizeof(uint32_t);
- assert(stride >= width * sizeof(uint32_t));
- /* Store parameters. */
- mdev->image.width = width;
- mdev->image.height = height;
- mdev->image.stride = stride;
+ assert(!mdev->current_image);
+ /* Ignore stride, store parameters. */
+ mdev->width = width;
+ mdev->height = height;
}
static void
@@ -234,53 +232,62 @@ static void
moticam_start(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(!mdev->image.pixels);
+ assert(!mdev->current_image);
/* Configure camera. */
moticam_control_exposure(&mdev->device, 30.0);
moticam_control_gain(&mdev->device, mdev->gain);
- moticam_control_resolution(&mdev->device, mdev->image.width,
- mdev->image.height);
+ moticam_control_resolution(&mdev->device, mdev->width, mdev->height);
moticam_control_exposure(&mdev->device, mdev->exposure_ms);
mdev->exposure_pending = false;
mdev->gain_pending = false;
utils_delay_us(100000);
/* Prepare sizes and buffers, request an extra frame to read the zero
* length packet. */
- int image_size = mdev->image.width * mdev->image.height;
+ int image_size = mdev->width * mdev->height;
int frame_size = 16384;
mdev->rawbuffer_size = (image_size + frame_size)
/ frame_size * frame_size;
- mdev->rawbuffer = libusb_dev_mem_alloc(mdev->handle,
- mdev->rawbuffer_size);
- if (!mdev->rawbuffer)
- utils_fatal("can not allocate raw buffer");
- mdev->image.pixels = utils_malloc(
- mdev->image.stride * mdev->image.height);
+ for (int i = 0; i < 2; i++) {
+ struct image *image = &mdev->image[i];
+ image->width = mdev->width;
+ image->height = mdev->height;
+ image->stride = mdev->width;
+ image->format = IMAGE_FORMAT_SGRBG8;
+ image->pixels = libusb_dev_mem_alloc(mdev->handle,
+ mdev->rawbuffer_size);
+ if (!image->pixels)
+ utils_fatal("can not allocate raw buffer");
+ assert(image->refs == 0);
+ image->release = NULL;
+ image->release_user_data = NULL;
+ }
+ mdev->current_image = &mdev->image[0];
+ mdev->other_image = &mdev->image[1];
/* Prepare and submit transfer. */
mdev->usb_transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(mdev->usb_transfer, mdev->handle, 0x83,
- mdev->rawbuffer, mdev->rawbuffer_size, moticam_transfer_cb, mdev,
- 30000);
+ mdev->current_image->pixels, mdev->rawbuffer_size,
+ moticam_transfer_cb, mdev, 30000);
int r = libusb_submit_transfer(mdev->usb_transfer);
if (r)
utils_fatal("can not submit transfer: %s", libusb_strerror(r));
}
-static const struct device_image *
+static struct image *
moticam_read(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(mdev->image.pixels);
+ assert(mdev->current_image);
if (mdev->usb_transfer_done) {
if (mdev->usb_transfer->status == LIBUSB_TRANSFER_COMPLETED) {
- const int image_size = mdev->image.width * mdev->image.height;
- const struct device_image *image = NULL;
- /* Image received? Convert, else drop. */
+ const int image_size = mdev->width * mdev->height;
+ struct image *image = NULL;
+ /* Image received? Return it, else drop. */
if (mdev->usb_transfer->actual_length == image_size) {
- image_bayer2argb(mdev->rawbuffer, mdev->image.pixels,
- mdev->image.width, mdev->image.height,
- mdev->image.stride);
- image = &mdev->image;
+ image = mdev->current_image;
+ image->refs = 1;
+ mdev->current_image = mdev->other_image;
+ mdev->other_image = image;
}
/* Pending updates? */
if (mdev->exposure_pending) {
@@ -292,12 +299,14 @@ moticam_read(struct device *device)
mdev->gain_pending = false;
}
/* Start a new transfer. */
+ assert(mdev->current_image->refs == 0);
mdev->usb_transfer_done = false;
+ mdev->usb_transfer->buffer = mdev->current_image->pixels;
int r = libusb_submit_transfer(mdev->usb_transfer);
if (r)
utils_fatal("can not submit transfer: %s",
libusb_strerror(r));
- /* Return converted image or NULL. */
+ /* Return image or NULL. */
return image;
} else {
utils_fatal("transfer stopped: TODO");
@@ -311,7 +320,7 @@ static void
moticam_stop(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- assert(mdev->image.pixels);
+ assert(mdev->current_image);
/* Cancel transfer if running. */
if (!mdev->usb_transfer_done) {
libusb_cancel_transfer(mdev->usb_transfer);
@@ -321,10 +330,13 @@ moticam_stop(struct device *device)
/* Release. */
libusb_free_transfer(mdev->usb_transfer);
mdev->usb_transfer = NULL;
- libusb_dev_mem_free(mdev->handle, mdev->rawbuffer, mdev->rawbuffer_size);
- mdev->rawbuffer = NULL;
- free(mdev->image.pixels);
- mdev->image.pixels = NULL;
+ for (int i = 0; i < 2; i++) {
+ libusb_dev_mem_free(mdev->handle, mdev->image[i].pixels,
+ mdev->rawbuffer_size);
+ mdev->image[i].pixels = NULL;
+ }
+ mdev->current_image = NULL;
+ mdev->other_image = NULL;
/* Stop camera. */
moticam_control_exposure(device, 0.0);
moticam_control_exposure(device, 0.0);
@@ -335,7 +347,7 @@ static void
moticam_close(struct device *device)
{
struct moticam_device *mdev = (struct moticam_device *) device;
- if (mdev->image.pixels)
+ if (mdev->current_image)
moticam_stop(device);
libusb_close(mdev->handle);
free(device);
@@ -368,16 +380,18 @@ moticam_usb_open(libusb_context *usb, libusb_device *usb_device)
mdev->device.close = &moticam_close;
mdev->usb = usb;
mdev->handle = NULL;
+ mdev->width = 0;
+ mdev->height = 0;
mdev->exposure_ms = moticam_device_info.exposure_default_ms;
mdev->exposure_pending = false;
mdev->gain = moticam_device_info.gain_default;
mdev->gain_pending = false;
mdev->rawbuffer_size = 0;
- mdev->rawbuffer = NULL;
- mdev->image.width = 0;
- mdev->image.height = 0;
- mdev->image.stride = 0;
- mdev->image.pixels = NULL;
+ for (int i = 0; i < 2; i++) {
+ mdev->image[i].refs = 0;
+ }
+ mdev->current_image = NULL;
+ mdev->other_image = NULL;
mdev->usb_transfer = NULL;
mdev->usb_transfer_done = false;
/* Open USB device. */