summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2012-03-19 12:58:24 +0100
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2012-05-01 12:13:56 +0200
commit97036161f78b336d9778460335d8fedb42259e5f (patch)
tree85f295d940ddb8d1a205c4fd3fc84ecb62fbddc9
parentcecf2f690a16ad8a99d739f42b3bdb6745fafc07 (diff)
snapshot: Add JPEG compression support
Specifying the -j flag results in captured images being saved in JPEG format instead of raw YUYV. JPEG compression implies YUYV capture, software demosaicing isn't supported. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-rw-r--r--Makefile9
-rw-r--r--jpeg.c214
-rw-r--r--jpeg.h39
-rw-r--r--snapshot.c69
4 files changed, 321 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 107e34d..07d1973 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,13 @@ LIBS := -lomap3isp -lrt
OBJ_LIVE := live.o events.o iq.o videoout.o
OBJ_SNAP := snapshot.o events.o iq.o
+ifneq (x$(LIBJPEG), x)
+CFLAGS += -I$(LIBJPEG)/include -DUSE_LIBJPEG=1
+SNAP_LDFLAGS = -L$(LIBJPEG)/lib
+SNAP_LIBS = -ljpeg
+OBJ_SNAP += jpeg.o
+endif
+
%.o : %.c
$(CC) $(CFLAGS) -c -o $@ $<
@@ -18,7 +25,7 @@ live: $(OBJ_LIVE) isp/libomap3isp.so
$(CC) $(LDFLAGS) -o $@ $(OBJ_LIVE) $(LIBS)
snapshot: $(OBJ_SNAP) isp/libomap3isp.so
- $(CC) $(LDFLAGS) -o $@ $(OBJ_SNAP) $(LIBS)
+ $(CC) $(LDFLAGS) $(SNAP_LDFLAGS) -o $@ $(OBJ_SNAP) $(LIBS) $(SNAP_LIBS)
__isp:
$(MAKE) -C isp CROSS_COMPILE=$(CROSS_COMPILE) KDIR=$(KDIR)
diff --git a/jpeg.c b/jpeg.c
new file mode 100644
index 0000000..a7e1963
--- /dev/null
+++ b/jpeg.c
@@ -0,0 +1,214 @@
+/*
+ * OMAP3 ISP snapshot test application
+ *
+ * Copyright (C) 2012 Ideas on board SPRL
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _BSD_SOURCE
+#include <errno.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jpeglib.h>
+
+#include "isp/tools.h"
+#include "isp/v4l2-pool.h"
+
+#include "jpeg.h"
+
+struct jpeg {
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr error;
+ unsigned int width;
+ unsigned int height;
+ uint8_t *buffer;
+ jmp_buf jump;
+};
+
+static void jpeg_error_exit(j_common_ptr cinfo)
+{
+ struct jpeg *jpeg = container_of(cinfo, struct jpeg, cinfo);
+
+ longjmp(jpeg->jump, 1);
+}
+
+int jpeg_save(struct jpeg *jpeg, FILE *file, struct v4l2_video_buffer *buffer)
+{
+ JSAMPROW rows_y[DCTSIZE];
+ JSAMPROW rows_u[DCTSIZE];
+ JSAMPROW rows_v[DCTSIZE];
+ JSAMPARRAY image[3];
+ unsigned int width;
+ unsigned int height;
+ uint8_t *planes[3];
+ unsigned int x;
+ unsigned int y;
+ uint8_t *mem;
+ unsigned int i;
+
+ /* Convert the YUYV input buffer to a planar format for JPEG
+ * compression.
+ */
+ width = (jpeg->width + 2 * DCTSIZE - 1) / (2 * DCTSIZE) * (2 * DCTSIZE);
+ height = (jpeg->height + DCTSIZE - 1) / DCTSIZE * DCTSIZE;
+
+ planes[0] = jpeg->buffer;
+ planes[1] = jpeg->buffer + width * height;
+ planes[2] = jpeg->buffer + 3 * width / 2 * height;
+
+ mem = buffer->mem;
+
+ for (y = 0; y < jpeg->height; ++y) {
+ for (x = 0; x < jpeg->width; x += 2) {
+ planes[0][x] = *mem++;
+ planes[1][x/2] = *mem++;
+ planes[0][x+1] = *mem++;
+ planes[2][x/2] = *mem++;
+ }
+ planes[0] += width;
+ planes[1] += width / 2;
+ planes[2] += width / 2;
+ }
+
+ /* Now perform JPEG compression. */
+ if (setjmp(jpeg->jump))
+ return -EIO;
+
+ jpeg_stdio_dest(&jpeg->cinfo, file);
+ jpeg_start_compress(&jpeg->cinfo, TRUE);
+
+ rows_y[0] = jpeg->buffer;
+ rows_u[0] = jpeg->buffer + width * height;
+ rows_v[0] = jpeg->buffer + 3 * width / 2 * height;
+
+ for (i = 1; i < DCTSIZE; ++i) {
+ rows_y[i] = rows_y[i-1] + width;
+ rows_u[i] = rows_u[i-1] + width / 2;
+ rows_v[i] = rows_v[i-1] + width / 2;
+ }
+
+ image[0] = rows_y;
+ image[1] = rows_u;
+ image[2] = rows_v;
+
+ while (jpeg->cinfo.next_scanline < jpeg->height) {
+ unsigned int written;
+
+ written = jpeg_write_raw_data(&jpeg->cinfo, image, DCTSIZE);
+
+ for (i = 0; i < DCTSIZE; ++i) {
+ rows_y[i] += width * written;
+ rows_u[i] += width / 2 * written;
+ rows_v[i] += width / 2 * written;
+ }
+ }
+
+ jpeg_finish_compress(&jpeg->cinfo);
+
+ return 0;
+}
+
+static int jpeg_init_compressor(struct jpeg *jpeg)
+{
+ jpeg->cinfo.err = jpeg_std_error(&jpeg->error);
+ jpeg->error.error_exit = jpeg_error_exit;
+ if (setjmp(jpeg->jump)) {
+ jpeg_destroy_compress(&jpeg->cinfo);
+ jpeg->cinfo.err = NULL;
+ printf("Unable to create JPEG compression handler.\n");
+ return -EIO;
+ }
+
+ jpeg_create_compress(&jpeg->cinfo);
+
+ jpeg->cinfo.image_width = jpeg->width;
+ jpeg->cinfo.image_height = jpeg->height;
+ jpeg->cinfo.input_components = 3;
+ jpeg->cinfo.in_color_space = JCS_YCbCr;
+
+ jpeg_set_defaults(&jpeg->cinfo);
+ jpeg_set_quality(&jpeg->cinfo, 80, TRUE);
+
+ jpeg->cinfo.raw_data_in = TRUE;
+ jpeg_set_colorspace(&jpeg->cinfo, JCS_YCbCr);
+ jpeg->cinfo.comp_info[0].h_samp_factor = 2;
+ jpeg->cinfo.comp_info[0].v_samp_factor = 1;
+ jpeg->cinfo.comp_info[1].h_samp_factor = 1;
+ jpeg->cinfo.comp_info[1].v_samp_factor = 1;
+ jpeg->cinfo.comp_info[2].h_samp_factor = 1;
+ jpeg->cinfo.comp_info[2].v_samp_factor = 1;
+
+ return 0;
+}
+
+struct jpeg *jpeg_init(const struct v4l2_mbus_framefmt *format)
+{
+ unsigned int width;
+ unsigned int height;
+ struct jpeg *jpeg;
+
+ jpeg = malloc(sizeof *jpeg);
+ if (jpeg == NULL)
+ return NULL;
+
+ memset(jpeg, 0, sizeof *jpeg);
+ jpeg->width = format->width;
+ jpeg->height = format->height;
+
+ if (jpeg_init_compressor(jpeg) < 0) {
+ free(jpeg);
+ return NULL;
+ }
+
+ /* Allocate a temporary buffer to hold the planar YUV image.
+ *
+ * All planes must be padded to the DCT block size horizontally
+ * and vertically. The luma plane must thus be padded to twice
+ * the DCT block size horizontally as the chroma planes are
+ * subsampled by 2 in the horizontal direction. For simplicity,
+ * pad all planes to twice the DCT block size horizontally.
+ */
+ width = (format->width + 2 * DCTSIZE - 1) / (2 * DCTSIZE) * (2 * DCTSIZE);
+ height = (format->height + DCTSIZE - 1) / DCTSIZE * DCTSIZE;
+
+ jpeg->buffer = malloc(width * height * 2);
+ if (jpeg->buffer == NULL) {
+ jpeg_destroy_compress(&jpeg->cinfo);
+ jpeg->cinfo.err = NULL;
+ free(jpeg);
+ printf("Unable to allocate memory for JPEG compressor.\n");
+ return NULL;
+ }
+
+ return jpeg;
+}
+
+void jpeg_cleanup(struct jpeg *jpeg)
+{
+ if (jpeg->cinfo.err != NULL) {
+ jpeg_destroy_compress(&jpeg->cinfo);
+ jpeg->cinfo.err = NULL;
+ }
+ free(jpeg->buffer);
+ free(jpeg);
+}
diff --git a/jpeg.h b/jpeg.h
new file mode 100644
index 0000000..0d646d0
--- /dev/null
+++ b/jpeg.h
@@ -0,0 +1,39 @@
+/*
+ * OMAP3 ISP snapshot test application
+ *
+ * Copyright (C) 2012 Ideas on board SPRL
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __JPEG_H__
+#define __JPEG_H__
+
+#include <stdio.h>
+
+#include <linux/v4l2-mediabus.h>
+
+#include "isp/v4l2-pool.h"
+
+struct jpeg;
+
+struct jpeg *jpeg_init(const struct v4l2_mbus_framefmt *format);
+void jpeg_cleanup(struct jpeg *jpeg);
+
+int jpeg_save(struct jpeg *jpeg, FILE *file, struct v4l2_video_buffer *buffer);
+
+#endif
diff --git a/snapshot.c b/snapshot.c
index 958307e..5d29653 100644
--- a/snapshot.c
+++ b/snapshot.c
@@ -42,6 +42,11 @@
#include <linux/spi/spidev.h>
+#if USE_LIBJPEG
+#include <jpeglib.h>
+#include <setjmp.h>
+#endif
+
#include "isp/list.h"
#include "isp/omap3isp.h"
#include "isp/tools.h"
@@ -50,6 +55,7 @@
#include "events.h"
#include "iq.h"
+#include "jpeg.h"
#define MEDIA_DEVICE "/dev/media0"
@@ -86,6 +92,9 @@ struct snapshot {
struct timespec time;
enum snapshot_trigger_type trigger;
bool save;
+
+ bool jpeg_enable;
+ struct jpeg *jpeg;
};
static struct snapshot snap = {
@@ -151,8 +160,8 @@ static bool snapshot_process(struct omap3_isp_device *isp,
{
static struct timespec ts;
char name[33];
+ FILE *file;
int ret;
- int fd;
if (buffer->error) {
printf("warning: error in dequeued buffer, skipping\n");
@@ -172,14 +181,21 @@ static bool snapshot_process(struct omap3_isp_device *isp,
printf("frame captured in %lu.%06lus.\n", ts.tv_sec, ts.tv_nsec / 1000);
if (snap.save && snap.frame >= snap.skip) {
- sprintf(name, "snapshot-%06u-%02u.bin", snap.cycle, snap.frame);
- fd = open(name, O_WRONLY | O_CREAT,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
- if (fd == -1)
+ sprintf(name, "snapshot-%06u-%02u.%s", snap.cycle, snap.frame,
+#if USE_LIBJPEG
+ snap.jpeg ? "jpg" :
+#endif
+ "bin");
+ file = fopen(name, "wb");
+ if (file == NULL)
goto requeue;
-
- write(fd, buffer->mem, buffer->bytesused);
- close(fd);
+#if USE_LIBJPEG
+ if (snap.jpeg)
+ jpeg_save(snap.jpeg, file, buffer);
+ else
+#endif
+ fwrite(buffer->mem, buffer->bytesused, 1, file);
+ fclose(file);
}
requeue:
@@ -220,12 +236,30 @@ static int snapshot_init(struct omap3_isp_device *isp)
return ret;
}
+#if USE_LIBJPEG
+ if (snap.jpeg_enable) {
+ snap.jpeg = jpeg_init(&snap.format);
+ if (snap.jpeg == NULL) {
+ printf("Unable to initialize JPEG compressor.\n");
+ return -EIO;
+ }
+ }
+#endif
+
printf("snapshot configured for %04x %ux%u\n",
snap.format.code, snap.format.width, snap.format.height);
return 0;
}
+static void snapshot_cleanup(void)
+{
+#if USE_LIBJPEG
+ if (snap.jpeg != NULL)
+ jpeg_cleanup(snap.jpeg);
+#endif
+}
+
/* -----------------------------------------------------------------------------
* Viewfinder
*/
@@ -394,6 +428,9 @@ static void usage(const char *argv0)
printf("-f, --format fmt Snapshot format\n");
printf("-h, --help Show this help screen\n");
printf("-i, --interval n Capture a snapshot every n frames (default 20)\n");
+#if USE_LIBJPEG
+ printf("-j, --jpeg Save snapshots in JPEG format (implies YUV mode)\n");
+#endif
printf("-k, --skip n Skip n frames per snapshot when saving to disk (default 2)\n");
printf("-N, --snap-frames n Capture n frames per snapshot (default 3)\n");
printf("-n, --snap-number n Capture n snapshots (default 1)\n");
@@ -425,6 +462,9 @@ static struct option opts[] = {
{ "format", 1, 0, 'f' },
{ "help", 0, 0, 'h' },
{ "interval", 1, 0, 'i' },
+#if USE_LIBJPEG
+ { "jpeg", 0, 0, 'j' },
+#endif
{ "save", 0, 0, 'S' },
{ "size", 1, 0, 's' },
{ "snap-frames", 1, 0, 'N' },
@@ -451,7 +491,11 @@ int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unu
iq_params_init(&iq_params);
- while ((c = getopt_long(argc, argv, "c:f:hi:k:N:n:Ss:v", opts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "c:f:hi:"
+#if USE_LIBJPEG
+ "j"
+#endif
+ "k:N:n:Ss:v", opts, NULL)) != -1) {
switch (c) {
case 'c':
if (parse_crop(optarg, &snap.crop)) {
@@ -476,6 +520,12 @@ int main(int argc __attribute__((__unused__)), char *argv[] __attribute__((__unu
return 1;
}
break;
+#if USE_LIBJPEG
+ case 'j':
+ snap.jpeg_enable = true;
+ snap.format.code = V4L2_MBUS_FMT_YUYV8_1X16;
+ break;
+#endif
case 'k':
snap.skip = strtoul(optarg, NULL, 10);
break;
@@ -612,6 +662,7 @@ cleanup:
viewfinder_cleanup(isp);
if (iq)
iq_cleanup(iq);
+ snapshot_cleanup();
return exit_code;
}