From 97036161f78b336d9778460335d8fedb42259e5f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 19 Mar 2012 12:58:24 +0100 Subject: 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 --- Makefile | 9 ++- jpeg.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ jpeg.h | 39 +++++++++++ snapshot.c | 69 +++++++++++++++++--- 4 files changed, 321 insertions(+), 10 deletions(-) create mode 100644 jpeg.c create mode 100644 jpeg.h 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#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 + * + * 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 + +#include + +#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 +#if USE_LIBJPEG +#include +#include +#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; } -- cgit v1.2.3