lkml.org 
[lkml]   [2017]   [Apr]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    From
    SubjectRe: support autofocus / autogain in libv4l2
    Hi Pavel,

    Em Mon, 24 Apr 2017 11:30:59 +0200
    Pavel Machek <pavel@ucw.cz> escreveu:

    > Hi!
    >
    > For focus to be useful, we need autofocus implmented
    > somewhere. Unfortunately, v4l framework does not seem to provide good
    > place where to put autofocus. I believe, long-term, we'll need some
    > kind of "video server" providing this kind of services.
    >
    > Anyway, we probably don't want autofocus in kernel (even through some
    > cameras do it in hardware), and we probably don't want autofocus in
    > each and every user application.
    >
    > So what remains is libv4l2.

    IMO, the best place for autofocus is at libv4l2. Putting it on a
    separate "video server" application looks really weird for me.

    Btw, libv4l2 already has some autotools for auto gain and auto
    white balance. See the implementation under:
    lib/libv4lconvert/processing

    The libv4l internal controls can be seen at:
    lib/libv4lconvert/control/libv4lcontrol.h

    The ones implemented by the processing part of the library are:

    $ git grep v4lcontrol_get_ctrl lib/libv4lconvert/processing/
    lib/libv4lconvert/processing/autogain.c: autogain = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN);
    lib/libv4lconvert/processing/autogain.c: target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET);
    lib/libv4lconvert/processing/gamma.c: int gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA);
    lib/libv4lconvert/processing/gamma.c: gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA);
    lib/libv4lconvert/processing/whitebalance.c: wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE);

    I guess it shouldn't be hard to add an extra processing module
    there for auto-focus.

    > Now, this is in no way clean or complete,
    > and functionality provided by sdl.c and asciicam.c probably _should_
    > be in application, but... I'd like to get the code out there.
    >
    > Oh and yes, I've canibalized decode_tm6000.c application instead of
    > introducing my own. Autotools scare me, sorry.

    Why replace decode_tm6000.c by something else? If you want to add another
    test application, just place it on a new file.

    I added a few notes together with the code, pointing the main things
    I think it require changes, in order for me to do a better review
    at the code. I didn't test nor tried to check the algorithms inside,
    as the code, on its current state, requires rework and code cleanup.

    >
    > Regards,
    > Pavel
    >
    > diff --git a/lib/libv4l2/asciicam.c b/lib/libv4l2/asciicam.c
    > new file mode 100644
    > index 0000000..5388967
    > --- /dev/null
    > +++ b/lib/libv4l2/asciicam.c
    > @@ -0,0 +1,63 @@
    > +/* gcc asciicam.c /usr/lib/i386-linux-gnu/libv4l2.so.0.0.0 -o asciicam
    > + gcc asciicam.c /usr/lib/arm-linux-gnueabi/libv4l2.so.0 -o asciicam
    > +
    > +gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I../.. -fvisibility=hidden -I../../lib/include -Wall -Wpointer-arith -D_GNU_SOURCE -I../../include -g -O2 asciicam.c libv4l2.c /usr/lib/arm-linux-gnueabi/libv4lconvert.so.0 log.c v4l2convert.c v4l2-plugin.c -o asciicam
    > + */
    > +#include <stdio.h>
    > +#include <sys/types.h>
    > +#include <sys/stat.h>
    > +#include <fcntl.h>
    > +
    > +#include <linux/videodev2.h>
    > +
    > +#define SIZE 10*1024*1024
    > +
    > +char buf[SIZE];
    > +
    > +void main(void)

    Please don't add a new application under lib/. It is fine if you want
    some testing application, if the ones there aren't enough, but please
    place it under contrib/test/.

    You should likely take a look at v4l2grab first, as it could have
    almost everything you would need.

    > +{
    > + int fd = v4l2_open("/dev/video2", O_RDWR);

    Hardcoding /dev/video2 doesn't seem a good idea.

    > + int i;
    > + static struct v4l2_format fmt;
    > +
    > +#if 1
    > + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    > + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
    > + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    > + fmt.fmt.pix.width = 640;
    > + fmt.fmt.pix.height = 480;
    > + if (fmt.fmt.pix.pixelformat != 'RGB3') /* v4l2_fourcc('R', 'G', 'B', '3'); */
    > + printf("hmm. strange format?\n");
    > +
    > + printf("ioctl = %d\n", v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt));
    > +#endif
    > +
    > + for (i=0; i<500; i++) {
    > + int num = v4l2_read(fd, buf, SIZE);
    > + int y,x;
    > +
    > + printf("%d\n", num);
    > +#if 0
    > + for (y = 0; y < 25; y++) {
    > + for (x = 0; x < 80; x++) {
    > + int y1 = y * 480/25;
    > + int x1 = x * 640/80;
    > + int c = buf[fmt.fmt.pix.width*3*y1 + 3*x1] +
    > + buf[fmt.fmt.pix.width*3*y1 + 3*x1 + 1] +
    > + buf[fmt.fmt.pix.width*3*y1 + 3*x1 + 2];
    > +
    > + if (c < 30) c = ' ';
    > + else if (c < 60) c = '.';
    > + else if (c < 120) c = '_';
    > + else if (c < 180) c = 'o';
    > + else if (c < 300) c = 'x';
    > + else if (c < 400) c = 'X';
    > + else c = '#';
    > +
    > + printf("%c", c);
    > + }
    > + printf("\n");
    > + }

    IMHO, it would be better to use aalib. Btw, xawtv3 has a code example
    using it, under:
    console/ttv.c

    As it already uses libv4l, prhaps you could use it, instead of adding
    a new ascii app.

    > +#endif
    > + }
    > +}
    > diff --git a/lib/libv4l2/libv4l2-priv.h b/lib/libv4l2/libv4l2-priv.h
    > index 343db5e..af740a7 100644
    > --- a/lib/libv4l2/libv4l2-priv.h
    > +++ b/lib/libv4l2/libv4l2-priv.h
    > @@ -1,3 +1,4 @@
    > +/* -*- c-file-style: "linux" -*- */
    > /*
    > # (C) 2008 Hans de Goede <hdegoede@redhat.com>
    >
    > @@ -70,6 +71,14 @@
    > } while (0)
    >
    > #define MIN(a, b) (((a) < (b)) ? (a) : (b))
    > +#define V4L2_MAX_SUBDEVS 16
    > +
    > +#define V4L2_MAX_FOCUS 10
    > +struct v4l2_focus_step {
    > + int num;
    > + int sharpness[V4L2_MAX_FOCUS];
    > + int position[V4L2_MAX_FOCUS];
    > +};
    >
    > struct v4l2_dev_info {
    > int fd;
    > @@ -104,6 +113,14 @@ struct v4l2_dev_info {
    > void *plugin_library;
    > void *dev_ops_priv;
    > const struct libv4l_dev_ops *dev_ops;
    > + int subdev_fds[V4L2_MAX_SUBDEVS];
    > + int exposure;
    > + int frame;
    > + int focus;
    > +
    > + /* Autofocus parameters */
    > + int focus_frame, focus_exposure, focus_step, sh_prev;
    > + struct v4l2_focus_step focus_step_params;
    > };
    >
    > /* From v4l2-plugin.c */
    > diff --git a/lib/libv4l2/libv4l2.c b/lib/libv4l2/libv4l2.c
    > index 0ba0a88..3b84437 100644
    > --- a/lib/libv4l2/libv4l2.c
    > +++ b/lib/libv4l2/libv4l2.c
    > @@ -1,3 +1,4 @@
    > +/* -*- c-file-style: "linux" -*- */

    Don't add editor-specific macros inside the code. Not everybody uses emacs.

    > /*
    > # (C) 2008 Hans de Goede <hdegoede@redhat.com>
    >
    > @@ -100,6 +101,13 @@ static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = {
    > };
    > static int devices_used;
    >
    > +#include "sdl.c"
    > +
    > +static struct sdl sdl;
    > +
    > +int v4l2_get_index(int fd);
    > +void my_main(void);
    > +

    The above looks really odd. Why do you want to make libv4l2 dependent
    on sdl?

    > static int v4l2_ensure_convert_mmap_buf(int index)
    > {
    > if (devices[index].convert_mmap_buf != MAP_FAILED) {
    > @@ -311,7 +319,409 @@ static int v4l2_queue_read_buffer(int index, int buffer_index)
    > return 0;
    > }
    >
    > -static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
    > +static void v4l2_paint(char *buf, int x1, int y1, const struct v4l2_format *fmt)
    > +{
    > + int y, x;
    > + int width = fmt->fmt.pix.width;
    > + printf("width = %d\n", width);
    > +
    > + for (y = 0; y < 25; y++) {
    > + for (x = 0; x < 80; x++) {
    > + int c = buf[width*4*y1 + 4*x1] +
    > + buf[width*4*y1 + 4*x1 + 1] +
    > + buf[width*4*y1 + 4*x1 + 2];
    > +
    > + if (c < 30) c = ' ';
    > + else if (c < 60) c = '.';
    > + else if (c < 120) c = '_';
    > + else if (c < 180) c = 'o';
    > + else if (c < 300) c = 'x';
    > + else if (c < 400) c = 'X';
    > + else c = '#';
    > +
    > + printf("%c", c);
    > + }
    > + printf("\n");
    > + }
    > +}

    Please don't add anything like that inside the library.

    > +
    > +#define SX 80
    > +#define SY 25
    > +#define BUCKETS 20
    > +
    > +static void v4l2_histogram(unsigned char *buf, int cdf[], struct v4l2_format *fmt)
    > +{
    > + for (int y = 0; y < fmt->fmt.pix.height; y+=19)
    > + for (int x = 0; x < fmt->fmt.pix.width; x+=19) {
    > + pixel p = buf_pixel(fmt, buf, x, y);
    > +
    > + int b;
    > + /* HACK: we divide green by 2 to have nice picture, undo it here. */
    > + b = p.r + 2*p.g + p.b;
    > + b = (b * BUCKETS)/(256);
    > + cdf[b]++;
    > + }
    > +}
    > +
    > +static long v4l2_sharpness(unsigned char *buf, struct v4l2_format *fmt)
    > +{
    > + int h = fmt->fmt.pix.height;
    > + int w = fmt->fmt.pix.width;
    > + long r = 0;
    > +
    > + for (int y = h/3; y < h-h/3; y+=h/9)
    > + for (int x = w/3; x < w-w/3; x++) {
    > + pixel p1 = buf_pixel(fmt, buf, x, y);
    > + pixel p2 = buf_pixel(fmt, buf, x+2, y);
    > +
    > + int b1, b2;
    > + /* HACK: we divide green by 2 to have nice picture, undo it here. */
    > + b1 = p1.r + 2*p1.g + p1.b;
    > + b2 = p2.r + 2*p2.g + p2.b;
    > +
    > + int v;
    > + v = (b1-b2)*(b1-b2);
    > + if (v > 36)
    > + r+=v;
    > + }
    > +
    > + return r;
    > +}

    IMO, the above belongs to a separate processing module under
    lib/libv4lconvert/processing/

    > +
    > +int v4l2_set_exposure(int fd, int exposure)
    > +{
    > + int index = v4l2_get_index(fd);
    > +
    > + if (index == -1 || devices[index].convert == NULL) {
    > + V4L2_LOG_ERR("v4l2_set_exposure called with invalid fd: %d\n", fd);
    > + errno = EBADF;
    > + return -1;
    > + }
    > +
    > + struct v4l2_control ctrl;
    > + ctrl.id = V4L2_CID_EXPOSURE;
    > + ctrl.value = exposure;
    > + if (ioctl(devices[index].subdev_fds[0], VIDIOC_S_CTRL, &ctrl) < 0) {
    > + printf("Could not set exposure\n");
    > + }
    > + return 0;
    > +}

    Shouldn't it be together with lib/libv4lconvert/processing/autogain.c,
    perhaps as an alternative implementation, if what's there is not enough?

    > +
    > +int v4l2_set_gain(int fd, int gain)
    > +{
    > + int index = v4l2_get_index(fd);
    > +
    > + if (index == -1 || devices[index].convert == NULL) {
    > + V4L2_LOG_ERR("v4l2_set_exposure called with invalid fd: %d\n", fd);
    > + errno = EBADF;
    > + return -1;
    > + }
    > +
    > + struct v4l2_control ctrl;
    > + ctrl.id = 0x00980913;

    Don't hardcode control numbers here.

    > + ctrl.value = gain;
    > + if (ioctl(devices[index].subdev_fds[0], VIDIOC_S_CTRL, &ctrl) < 0) {
    > + printf("Could not set exposure\n");
    > + }
    > + return 0;
    > +}
    > +
    > +int v4l2_set_focus(int fd, int diopt)
    > +{
    > + int index = v4l2_get_index(fd);
    > +
    > + if (index == -1 || devices[index].convert == NULL) {
    > + V4L2_LOG_ERR("v4l2_set_focus called with invalid fd: %d\n", fd);
    > + errno = EBADF;
    > + return -1;
    > + }
    > +
    > + struct v4l2_control ctrl;
    > + ctrl.id = V4L2_CID_FOCUS_ABSOLUTE;
    > + ctrl.value = diopt;
    > + if (ioctl(devices[index].subdev_fds[1], VIDIOC_S_CTRL, &ctrl) < 0) {
    > + printf("Could not set focus\n");
    > + }
    > + return 0;
    > +}
    > +
    > +#define LESS 1
    > +#define MID 0
    > +#define MORE 2
    > +
    > +static void v4l2_start_focus_step(struct v4l2_focus_step *fs, int focus, int step)
    > +{
    > + int i;
    > +
    > + fs->num = 3;
    > + for (i=0; i<V4L2_MAX_FOCUS; i++) {
    > + fs->sharpness[i] = -1;
    > + fs->position[i] = -1;
    > + }
    > +
    > + fs->position[LESS] = focus - step;
    > + fs->position[MID] = focus;
    > + fs->position[MORE] = focus + step;
    > +}
    > +
    > +static int v4l2_focus_get_best(struct v4l2_focus_step *fs)
    > +{
    > + int i, max = -1, maxi = -1;
    > +
    > + for (i=0; i<fs->num; i++)
    > + if (max < fs->sharpness[i]) {
    > + max = fs->sharpness[i];
    > + maxi = i;
    > + }
    > +
    > + return maxi;
    > +}
    > +
    > +static void v4l2_auto_focus_continuous(struct v4l2_dev_info *m, int sh)
    > +{
    > + int f = m->frame - m->focus_frame;
    > + int step;
    > + const int max_step = 300, min_step = 20;
    > + struct v4l2_focus_step *fs = &m->focus_step_params;
    > +
    > + if (m->focus_step == 0 || m->focus_step > max_step) {
    > + printf("step reset -- max\n");
    > + m->focus_step = max_step;
    > + }
    > + if (m->focus_step < min_step) {
    > + printf("step reset -- 10 (%d)\n", m->focus_step);
    > + m->focus_step = min_step;
    > + /* It takes cca 5.7 seconds to achieve the focus:
    > + 0.76user 0.30system 5.66 (0m5.661s) elapsed 18.72%CPU
    > + */

    The above note seems hardware dependent.

    > + printf("Focused at %d\n", m->focus);
    > + exit(0);
    > + }
    > +
    > + step = m->focus_step;
    > +
    > + if (m->exposure != m->focus_exposure) {
    > + m->focus_frame = m->frame;
    > + m->focus_exposure = m->exposure;
    > + v4l2_start_focus_step(fs, m->focus, m->focus_step);
    > + return;
    > + }
    > + if (m->focus < step) {
    > + m->focus = step;
    > + }
    > +
    > + const int every = 3;
    > + if (f%every)
    > + return;
    > +
    > + {
    > + int i = f/every;
    > +
    > + if (i == 0) {
    > + printf("Can not happen?\n");
    > + return;
    > + }
    > + i--;
    > + if (i < fs->num)
    > + v4l2_set_focus(m->fd, fs->position[i]);
    > + if (i > 0)
    > + fs->sharpness[i-1] = sh;
    > + if (i < fs->num)
    > + return;
    > + }
    > + int i;
    > + for (i=0; i<fs->num; i++) {
    > + printf("%d: %d | ", fs->position[i], fs->sharpness[i]);
    > + }

    You should probably print something only if log is enabled. Take a look
    at lib/libv4l2/log.c. Same applies to similar printf() calls.

    > + int best = v4l2_focus_get_best(fs);
    > + if ((fs->sharpness[best] < m->sh_prev) && (m->focus_step < max_step)) {
    > + m->focus_step *=2;
    > + m->sh_prev = m->sh_prev * 0.9;
    > + printf("step up %d\n", m->focus_step);
    > + } else if (best == LESS) {
    > + printf("less ");
    > + m->focus -= step;
    > + } else if (best == MORE) {
    > + printf("more ");
    > + m->focus += step;
    > + } else {
    > + m->sh_prev = fs->sharpness[MID];
    > + m->focus_step = m->focus_step / 2;
    > + printf("step %d ", m->focus_step);
    > + }
    > + m->focus_frame = m->frame;
    > + v4l2_start_focus_step(fs, m->focus, m->focus_step);
    > + printf("Focus now %d\n", m->focus);
    > +}
    > +
    > +static void v4l2_start_focus_sweep(struct v4l2_focus_step *fs, int focus, int step)
    > +{
    > + int i;
    > +
    > + fs->num = V4L2_MAX_FOCUS;
    > + for (i=0; i<V4L2_MAX_FOCUS; i++) {
    > + fs->sharpness[i] = -1;
    > + fs->position[i] = -1;
    > + }
    > +
    > + int f = focus;
    > + for (i=0; i<V4L2_MAX_FOCUS; i++) {
    > + fs->position[i] = f;
    > + f += step;
    > + }
    > +}
    > +
    > +static void v4l2_auto_focus_single(struct v4l2_dev_info *m, int sh)
    > +{
    > + int f = m->frame - m->focus_frame;
    > + int step;
    > + struct v4l2_focus_step *fs = &m->focus_step_params;
    > +
    > + if (m->focus_step == 0) {
    > + printf("step reset -- max\n");
    > + m->focus_step = 1;
    > + }
    > +
    > + if (m->exposure != m->focus_exposure) {
    > + m->focus_frame = m->frame;
    > + m->focus_exposure = m->exposure;
    > + v4l2_start_focus_sweep(fs, 0, 100);
    > + return;
    > + }
    > +
    > + const int every = 3;
    > + if (f%every)
    > + return;
    > +
    > + {
    > + int i = f/every;
    > +
    > + if (i == 0) {
    > + printf("Can not happen?\n");
    > + return;
    > + }
    > + i--;
    > + if (i < fs->num)
    > + v4l2_set_focus(m->fd, fs->position[i]);
    > + if (i > 0)
    > + fs->sharpness[i-1] = sh;
    > + if (i < fs->num)
    > + return;
    > + }
    > +#if 0
    > + int i;
    > + for (i=0; i<fs->num; i++) {
    > + printf("%d: %d | ", fs->position[i], fs->sharpness[i]);
    > + }
    > +#endif
    > + int best = v4l2_focus_get_best(fs);
    > + m->focus_frame = m->frame;
    > + switch (m->focus_step) {
    > + case 1:
    > + printf("Best now %d / %d\n", fs->position[best], fs->sharpness[best]);
    > + v4l2_start_focus_sweep(fs, fs->position[best] - 50, 10);
    > + m->focus_step = 2;
    > + break;
    > + case 2:
    > + printf("Best now %d / %d\n", fs->position[best], fs->sharpness[best]);
    > + v4l2_start_focus_sweep(fs, fs->position[best] - 10, 2);
    > + m->focus_step = 3;
    > + break;
    > + case 3:
    > + printf("Best now %d / %d\n", fs->position[best], fs->sharpness[best]);
    > + printf("done.\n");
    > + exit(0);
    > + }
    > +}
    > +
    > +
    > +static void v4l2_auto_exposure(int index, struct v4l2_buffer *buf)
    > +{
    > + struct v4l2_format *fmt;
    > + int cdf[BUCKETS] = { 0, };
    > + int i;
    > +
    > + fmt = &devices[index].src_fmt;
    > +
    > + v4l2_histogram(devices[index].frame_pointers[buf->index], cdf, fmt);
    > +
    > +#if 0
    > + printf("hist: ");
    > + for (i = 0; i<BUCKETS; i++)
    > + printf("%d ", cdf[i]);
    > + printf("\n");
    > +#endif
    > + for (i=1; i<BUCKETS; i++)
    > + cdf[i] += cdf[i-1];
    > +
    > + int b = BUCKETS;
    > + int brightPixels = cdf[b-1] - cdf[b-8];
    > + int targetBrightPixels = cdf[b-1]/50;
    > + int maxSaturatedPixels = cdf[b-1]/200;
    > + int saturatedPixels = cdf[b-1] - cdf[b-2];
    > + // how much should I change brightness by
    > + float adjustment = 1.0f;
    > +#if 0
    > + printf( "AutoExposure: totalPixels: %d,"
    > + "brightPixels: %d, targetBrightPixels: %d,"
    > + "saturatedPixels: %d, maxSaturatedPixels: %d\n",
    > + cdf[b-1], brightPixels, targetBrightPixels,
    > + saturatedPixels, maxSaturatedPixels);
    > +#endif
    > +
    > + if (saturatedPixels > maxSaturatedPixels) {
    > + // first don't let things saturate too much
    > + adjustment = 1.0f - ((float)(saturatedPixels - maxSaturatedPixels))/cdf[b-1];
    > + } else if (brightPixels < (targetBrightPixels - (saturatedPixels * 4))) {
    > + // increase brightness to try and hit the desired number of well exposed pixels
    > + int l = b-6;
    > + while (brightPixels < targetBrightPixels && l > 0) {
    > + brightPixels += cdf[l];
    > + brightPixels -= cdf[l-1];
    > + l--;
    > + }
    > +
    > + // that level is supposed to be at b-11;
    > + adjustment = ((float) (b-6+1))/(l+1);
    > + } else {
    > + // we're not oversaturated, and we have enough bright pixels. Do nothing.
    > + }
    > +
    > + {
    > + float limit = 4;
    > + if (adjustment > limit) { adjustment = limit; }
    > + if (adjustment < 1/limit) { adjustment = 1/limit; }
    > + }
    > +
    > + if (!devices[index].exposure)
    > + devices[index].exposure = 1;
    > + devices[index].exposure *= adjustment;
    > + if (adjustment != 1.)
    > + printf( "AutoExposure: adjustment: %f exposure %d\n", adjustment, devices[index].exposure);
    > +
    > + v4l2_set_exposure(devices[index].fd, devices[index].exposure);
    > +}
    > +
    > +static void v4l2_statistics(int index, struct v4l2_buffer *buf)
    > +{
    > + unsigned char *b;
    > + struct v4l2_format *fmt;
    > +
    > + fmt = &devices[index].src_fmt;
    > + b = devices[index].frame_pointers[buf->index];
    > +
    > + if (!(devices[index].frame%3))
    > + v4l2_auto_exposure(index, buf);
    > +
    > + int sh = v4l2_sharpness(b, fmt);
    > + v4l2_auto_focus_single(&devices[index], sh);
    > +
    > + devices[index].frame++;
    > + if (!(devices[index].frame%4))
    > + sdl_render(&sdl, b, fmt);
    > +}
    > +
    > +int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
    > unsigned char *dest, int dest_size)
    > {
    > const int max_tries = V4L2_IGNORE_FIRST_FRAME_ERRORS + 1;
    > @@ -345,6 +755,13 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
    > errno = -EINVAL;
    > return -1;
    > }
    > +
    > +#if 1
    > + v4l2_statistics(index, buf);
    > +#endif
    > +#if 0
    > + /* This is rather major eater of CPU time. CPU time goes from 80% to 4%
    > + when conversion is disabled. */
    >
    > result = v4lconvert_convert(devices[index].convert,
    > &devices[index].src_fmt, &devices[index].dest_fmt,
    > @@ -352,7 +769,7 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf,
    > buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf +
    > buf->index * devices[index].convert_mmap_frame_size),
    > dest_size);
    > -
    > +#endif
    > if (devices[index].first_frame) {
    > /* Always treat convert errors as EAGAIN during the first few frames, as
    > some cams produce bad frames at the start of the stream
    > @@ -789,18 +1206,24 @@ no_capture:
    >
    > /* Note we always tell v4lconvert to optimize src fmt selection for
    > our default fps, the only exception is the app explicitly selecting
    > - a fram erate using the S_PARM ioctl after a S_FMT */
    > + a frame rate using the S_PARM ioctl after a S_FMT */
    > if (devices[index].convert)
    > v4lconvert_set_fps(devices[index].convert, V4L2_DEFAULT_FPS);
    > v4l2_update_fps(index, &parm);
    >
    > + devices[index].subdev_fds[0] = SYS_OPEN("/dev/video_sensor", O_RDWR, 0);
    > + devices[index].subdev_fds[1] = SYS_OPEN("/dev/video_focus", O_RDWR, 0);
    > +
    > + printf("Sensor: %d, focus: %d\n", devices[index].subdev_fds[0],
    > + devices[index].subdev_fds[1]);
    > +
    > V4L2_LOG("open: %d\n", fd);
    >
    > return fd;
    > }
    >
    > /* Is this an fd for which we are emulating v4l1 ? */
    > -static int v4l2_get_index(int fd)
    > +int v4l2_get_index(int fd)
    > {
    > int index;
    >
    > @@ -823,6 +1246,10 @@ int v4l2_close(int fd)
    > {
    > int index, result;
    >
    > + if (fd == -2) {
    > + my_main();
    > + }
    > +

    That looks a hack!

    > index = v4l2_get_index(fd);
    > if (index == -1)
    > return SYS_CLOSE(fd);
    > @@ -1782,3 +2209,65 @@ int v4l2_get_control(int fd, int cid)
    > (qctrl.maximum - qctrl.minimum) / 2) /
    > (qctrl.maximum - qctrl.minimum);
    > }
    > +
    > +void v4l2_debug(void)
    > +{
    > + printf("debug\n");
    > +}
    > +
    > +/* ------------------------------------------------------------------ */
    > +
    > +#define SIZE 10*1024*1024
    > +
    > +char buf[SIZE];
    > +
    > +void my_main(void)
    > +{
    > + int fd = v4l2_open("/dev/video2", O_RDWR);
    > + int i;
    > + static struct v4l2_format fmt;
    > +
    > + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    > + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
    > + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    > + fmt.fmt.pix.width = 640;
    > + fmt.fmt.pix.height = 480;
    > +
    > + v4l2_set_gain(fd, 300);
    > + v4l2_set_exposure(fd, 10000);
    > + v4l2_set_focus(fd, 0);
    > +
    > +
    > + printf("ioctl = %d\n", v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt));
    > +
    > + printf("capture is %d, %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    > + /* factor == 2 still fits window, but very slow. factor == 6 .. */
    > + /* factor needs to be odd, otherwise ... fun with BA10 format. */
    > + sdl_init(&sdl, fmt.fmt.pix.width, fmt.fmt.pix.height, 5);
    > +
    > + v4l2_debug();
    > +
    > + /* In 800x600 "raw" mode, this should take cca 17.8 seconds (without
    > + sdl output. CPU usage should be cca 5% without conversion). That's 28 fps.
    > + (benchmark with i<500)
    > + */
    > + for (i=0; i<50000; i++) {
    > + int num = v4l2_read(fd, buf, SIZE);
    > +
    > + if (i==490) {
    > + printf("Focus to closest.... -------------------\n");
    > + v4l2_set_focus(fd, 99999);
    > + }
    > +
    > +#if 0
    > + v4l2_paint(buf, 640/80, 480/25, &fmt);
    > +#endif
    > + /* Over USB connection, rendering every single frame slows
    > + execution down from 23 seconds to 36 seconds. */
    > +#if 0
    > + if (!(i%4))
    > + sdl_render(&sdl, buf, &fmt);
    > +#endif
    > + }
    > +
    > +}
    > diff --git a/lib/libv4l2/sdl.c b/lib/libv4l2/sdl.c
    > new file mode 100644
    > index 0000000..17a8d24
    > --- /dev/null
    > +++ b/lib/libv4l2/sdl.c
    > @@ -0,0 +1,530 @@
    > +/* -*- c-file-style: "linux" -*- */
    > +/* SDL support.
    > +
    > + Copyright 2017 Pavel Machek, LGPL
    > +*/
    > +
    > +#include <SDL2/SDL.h>
    > +#include <SDL2/SDL_image.h>

    If you're adding a SDL-specific application, you'll need to add the
    needed autoconf bits to detect if SDL devel package is installed,
    auto-disabling it if not.

    Yet, I don't think that SDL should be part of the library, but,
    instead, part of some application.

    > +
    > +struct sdl {
    > + SDL_Window *window;
    > + SDL_Surface *liveview, *screen;
    > +
    > + int wx, wy;
    > + int sx, sy;
    > + int bx, by;
    > + int factor;
    > + float focus, gain, exposure, do_focus, do_exposure;
    > +};
    > +
    > +#if 0
    > +void loop(void) {
    > + int done;
    > + SDL_Event event;
    > +
    > + while(!done){ //While program isn't done
    > + while(SDL_PollEvent(&event)){ //Poll events
    > + switch(event.type){ //Check event type
    > + case SDL_QUIT: //User hit the X (or equivelent)
    > + done = true; //Make the loop end
    > + break; //We handled the event
    > + } //Finished with current event
    > + } //Done with all events for now
    > + } //Program done, exited
    > +}
    > +#endif
    > +
    > +typedef struct {
    > + Uint8 r;
    > + Uint8 g;
    > + Uint8 b;
    > + Uint8 alpha;
    > +} pixel;
    > +
    > +#define d_raw 1
    > +
    > +void sfc_put_pixel(SDL_Surface* liveview, int x, int y, pixel *p)
    > +{
    > + Uint32* p_liveview = (Uint32*)liveview->pixels;
    > + p_liveview += y*liveview->w+x;
    > + *p_liveview = SDL_MapRGBA(liveview->format,p->r,p->g,p->b,p->alpha);
    > +}
    > +
    > +#if 0
    > +int pix_exposure(float x)
    > +{
    > + return sy*x / 199410.0;
    > +}
    > +
    > +int pix_gain(float x)
    > +{
    > + return sy*x / 16.0;
    > +}
    > +
    > +int render_statistics(SDL_Surface* liveview)
    > +{
    > + pixel white;
    > + white.r = (Uint8)0xff;
    > + white.g = (Uint8)0xff;
    > + white.b = (Uint8)0xff;
    > + white.alpha = (Uint8)128;
    > +
    > + //printf("Stats: focus %d, gain %d, exposure %d\n", focus, gain, exposure);
    > +
    > + for (int x=0; x<sx && x<sx*focus; x++)
    > + put_pixel(liveview, x, 0, &white);
    > +
    > + for (int y=0; y<sy && y<pix_gain(gain); y++)
    > + put_pixel(liveview, 0, y, &white);
    > +
    > + for (int y=0; y<sy && y<pix_exposure(exposure); y++)
    > + put_pixel(liveview, sx-1, y, &white);
    > +
    > + for (int x=0; x<sx; x++)
    > + put_pixel(liveview, x, sy-1, &white);
    > +
    > + return 0;
    > +}
    > +#endif
    > +
    > +void sdl_begin_paint(struct sdl *m) {
    > + //Fill the surface white
    > + SDL_FillRect(m->liveview, NULL, SDL_MapRGB( m->liveview->format, 0, 0, 0 ));
    > +
    > + SDL_LockSurface(m->liveview);
    > +}
    > +
    > +void sdl_finish_paint(struct sdl *m) {
    > + SDL_UnlockSurface(m->liveview);
    > + SDL_Rect rcDest = { m->bx, m->by, m->sx, m->sy };
    > +
    > + SDL_BlitSurface(m->liveview, NULL, m->screen, &rcDest);
    > + //Update the surface
    > + SDL_UpdateWindowSurfaceRects(m->window, &rcDest, 1);
    > +}
    > +
    > +void sdl_paint_image(struct sdl *m, char **xpm, int x, int y) {
    > + SDL_Surface *image = IMG_ReadXPMFromArray(xpm);
    > + if (!image) {
    > + printf("IMG_Load: %s\n", IMG_GetError());
    > + exit(1);
    > + }
    > +
    > + int x_pos = x - image->w/2, y_pos = y - image->h/2;
    > +
    > + SDL_Rect rcDest = { x_pos, y_pos, image->w, image->h };
    > + int r = SDL_BlitSurface ( image, NULL, m->screen, &rcDest );
    > +
    > + if (r) {
    > + printf("Error blitting: %s\n", SDL_GetError());
    > + exit(1);
    > + }
    > + SDL_FreeSurface ( image );
    > +}
    > +
    > +void sdl_paint_ui(struct sdl *m) {
    > + static char *wait_xpm[] = {
    > + "16 9 2 1",
    > + "# c #ffffff",
    > + ". c #000000",
    > + "....########....",
    > + ".....#....#.....",
    > + ".....#....#.....",
    > + "......#..#......",
    > + ".......##.......",
    > + "......#..#......",
    > + ".....#....#.....",
    > + ".....#....#.....",
    > + "....########....",
    > + };
    > +
    > + static char *ok_xpm[] = {
    > + "16 9 2 1",
    > + "# c #ffffff",
    > + ". c #000000",
    > + "...............#",
    > + "............###.",
    > + "..........##....",
    > + "#.......##......",
    > + ".#.....#........",
    > + "..#...#.........",
    > + "..#..#..........",
    > + "...##...........",
    > + "...#............",
    > + };
    > +
    > + static char *exit_xpm[] = {
    > + "16 9 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + "....x......x....",
    > + ".....x....x.....",
    > + "......x..x......",
    > + ".......xx.......",
    > + ".......xx.......",
    > + "......x..x......",
    > + ".....x....x.....",
    > + "....x......x....",
    > + "................",
    > + };
    > +
    > + static char *f1m_xpm[] = {
    > + "16 9 2 1",
    > + "# c #ffffff",
    > + ". c #000000",
    > + "....##..........",
    > + "...#.#..........",
    > + "..#..#..........",
    > + ".....#...#.#.##.",
    > + ".....#...##.#..#",
    > + ".....#...#..#..#",
    > + ".....#...#..#..#",
    > + ".....#...#..#..#",
    > + "................",
    > + };
    > +
    > + static char *f25cm_xpm[] = {
    > + "16 9 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + ".xxx..xxxx......",
    > + "x...x.x.........",
    > + "...x..xxx.......",
    > + "..x......x..xx.x",
    > + ".x.......x.x.xxx",
    > + "xxxxx.xxx...xxxx",
    > + "................",
    > + "................",
    > + "................",
    > + };
    > +
    > + static char *iso400_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + "x..x.xxxx.xxxx..",
    > + "x..x.x..x.x..x..",
    > + "xxxx.x..x.x..x..",
    > + "...x.x..x.x..x..",
    > + "...x.xxxx.xxxx..",
    > + "................",
    > + ".x..xx..x.......",
    > + ".x.x...x.x......",
    > + ".x..x..x.x......",
    > + ".x...x.x.x......",
    > + ".x.xx...x.......",
    > + "................",
    > + };
    > +
    > + static char *time_1_100_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + ".x....x.........",
    > + ".x...x..........",
    > + ".x..x...........",
    > + ".x.x............",
    > + "................",
    > + "..x.xxxx.xxxx...",
    > + "..x.x..x.x..x...",
    > + "..x.x..x.x..x...",
    > + "..x.x..x.x..x...",
    > + "..x.x..x.x..x...",
    > + "..x.xxxx.xxxx...",
    > + "................",
    > + };
    > +
    > + static char *time_1_10_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + ".x....x.........",
    > + ".x...x..........",
    > + ".x..x...........",
    > + ".x.x............",
    > + "................",
    > + "..x.xxxx........",
    > + "..x.x..x........",
    > + "..x.x..x........",
    > + "..x.x..x........",
    > + "..x.x..x........",
    > + "..x.xxxx........",
    > + "................",
    > + };
    > +
    > + static char *af_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + ".....xxxxxxx....",
    > + ".....x..........",
    > + ".....x..........",
    > + ".x...xxxxx......",
    > + "x.x..x..........",
    > + "xxx..x..........",
    > + "x.x..x..........",
    > + "x.x..x..........",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + };
    > +
    > + static char *ae_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + ".....xxxxxxx....",
    > + ".....x..........",
    > + ".....x..........",
    > + ".x...xxxxx......",
    > + "x.x..x..........",
    > + "xxx..x..........",
    > + "x.x..x..........",
    > + "x.x..xxxxxxx....",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + };
    > +
    > + static char *not_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + "......xxxxx.....",
    > + "....xx.....xx...",
    > + "...x.........x..",
    > + "..x........xx.x.",
    > + "..x......xx...x.",
    > + ".x.....xx......x",
    > + ".x...xx........x",
    > + "..xxx.........x.",
    > + "..x...........x.",
    > + "...x.........x..",
    > + "....xx.....xx...",
    > + "......xxxxx.....",
    > + };
    > +
    > + static char *empty_xpm[] = {
    > + "16 12 2 1",
    > + "x c #ffffff",
    > + ". c #000000",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + "................",
    > + };
    > +
    > + SDL_FillRect(m->screen, NULL, SDL_MapRGB( m->liveview->format, 0, 0, 0 ));
    > + sdl_paint_image(m, wait_xpm, m->wx/2, m->wy/2);
    > + sdl_paint_image(m, ok_xpm, m->wx-m->bx/2, m->wy-m->by/2);
    > + sdl_paint_image(m, exit_xpm, m->bx/2, m->wy-m->by/2);
    > + sdl_paint_image(m, f1m_xpm, m->bx+m->sx/20, m->by/2);
    > + sdl_paint_image(m, f25cm_xpm, m->bx+m->sx/5, m->by/2);
    > +
    > + sdl_paint_image(m, af_xpm, m->bx/2, m->by/2);
    > + if (!m->do_focus) {
    > + sdl_paint_image(m, not_xpm, 16+m->bx/2, m->by/2);
    > + }
    > + sdl_paint_image(m, ae_xpm, m->wx-m->bx/2, m->by/2);
    > + if (!m->do_exposure) {
    > + sdl_paint_image(m, not_xpm, 16+m->bx/2, m->by/2);
    > + }
    > +
    > +#if 0
    > + sdl_paint_image(m, time_1_100_xpm, m->wx-m->bx/2, m->by+pix_exposure(10000));
    > + sdl_paint_image(m, time_1_10_xpm, m->wx-m->bx/2, m->by+pix_exposure(100000));
    > + sdl_paint_image(m, iso400_xpm, m->bx/2, m->by+pix_gain(4.0));
    > +#endif
    > +
    > + SDL_UpdateWindowSurface(m->window);
    > +}
    > +
    > +void fmt_print(struct v4l2_format *fmt)
    > +{
    > + int f;
    > + printf("Format: %dx%d. ", fmt->fmt.pix.width, fmt->fmt.pix.height);
    > + printf("%x ", fmt->fmt.pix.pixelformat);
    > + f = fmt->fmt.pix.pixelformat;
    > + for (int i=0; i<4; i++) {
    > + printf("%c", f & 0xff);
    > + f >>= 8;
    > + }
    > + printf("\n");
    > +}
    > +
    > +pixel buf_pixel(struct v4l2_format *fmt, unsigned char *buf, int x, int y)
    > +{
    > + pixel p = { 0, 0, 0, 0 };
    > + int pos = x + y*fmt->fmt.pix.width;
    > + int b;
    > +
    > + p.alpha = 128;
    > +
    > + switch (fmt->fmt.pix.pixelformat) {
    > + case '01AB': /* BA10, aka GRBG10,
    > + https://www.linuxtv.org/downloads/v4l-dvb-apis-new/uapi/v4l/pixfmt-srggb10.html?highlight=ba10
    > + */
    > + b = buf[pos*2];
    > + b += buf[pos*2+1] << 8;
    > + b /= 4;
    > + if ((y % 2) == 0) {
    > + switch (x % 2) {
    > + case 0: p.g = b/2; break;
    > + case 1: p.r = b; break;
    > + }
    > + } else {
    > + switch (x % 2) {
    > + case 0: p.b = b; break;
    > + case 1: p.g = b/2; break;
    > + }
    > + }
    > + break;
    > +
    > + case V4L2_PIX_FMT_RGB24:
    > + pos *= 4;
    > + p.r = buf[pos];
    > + p.g = buf[pos+1];
    > + p.b = buf[pos+2];
    > + break;
    > +
    > + default:
    > + printf("Wrong pixel format!\n");
    > + fmt_print(fmt);
    > + exit(1);
    > + }
    > +
    > + return p;
    > +}
    > +
    > +void sdl_render(struct sdl *m, unsigned char *buf, struct v4l2_format *fmt)
    > +{
    > + if (!m->window)
    > + return;
    > + sdl_begin_paint(m);
    > + /* do your rendering here */
    > +
    > + for (int y = 0; y < m->sy; y++)
    > + for (int x = 0; x < m->sx; x++) {
    > + pixel p = buf_pixel(fmt, buf, x*m->factor, y*m->factor);
    > + p.alpha = 128;
    > + sfc_put_pixel(m->liveview, x, y, &p);
    > + }
    > +
    > + //render_statistics(liveview);
    > +
    > + sdl_finish_paint(m);
    > +}
    > +
    > +#if 0
    > +void imageStatistics(FCam::Image img)
    > +{
    > + int sx, sy;
    > +
    > + sx = img.size().width;
    > + sy = img.size().height;
    > + printf("Image is %d x %d, ", sx, sy);
    > +
    > + printf("(%d) ", img.brightness(sx/2, sy/2));
    > +
    > + int dark = 0;
    > + int bright = 0;
    > + int total = 0;
    > +#define RATIO 10
    > + for (int y = sy/(2*RATIO); y < sy; y += sy/RATIO)
    > + for (int x = sx/(2*RATIO); x < sx; x += sx/RATIO) {
    > + int br = img.brightness(x, y);
    > +
    > + if (!d_raw) {
    > + /* It seems real range is 60 to 218 */
    > + if (br > 200)
    > + bright++;
    > + if (br < 80)
    > + dark++;
    > + } else {
    > + /* there's a lot of noise, it seems black is commonly 65..71,
    > + bright is cca 1023 */
    > + if (br > 1000)
    > + bright++;
    > + if (br < 75)
    > + dark++;
    > + }
    > + total++;
    > + }
    > +
    > + printf("%d dark %d bri,", dark, bright);
    > +
    > + long sharp = 0;
    > + for (int y = sy/3; y < 2*(sy/3); y+=sy/12) {
    > + int b = -1;
    > + for (int x = sx/3; x < 2*(sx/3); x++) {
    > + int b2;
    > + b2 = img.brightness(x, y/2);
    > + if (b != -1)
    > + sharp += (b-b2) * (b-b2);
    > + b = b2;
    > + }
    > + }
    > + printf("sh %d\n", sharp);
    > +}
    > +#endif
    > +
    > +
    > +void sdl_init(struct sdl *m, int _sx, int _sy, int _factor)
    > +{
    > + printf("Initing SDL\n");
    > +
    > + if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    > + printf("Could not init SDL\n");
    > + exit(1);
    > + }
    > +
    > + atexit(SDL_Quit);
    > +
    > + m->wx = 800;
    > + m->wy = 400;
    > +
    > + m->window = SDL_CreateWindow( "Camera", SDL_WINDOWPOS_UNDEFINED,
    > + SDL_WINDOWPOS_UNDEFINED, m->wx, m->wy,
    > + SDL_WINDOW_SHOWN );
    > + if (m->window == NULL) {
    > + printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
    > + }
    > +
    > + m->screen = SDL_GetWindowSurface(m->window);
    > + if (!m->screen) {
    > + printf("Couldn't create liveview\n");
    > + exit(1);
    > + }
    > +
    > + m->sx = _sx;
    > + m->sy = _sy;
    > + m->factor = _factor;
    > +
    > + m->sx /= m->factor;
    > + m->sy /= m->factor;
    > +
    > + m->focus = 0;
    > + m->gain = 0;
    > + m->exposure = 0;
    > +
    > + m->bx = (m->wx-m->sx)/2;
    > + m->by = (m->wy-m->sy)/2;
    > +
    > + m->liveview = SDL_CreateRGBSurface(0,m->sx,m->sy,32,0,0,0,0);
    > + if (!m->liveview) {
    > + printf("Couldn't create liveview\n");
    > + exit(1);
    > + }
    > + sdl_paint_ui(m);
    > +}
    > diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
    > index d3d8936..7521ec8 100644
    > --- a/lib/libv4lconvert/libv4lconvert.c
    > +++ b/lib/libv4lconvert/libv4lconvert.c
    > @@ -1205,6 +1205,8 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
    > v4lconvert_swap_uv(src, dest, fmt);
    > break;
    > }
    > + default:
    > + /* This is bad, fixme? */
    > break;
    >
    > case V4L2_PIX_FMT_YVU420:
    > @@ -1228,6 +1230,8 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
    > case V4L2_PIX_FMT_YVU420:
    > memcpy(dest, src, width * height * 3 / 2);
    > break;
    > + default:
    > + /* This is bad, fixme? */
    > }
    > break;
    >
    > diff --git a/utils/decode_tm6000/Makefile.am b/utils/decode_tm6000/Makefile.am
    > index ac4e85e..f059e3c 100644
    > --- a/utils/decode_tm6000/Makefile.am
    > +++ b/utils/decode_tm6000/Makefile.am
    > @@ -1,4 +1,4 @@
    > bin_PROGRAMS = decode_tm6000
    > decode_tm6000_SOURCES = decode_tm6000.c
    > -decode_tm6000_LDADD = ../libv4l2util/libv4l2util.la
    > +decode_tm6000_LDADD = ../libv4l2/libv4l2.la
    > decode_tm6000_LDFLAGS = $(ARGP_LIBS)
    > diff --git a/utils/decode_tm6000/decode_tm6000.c b/utils/decode_tm6000/decode_tm6000.c
    > index 4bffbdd..fda7e3b 100644
    > --- a/utils/decode_tm6000/decode_tm6000.c
    > +++ b/utils/decode_tm6000/decode_tm6000.c

    Everything below it is completely wrong!

    > @@ -1,365 +1,16 @@
    > -/*
    > - decode_tm6000.c - decode multiplexed format from TM5600/TM6000 USB
    > +/* gcc asciicam.c /usr/lib/i386-linux-gnu/libv4l2.so.0.0.0 -o asciicam
    > + gcc asciicam.c /usr/lib/arm-linux-gnueabi/libv4l2.so.0 -o asciicam
    >
    > - Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org>
    > -
    > - 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 version 2.
    > -
    > - 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 Street, Suite 500, Boston, MA 02110-1335 USA.
    > +gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I../.. -fvisibility=hidden -I../../lib/include -Wall -Wpointer-arith -D_GNU_SOURCE -I../../include -g -O2 asciicam.c libv4l2.c /usr/lib/arm-linux-gnueabi/libv4lconvert.so.0 log.c v4l2convert.c v4l2-plugin.c -o asciicam
    > */
    > -#include "../libv4l2util/v4l2_driver.h"
    > #include <stdio.h>
    > -#include <string.h>
    > -#include <argp.h>
    > -#include <unistd.h>
    > #include <sys/types.h>
    > #include <sys/stat.h>
    > #include <fcntl.h>
    > -#include <stdlib.h>
    > -#include <errno.h>
    > -
    > -const char *argp_program_version="decode_tm6000 version 0.0.1";
    > -const char *argp_program_bug_address="Mauro Carvalho Chehab <mchehab@infradead.org>";
    > -const char doc[]="Decodes tm6000 proprietary format streams";
    > -const struct argp_option options[] = {
    > - {"verbose", 'v', 0, 0, "enables debug messages", 0},
    > - {"device", 'd', "DEV", 0, "uses device for reading", 0},
    > - {"output", 'o', "FILE", 0, "outputs raw stream to a file", 0},
    > - {"input", 'i', "FILE", 0, "parses a file, instead of a device", 0},
    > - {"freq", 'f', "Freq", 0, "station frequency, in MHz (default is 193.25)", 0},
    > - {"nbufs", 'n', "quant",0, "number of video buffers", 0},
    > - {"audio", 'a', 0, 0, "outputs audio on stdout", 0},
    > - {"read", 'r', 0, 0, "use read() instead of mmap method", 0},
    > - { 0, 0, 0, 0, 0, 0 }
    > -};
    > -
    > -static char outbuf[692224];
    > -static int debug=0, audio=0, use_mmap=1, nbufs=4;
    > -static float freq_mhz=193.25;
    > -static char *devicename="/dev/video0";
    > -static char *filename=NULL;
    > -static enum {
    > - NORMAL,
    > - INPUT,
    > - OUTPUT
    > -} mode = NORMAL;
    > -
    > -static FILE *fout;
    > -
    > -//const char args_doc[]="ARG1 ARG2";
    > -
    > -static error_t parse_opt (int key, char *arg, struct argp_state *state)
    > -{
    > - switch (key) {
    > - case 'a':
    > - audio++;
    > - break;
    > - case 'r':
    > - use_mmap=0;
    > - break;
    > - case 'v':
    > - debug++;
    > - break;
    > - case 'd':
    > - devicename=arg;
    > - break;
    > - case 'i':
    > - case 'o':
    > - if (mode!=NORMAL) {
    > - argp_error(state,"You can't use input/output options simultaneously.\n");
    > - break;
    > - }
    > - if (key=='i')
    > - mode=INPUT;
    > - else
    > - mode=OUTPUT;
    > -
    > - filename=arg;
    > - break;
    > - case 'f':
    > - freq_mhz=atof(arg);
    > - break;
    > - case 'n':
    > - nbufs=atoi(arg);
    > - if (nbufs<2)
    > - nbufs=2;
    > - break;
    > - default:
    > - return ARGP_ERR_UNKNOWN;
    > - }
    > - return 0;
    > -}
    > -
    > -static struct argp argp = {
    > - .options=options,
    > - .parser=parse_opt,
    > - .args_doc=NULL,
    > - .doc=doc,
    > -};
    >
    > -#define TM6000_URB_MSG_LEN 180
    > -enum {
    > - TM6000_URB_MSG_VIDEO=1,
    > - TM6000_URB_MSG_AUDIO,
    > - TM6000_URB_MSG_VBI,
    > - TM6000_URB_MSG_PTS,
    > - TM6000_URB_MSG_ERR,
    > -};
    > -
    > -const char *tm6000_msg_type[]= {
    > - "unknown(0)", //0
    > - "video", //1
    > - "audio", //2
    > - "vbi", //3
    > - "pts", //4
    > - "err", //5
    > - "unknown(6)", //6
    > - "unknown(7)", //7
    > -};
    > -
    > -#define dprintf(fmt,arg...) \
    > - if (debug) fprintf(stderr, fmt, ##arg)
    > -
    > -static int recebe_buffer (struct v4l2_buffer *v4l2_buf, struct v4l2_t_buf *buf)
    > -{
    > - dprintf("Received %zd bytes\n", buf->length);
    > -fflush(stdout);
    > - memcpy (outbuf,buf->start,buf->length);
    > - return buf->length;
    > -}
    > -
    > -
    > -static int prepare_read (struct v4l2_driver *drv)
    > -{
    > - struct v4l2_format fmt;
    > - double freq;
    > - int rc;
    > -
    > - memset (drv,0,sizeof(*drv));
    > -
    > - if (v4l2_open (devicename, 1,drv)<0) {
    > - perror ("Error opening dev");
    > - return -1;
    > - }
    > -
    > - memset (&fmt,0,sizeof(fmt));
    > -
    > - uint32_t pixelformat=V4L2_PIX_FMT_TM6000;
    > -
    > - if (v4l2_gettryset_fmt_cap (drv,V4L2_SET,&fmt, 720, 480,
    > - pixelformat,V4L2_FIELD_ANY)) {
    > - perror("set_input to tm6000 raw format");
    > - return -1;
    > - }
    > -
    > - if (freq_mhz) {
    > - freq=freq_mhz * 1000 * 1000;
    > - rc=v4l2_getset_freq (drv,V4L2_SET, &freq);
    > - if (rc<0)
    > - printf ("Cannot set freq to %.3f MHz\n",freq_mhz);
    > - }
    > -
    > - if (use_mmap) {
    > - printf("Preparing for receiving frames on %d buffers...\n",nbufs);
    > - fflush (stdout);
    > -
    > - rc=v4l2_mmap_bufs(drv, nbufs);
    > - if (rc<0) {
    > - printf ("Cannot mmap %d buffers\n",nbufs);
    > - return -1;
    > - }
    > -
    > -// v4l2_stop_streaming(&drv);
    > - rc=v4l2_start_streaming(drv);
    > - if (rc<0) {
    > - printf ("Cannot start streaming\n");
    > - return -1;
    > - }
    > - }
    > - printf("Waiting for frames...\n");
    > -
    > - return 0;
    > -}
    > +#include <linux/videodev2.h>
    >
    > -static int read_stream (struct v4l2_driver *drv, int fd)
    > +void main(void)
    > {
    > - if (use_mmap) {
    > - fd_set fds;
    > - struct timeval tv;
    > - int r;
    > -
    > - FD_ZERO (&fds);
    > - FD_SET (fd, &fds);
    > -
    > - /* Timeout. */
    > - tv.tv_sec = 2;
    > - tv.tv_usec = 0;
    > -
    > - r = select (fd + 1, &fds, NULL, NULL, &tv);
    > - if (-1 == r) {
    > - if (EINTR == errno) {
    > - perror ("select");
    > - return -errno;
    > - }
    > - }
    > -
    > - if (0 == r) {
    > - fprintf (stderr, "select timeout\n");
    > - return -errno;
    > - }
    > -
    > - return v4l2_rcvbuf(drv, recebe_buffer);
    > - } else {
    > - int size=read(fd, outbuf, sizeof(outbuf));
    > - return size;
    > - }
    > -
    > - return 0;
    > -}
    > -
    > -static int read_char (struct v4l2_driver *drv, int fd)
    > -{
    > - static int sizebuf=0;
    > - static unsigned char *p=NULL;
    > - unsigned char c;
    > -
    > - if (sizebuf<=0) {
    > - sizebuf=read_stream(drv,fd);
    > - if (sizebuf<=0)
    > - return -1;
    > - p=(unsigned char *)outbuf;
    > - }
    > - c=*p;
    > - p++;
    > - sizebuf--;
    > -
    > - return c;
    > -}
    > -
    > -
    > -int main (int argc, char*argv[])
    > -{
    > - int fd;
    > - unsigned int i;
    > - unsigned char buf[TM6000_URB_MSG_LEN], img[720*2*480];
    > - unsigned int cmd, size, field, block, line, pos=0;
    > - unsigned long header=0;
    > - int linesize=720*2,skip=0;
    > - struct v4l2_driver drv;
    > -
    > - argp_parse (&argp, argc, argv, 0, 0, 0);
    > -
    > - if (mode!=INPUT) {
    > - if (prepare_read (&drv)<0)
    > - return -1;
    > - fd=drv.fd;
    > - } else {
    > - /*mode == INPUT */
    > -
    > - fd=open(filename,O_RDONLY);
    > - if (fd<0) {
    > - perror ("error opening a file for parsing");
    > - return -1;
    > - }
    > - dprintf("file %s opened for parsing\n",filename);
    > - use_mmap=0;
    > - }
    > -
    > - if (mode==OUTPUT) {
    > - fout=fopen(filename,"w");
    > - if (!fout) {
    > - perror ("error opening a file to write");
    > - return -1;
    > - }
    > - dprintf("file %s opened for output\n",filename);
    > -
    > - do {
    > - size=read_stream (&drv,fd);
    > -
    > - if (size<=0) {
    > - close (fd);
    > - return -1;
    > - }
    > - dprintf("writing %d bytes\n",size);
    > - fwrite(outbuf,1, size,fout);
    > -// fflush(fout);
    > - } while (1);
    > - }
    > -
    > -
    > - while (1) {
    > - skip=0;
    > - header=0;
    > - do {
    > - int c;
    > - c=read_char (&drv,fd);
    > - if (c<0) {
    > - perror("read");
    > - return -1;
    > - }
    > -
    > - header=(header>>8)&0xffffff;
    > - header=header|(c<<24);
    > - skip++;
    > - } while ( (((header>>24)&0xff) != 0x47) );
    > -
    > - /* split the header fields */
    > - size = (((header & 0x7e)<<1) -1) * 4;
    > - block = (header>>7) & 0xf;
    > - field = (header>>11) & 0x1;
    > - line = (header>>12) & 0x1ff;
    > - cmd = (header>>21) & 0x7;
    > -
    > - /* Read the remaining buffer */
    > - for (i=0;i<sizeof(buf);i++) {
    > - int c;
    > - c=read_char (&drv,fd);
    > - if (c<0) {
    > - perror("read");
    > - return -1;
    > - }
    > - buf[i]=c;
    > - }
    > -
    > - /* FIXME: Mounts the image as field0+field1
    > - * It should, instead, check if the user selected
    > - * entrelaced or non-entrelaced mode
    > - */
    > - pos=((line<<1)+field)*linesize+
    > - block*TM6000_URB_MSG_LEN;
    > -
    > - /* Prints debug info */
    > - dprintf("0x%08x (skip %d), %s size=%d, line=%d, field=%d, block=%d\n",
    > - (unsigned int)header, skip,
    > - tm6000_msg_type[cmd],
    > - size, line, field, block);
    > -
    > - /* Don't allow to write out of the buffer */
    > - if (pos+sizeof(buf) > sizeof(img))
    > - cmd = TM6000_URB_MSG_ERR;
    > -
    > - /* handles each different URB message */
    > - switch(cmd) {
    > - case TM6000_URB_MSG_VIDEO:
    > - /* Fills video buffer */
    > - memcpy(buf,&img[pos],sizeof(buf));
    > - case TM6000_URB_MSG_AUDIO:
    > - if (audio)
    > - fwrite(buf,sizeof(buf),1,stdout);
    > -// case TM6000_URB_MSG_VBI:
    > -// case TM6000_URB_MSG_PTS:
    > - break;
    > - }
    > - }
    > - close(fd);
    > - return 0;
    > + v4l2_close(-2);
    > }
    >



    Thanks,
    Mauro

    \
     
     \ /
      Last update: 2017-04-24 15:39    [W:6.370 / U:0.188 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site