A Place Where No Dreams Come True...

Current Project: Test software I'm writing to interface to a webcam based on the V4L2 Video For Linux standard. I plan to eventually turn this into a private webcam server.

What Does This Code Do...

Implements an interface to a Video For Linux Version-2 (V4L2) video device.

What Doesn't This Code Do...

This is not the planned complete interface. Currently it has no outside methods of setting interface parameters. Additionally there are some hard-coded values used for the purpose of ongoing debugging.

//-----------------------------------------------------------------------------
// v4l2hlpr.c
//
//  Video 4 Linux 2 Helper Implementation.
//
// Copyright (c) 2014 - No Fun Farms A.K.A. www.smegware.com
//
//  All Smegware software is free; 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 software 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.
//
//-----------------------------------------------------------------------------
//
// History...
//
//   $Source$
//   $Author$
// $Revision$
//
// $Log$
//
//-----------------------------------------------------------------------------

#include <errno.h>
#include <stdlib.h>
#include <memory.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

#include "v4l2hlpr.h"
#include "report.h"

//-----------------------------------------------------------------------------

#define MAXDEVNAME 128

struct _v4l2buf {
  void *start;
  struct v4l2_buffer buf;
};

struct _v4l2 {
  char name[MAXDEVNAME + 1];
  int dev;
  int inputs;
  struct v4l2_capability cap;
  struct v4l2_format format;
  struct v4l2_input input;
  struct v4l2_enc_idx encode;
  struct v4l2_standard standard;
  struct v4l2_jpegcompression jcomp;
  struct v4l2_streamparm stream;
  int bufcnt;
  int bufidx;
  struct _v4l2buf *mmaps;
  long frames;
  char pxfmt[8];
};

//-----------------------------------------------------------------------------
// Recommended I/O wrapper.
/*
static int xioctl(int fddev, int request, void *arg)
{
  int rtn;

  do
  {
    rtn = ioctl(fddev, request, arg);
  }
  while((rtn == -1) && (errno == EINTR));
  return rtn;
}
*/
//-----------------------------------------------------------------------------

static const char *v4l2_get_pix_format(struct _v4l2 *video)
{
  strncpy(video->pxfmt, (char*)&video->format.fmt.pix.pixelformat, 4);
  video->pxfmt[4] = '\0';
  return video->pxfmt;
}

//-----------------------------------------------------------------------------

unsigned int v4l2_get_pixel_format(struct _v4l2 *video)
{
  return video->format.fmt.pix.pixelformat;
}

//-----------------------------------------------------------------------------

static int v4l2_get_standard(struct _v4l2 *video, int standard)
{
  memset(&video->standard, 0, sizeof(struct v4l2_standard));
  video->standard.index = standard;
  return ioctl(video->dev, VIDIOC_ENUMSTD, &video->standard);
}

//-----------------------------------------------------------------------------

static const char *v4l2_get_input_type(unsigned type)
{
  static const char *types[4] = {
    "none",
    "tuner",
    "camera",
    "unknown"
  };

  __u32 t = type;

  if(t > 3)
  {
    t = 3;
  }
  return types[t];
}

//-----------------------------------------------------------------------------

int v4l2_show_inputs(struct _v4l2 *video)
{
  int rtn = 0;
  struct v4l2_input arg;

  memset(&arg, 0, sizeof(struct v4l2_input));
  report(dbglevl_msg, "Available video inputs...\n");
  while((rtn = ioctl(video->dev, VIDIOC_ENUMINPUT, &arg)) == 0)
  {
    report(dbglevl_msg, " index           : %i.\n", arg.index);
    report(dbglevl_msg, "  name           : %s.\n", arg.name);
    report(dbglevl_msg, "  type           : %s.\n", v4l2_get_input_type(arg.type));
    report(dbglevl_msg, "  audioset       : %i.\n", arg.audioset);
    report(dbglevl_msg, "  tuner          : %i.\n", arg.tuner);
    if(v4l2_get_standard(video, arg.std) != -1)
    {
      report(dbglevl_msg, "  standard       : id=%i name=%s lines=%i.\n",
	     video->standard.id, video->standard.name, video->standard.framelines);
    }
    else
    {
      report(dbglevl_msg, "  standard       : none.\n");
    }
    arg.index++;
  }
  if((errno != EINVAL) || (arg.index == 0))
  {
    report_error("error : ioctl(VIDIOC_ENUMIMPUT) failed - ");
    rtn = -1;
  }
  else
  {
    rtn = arg.index;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

int v4l2_get_input(struct _v4l2 *video, unsigned index)
{
  int rtn;
  memset(&video->input, 0, sizeof(struct v4l2_input));
  video->input.index = index;
  report(dbglevl_msg, "Getting input %i.\n", index);
  rtn = ioctl(video->dev, VIDIOC_ENUMINPUT, &video->input);
  if(rtn == -1)
  {
    report_error("error : ioctl(VIDIOC_ENUMINPUT) failed - ");
  }
  return rtn;
}

//-----------------------------------------------------------------------------

int v4l2_set_input(struct _v4l2 *video, unsigned index)
{
  int i = index;

  report(dbglevl_msg, "Requesting input %i.\n", i);
  return ioctl(video->dev, VIDIOC_S_INPUT, &i);
}

//-----------------------------------------------------------------------------

int v4l2_get_caps(struct _v4l2 *video)
{
  int rtn;

  memset(&video->cap, 0, sizeof(struct v4l2_capability));
  rtn = ioctl(video->dev, VIDIOC_QUERYCAP, &video->cap);
  if(rtn == -1)
  {
    report_error("error : ioctl(VIDIOC_QUERYCAP) failed - ");
  }
  else
  {
    report(dbglevl_msg, "Device capabilities...\n");
    report(dbglevl_msg, " driver          : %s.\n",   video->cap.driver);
    report(dbglevl_msg, " card            : %s.\n",   video->cap.card);
    report(dbglevl_msg, " bus             : %s.\n",   video->cap.bus_info);
    report(dbglevl_msg, " version         : %08X.\n", video->cap.version);
    report(dbglevl_msg, " capabilities    : %08X.\n", video->cap.capabilities);

    if((video->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0)
    {
      report(dbglevl_msg, "                 : is a video capture device.\n");
    }
    if((video->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) != 0)
    {
      report(dbglevl_msg, "                 : supports video output.\n");
    }
    if((video->cap.capabilities & V4L2_CAP_AUDIO) != 0)
    {
      report(dbglevl_msg, "                 : supports audio input.\n");
    }
    if((video->cap.capabilities & V4L2_CAP_STREAMING) != 0)
    {
      report(dbglevl_msg, "                 : supports streaming.\n");
    }
    if((video->cap.capabilities & V4L2_CAP_READWRITE) != 0)
    {
      report(dbglevl_msg, "                 : supports read()/write().\n");
    }
  }
  return rtn;
}

//-----------------------------------------------------------------------------

static void v4l2_show_jpeg_markers(__u32 markers)
{
  // Enumerate control flags.
  report(dbglevl_msg, "%08X [ ", markers);
  if((markers & V4L2_JPEG_MARKER_DHT) != 0)
  {
    report(dbglevl_msg, "def-huffman-tables ");
  }
  if((markers & V4L2_JPEG_MARKER_DQT) != 0)
  {
    report(dbglevl_msg, "def-quantization-tables ");
  }
  if((markers & V4L2_JPEG_MARKER_DRI) != 0)
  {
    report(dbglevl_msg, "def-restart-interval ");
  }
  if((markers & V4L2_JPEG_MARKER_COM) != 0)
  {
    report(dbglevl_msg, "def-comment-segment ");
  }
  if((markers & V4L2_JPEG_MARKER_APP) != 0)
  {
    report(dbglevl_msg, "app segment is always APP0 ");
  }
  report(dbglevl_msg, "].\n");
}

//-----------------------------------------------------------------------------

void v4l2_get_jpeg_compression(struct _v4l2 *video)
{
  memset(&video->jcomp, 0, sizeof(struct v4l2_jpegcompression));
  if(ioctl(video->dev, VIDIOC_G_JPEGCOMP, &video->jcomp) != -1)
  {
    report(dbglevl_msg, "JPEG compression parameters...\n");
    report(dbglevl_msg, " quality         : %i.\n", video->jcomp.quality);
    report(dbglevl_msg, " APPn            : %i.\n", video->jcomp.APPn);
    report(dbglevl_msg, " APP_len         : %i.\n", video->jcomp.APP_len);
    report(dbglevl_msg, " APP_data        : %s.\n", video->jcomp.APP_data);
    report(dbglevl_msg, " COM len         : %i.\n", video->jcomp.COM_len);
    report(dbglevl_msg, " COM data        : %s.\n", video->jcomp.COM_data);
    report(dbglevl_msg, " markers         : ");
    v4l2_show_jpeg_markers(video->jcomp.jpeg_markers);
  }
  else
  {
    report_error("error : ioctl(VIDIOC_G_JPEGCOMP) failed - ");
  }
}

//-----------------------------------------------------------------------------

void v4l2_set_jpeg_compression(struct _v4l2 *video)
{
  report(dbglevl_msg, "Setting JPEG parameters.\n");
  if(ioctl(video->dev, VIDIOC_S_JPEGCOMP, &video->jcomp) == -1)
  {
    report_error("error : ioctl(VIDIOC_S_JPEGCOMP) failed - ");
  }
}

//-----------------------------------------------------------------------------

int v4l2_get_enc_index(struct _v4l2 *video)
{
  int rtn;

  report(dbglevl_msg, "Getting encoding indecies.\n");
  rtn = ioctl(video->dev, VIDIOC_G_ENC_INDEX, &video->encode);
  if(rtn != -1)
  {
    report(dbglevl_msg, " entries    : %i.\n", video->encode.entries);
  }
  else
  {
    report_error("error : ioctl(VIDIOC_G_ENC_INDEX) failed - ");
  }
  return rtn;
}

//-----------------------------------------------------------------------------

void v4l2_show_enc_index(struct _v4l2 *video)
{
  // FIXME: Ugh!
  v4l2_get_enc_index(video);
}

//-----------------------------------------------------------------------------

int v4l2_show_formats(struct _v4l2 *video)
{
  int rtn = 0;
  __u32 opxfmt = video->format.fmt.pix.pixelformat;

  struct v4l2_fmtdesc arg;

  arg.index = 0;
  arg.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  report(dbglevl_msg, "Device formats...\n");
  while(ioctl(video->dev, VIDIOC_ENUM_FMT, &arg) == 0)
  {
    video->format.fmt.pix.pixelformat = arg.pixelformat;
    report(dbglevl_msg, " index           : %i.\n", arg.index);
    report(dbglevl_msg, "  type           : capture.\n");
    report(dbglevl_msg, "  description    : %s.\n", arg.description);
    report(dbglevl_msg, "  pix format     : %s.\n", v4l2_get_pix_format(video));
    report(dbglevl_msg, "  flags          : %X.\n", arg.flags);
    arg.index += 1;
  }
  if((errno != EINVAL) || (arg.index == 0))
  {
    rtn = -1;
  }
  else
  {
    rtn = arg.index;
  }
  video->format.fmt.pix.pixelformat = opxfmt;
  return rtn;
}

//-----------------------------------------------------------------------------

int v4l2_get_format(struct _v4l2 *video)
{
  int rtn = 0;

  memset(&video->format, 0, sizeof(struct v4l2_format));
  video->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  report(dbglevl_msg, "Getting format...\n");
  if(ioctl(video->dev, VIDIOC_G_FMT, &video->format) != -1)
  {
    report(dbglevl_msg, " Capture Format...\n");
    report(dbglevl_msg, "  pixel format   : %s.\n", v4l2_get_pix_format(video));
    report(dbglevl_msg, "  device width   : %i.\n", video->format.fmt.pix.width);
    report(dbglevl_msg, "  device height  : %i.\n", video->format.fmt.pix.height);
    report(dbglevl_msg, "  device fields  : %i.\n", video->format.fmt.pix.field);
    report(dbglevl_msg, "  bytes per line : %i.\n", video->format.fmt.pix.bytesperline);
    report(dbglevl_msg, "  size of image  : %i.\n", video->format.fmt.pix.sizeimage);
    report(dbglevl_msg, "  colorspace     : %X.\n", video->format.fmt.pix.colorspace);
    report(dbglevl_msg, "  private        : %i.\n", video->format.fmt.pix.priv);
  }
  else
  {
    report_error("error : ioctl(VIDIOC_G_FMT) failed - ");
    rtn = -1;
  }
  return rtn;
}

//-----------------------------------------------------------------------------

int v4l2_set_format(struct _v4l2 *video)
{
  int rtn;

  report(dbglevl_msg, "Requesting format...\n");
  rtn = ioctl(video->dev, VIDIOC_S_FMT, &video->format);
  if(rtn != -1)
  {
    report(dbglevl_msg, " pixel format    : %s.\n", v4l2_get_pix_format(video));
    report(dbglevl_msg, " device width    : %i.\n", video->format.fmt.pix.width);
    report(dbglevl_msg, " device height   : %i.\n", video->format.fmt.pix.height);
  }
  else
  {
    report_error("error : ioctl(VIDIOC_S_FMT) failed - ");
  }
  return rtn;
}

//-----------------------------------------------------------------------------

void v4l2_show_framesize(struct _v4l2 *video)
{
  struct v4l2_frmsizeenum arg;

  arg.index        = 0;
  arg.pixel_format = video->format.fmt.pix.pixelformat;

  report(dbglevl_msg, "Device frame-sizes...\n");
  while(ioctl(video->dev, VIDIOC_ENUM_FRAMESIZES, &arg) == 0)
  {
    if(arg.type == V4L2_FRMSIZE_TYPE_DISCRETE)
    {
      report(dbglevl_msg, " discrete        : index=%i X=%i Y=%i.\n",
	     arg.index, arg.discrete.width, arg.discrete.height);
    }
    arg.index += 1;
  }

}

//-----------------------------------------------------------------------------

void v4l2_show_frameinterval(struct _v4l2 *video)
{
  struct v4l2_frmivalenum arg;

  memset(&arg, 0, sizeof(struct v4l2_frmivalenum));
  arg.index        = 0;
  arg.pixel_format = video->format.fmt.pix.pixelformat;
  arg.width        = video->format.fmt.pix.width;
  arg.height       = video->format.fmt.pix.height;

  report(dbglevl_msg, "Device frame-rates...\n");
  while(ioctl(video->dev, VIDIOC_ENUM_FRAMEINTERVALS, &arg) == 0)
  {
    if(arg.type == V4L2_FRMIVAL_TYPE_DISCRETE)
    {
      report(dbglevl_msg, " discrete        : index=%i r=%i/%i.\n",
	     arg.index, arg.discrete.numerator, arg.discrete.denominator);
    }
    else
    {
      report(dbglevl_debug, " unknown type : %i.\n", arg.type);
    }
    arg.index += 1;
  }
  if(arg.index == 0)
  {
    report(dbglevl_msg, " Unspecified.\n");
  }
}

//-----------------------------------------------------------------------------

static void v4l2_show_control_menu(int fddev, int id, int min, int max)
{
  struct v4l2_querymenu querymenu;

  report(dbglevl_msg, "  menu items...\n");

  memset(&querymenu, 0, sizeof(querymenu));
  querymenu.id = id;

  for(querymenu.index = min; querymenu.index <= max; querymenu.index++)
  {
    if(ioctl(fddev, VIDIOC_QUERYMENU, &querymenu) != -1)
    {
      report(dbglevl_msg, "   %s.\n", querymenu.name);
    }
    else
    {
      report_error("error : ioctl(VIDIOC_QUERYMENU) failed - ");
      break;
    }
  }
}

//-----------------------------------------------------------------------------

static void v4l2_show_control_flags(__u32 flags)
{
  // Enumerate control flags.
  report(dbglevl_msg, "%08X ", flags);
  if((flags & V4L2_CTRL_FLAG_DISABLED) != 0)
  {
    report(dbglevl_msg, "disabled ");
  }
  if((flags & V4L2_CTRL_FLAG_GRABBED) != 0)
  {
    report(dbglevl_msg, "locked ");
  }
  if((flags & V4L2_CTRL_FLAG_READ_ONLY) != 0)
  {
    report(dbglevl_msg, "read-only ");
  }
  if((flags & V4L2_CTRL_FLAG_UPDATE) != 0)
  {
    report(dbglevl_msg, "update ");
  }
  if((flags & V4L2_CTRL_FLAG_INACTIVE) != 0)
  {
    report(dbglevl_msg, "inactive ");
  }
  if((flags & V4L2_CTRL_FLAG_SLIDER) != 0)
  {
    report(dbglevl_msg, "slider-hint ");
  }
  report(dbglevl_msg, ".\n");
}

//-----------------------------------------------------------------------------

void v4l2_show_controls(struct _v4l2 *video)
{
  struct v4l2_queryctrl queryctrl;

  report(dbglevl_msg, "Device Controls...\n");

  memset(&queryctrl, 0, sizeof(queryctrl));

  for(queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
  {
    if(ioctl(video->dev, VIDIOC_QUERYCTRL, &queryctrl) == 0)
    {
      report(dbglevl_msg, " %s...\n", queryctrl.name);
      if(queryctrl.type == V4L2_CTRL_TYPE_MENU)
      {
	v4l2_show_control_menu(video->dev, queryctrl.id, queryctrl.minimum, queryctrl.maximum);
      }
      else if(queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
      {
	report(dbglevl_msg, "  Integer...\n");
	report(dbglevl_msg, "   minimum       : %i.\n", queryctrl.minimum);
	report(dbglevl_msg, "   maximum       : %i.\n", queryctrl.maximum);
	report(dbglevl_msg, "   step          : %i.\n", queryctrl.step);
	report(dbglevl_msg, "   default       : %i.\n", queryctrl.default_value);
	report(dbglevl_msg, "   flags         : ");
	v4l2_show_control_flags(queryctrl.flags);
      }
    }
    else
    {
      if(errno == EINVAL)
      {
	continue;
      }
      report_error("error : ioctl(VIDIOC_QUERYCTRL) failed - ");
      break;
    }
  }
  for(queryctrl.id = V4L2_CID_PRIVATE_BASE; ; queryctrl.id++)
  {
    if(ioctl(video->dev, VIDIOC_QUERYCTRL, &queryctrl) == 0)
    {
      if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
      {
	continue;
      }
      printf (" %s\n", queryctrl.name);
      if(queryctrl.type == V4L2_CTRL_TYPE_MENU)
      {
	v4l2_show_control_menu(video->dev, queryctrl.id, queryctrl.minimum, queryctrl.maximum);
      }
    }
    else
    {
      if(errno == EINVAL)
      {
	break;
      }
      report_error("error : ioctl(VIDIOC_QUERYCTRL) failed - ");
      break;
    }
  }
}

//-----------------------------------------------------------------------------

void v4l2_show_mpeg_controls(int fddev)
{
}

//-----------------------------------------------------------------------------

void v4l2_set_frame_rate(struct _v4l2 *video, int rate)
{
  struct v4l2_streamparm arg;

  memset(&arg, 0, sizeof(struct v4l2_streamparm));
  arg.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  arg.parm.capture.timeperframe.numerator   = 1;
  arg.parm.capture.timeperframe.denominator = rate;
  arg.parm.capture.capability               = V4L2_CAP_TIMEPERFRAME;

  report(dbglevl_msg, " Frequency : %i frame/sec.\n", rate);
  if(ioctl(video->dev, VIDIOC_S_PARM, &arg) == -1)
  {
    report_error("error : ioctl(VIDEOC_S_PARAM, %i) failed - ", rate);
  }
}

//-----------------------------------------------------------------------------

int v4l2_set_stream_parm(struct _v4l2 *video)
{
  return ioctl(video->dev, VIDIOC_S_PARM, &video->stream);
}

//-----------------------------------------------------------------------------

void v4l2_show_stream_parm(struct _v4l2 *video)
{

  video->stream.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

  report(dbglevl_msg, "Getting stream parameters...\n");
  if(ioctl(video->dev, VIDIOC_G_PARM, &video->stream) == -1)
  {
    report_error("error : ioctl(VIDIOC_G_PARM) failed - ");
  }
  else
  {
    if(video->stream.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
    {
      report(dbglevl_msg, " type            : capture.\n");
      report(dbglevl_msg, " capability      : %08X.\n", video->stream.parm.capture.capability);
      report(dbglevl_msg, " capture mode    : %08X.\n", video->stream.parm.capture.capturemode);
      if(video->stream.parm.capture.timeperframe.numerator)
      {
	report(dbglevl_msg, " framerate       : %i/%i sec.\n",
	       video->stream.parm.capture.timeperframe.numerator, video->stream.parm.capture.timeperframe.denominator);
      }
      report(dbglevl_msg, " extended mode   : %08X.\n", video->stream.parm.capture.extendedmode);
      report(dbglevl_msg, " read buffers    : %08X.\n", video->stream.parm.capture.readbuffers);
    }
    else if(video->stream.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
    {
      report(dbglevl_msg, " type            : output.\n");
    }
    else
    {
      report(dbglevl_msg, " unrecognised    : v4l2_buf_type = %i.\n", video->stream.type);
    }
  }
}

//-----------------------------------------------------------------------------

void v4l2_kill_buffers(struct _v4l2 *video)
{
  int i;

  report(dbglevl_msg, "Deleting Mapped Buffers : %i.\n", video->bufcnt);
  if(video->mmaps)
  {
    if(video->bufcnt)
    {
      for(i = 0; i < video->bufcnt; i++)
      {
	if(video->mmaps[i].start)
	{
	  if(munmap(video->mmaps[i].start, video->mmaps[i].buf.length) == -1)
	  {
	    report_error(" munmap(%016X, %i) failed - ", video->mmaps[i].start, video->mmaps[i].buf.length);
	  }
	  else
	  {
	    report(dbglevl_debug, " unmapped :%016lX length=%i.\n",
		   video->mmaps[i].start, video->mmaps[i].buf.length);
	  }
	}
      }
    }
    free(video->mmaps);
    video->mmaps = NULL;
  }
}

//-----------------------------------------------------------------------------

int v4l2_get_buffers(struct _v4l2 *video)
{
  int rtn = 0;
  int frame;
  struct v4l2_requestbuffers reqbuf;

  memset(&reqbuf, 0, sizeof(reqbuf));
  reqbuf.count  = video->bufcnt;
  reqbuf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  reqbuf.memory = V4L2_MEMORY_MMAP;
  report(dbglevl_msg, "Requesting buffers...\n");
  report(dbglevl_msg, " memory-mapped io %i.\n", reqbuf.count);
  if(ioctl(video->dev, VIDIOC_REQBUFS, &reqbuf) != -1)
  {
    report(dbglevl_debug, "v4l2_get_buffers():ioctl(VIDIOC_REQBUFS) returned : count=%i type=%i memory=%i.\n",
	   reqbuf.count, reqbuf.type, reqbuf.memory);
    report(dbglevl_msg, " allocating buffers : %i.\n", reqbuf.count);
    video->bufcnt = reqbuf.count;
    video->mmaps = calloc(video->bufcnt, sizeof(struct _v4l2buf));
    for(frame = 0; frame < video->bufcnt; frame++)
    {
      video->mmaps[frame].buf.type   = reqbuf.type;
      video->mmaps[frame].buf.memory = reqbuf.memory;
      video->mmaps[frame].buf.index  = frame;
      if(ioctl(video->dev, VIDIOC_QUERYBUF, &video->mmaps[frame].buf) != -1)
      {
	video->mmaps[frame].start  = mmap(NULL, video->mmaps[frame].buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
					  video->dev, video->mmaps[frame].buf.m.offset);
	report(dbglevl_debug, "   mapped :%016lX offset=%i length=%i.\n",
	       video->mmaps[frame].start, video->mmaps[frame].buf.m.offset, video->mmaps[frame].buf.length);
	if(video->mmaps[frame].start == MAP_FAILED)
	{
	  video->mmaps[frame].start = NULL;
	  report_error("error : mmap() failed - ");
	  break;
	}
      }
      else
      {
	report_error("error : v4l2_get_buffers():ioctl(VIDIOC_QUERYBUF) failed - ");
	break;
      }
    }
    if(frame == reqbuf.count)
    {
      rtn = frame;
    }
    else
    {
      // FIXME:
      // Error mapping... Clean up and fail.
      rtn = 0;
    }
  }
  else
  {
    report_error("error : v4l2_get_buffers():ioctl(VIDIOC_REQBUFS) failed - ");
  }
  return rtn;
}

//-----------------------------------------------------------------------------

void *v4l2_get_pointer(struct _v4l2 *video)
{
  return video->mmaps[video->bufidx].start;
}

//-----------------------------------------------------------------------------

int v4l2_get_length(struct _v4l2 *video)
{
  return video->mmaps[video->bufidx].buf.length;
}

//-----------------------------------------------------------------------------

int v4l2_que_buffer(struct _v4l2 *video)
{
  int rtn;

  rtn = ioctl(video->dev, VIDIOC_QBUF, &video->mmaps[video->bufidx].buf);
  if(rtn == -1)
  {
    report_error("error : ioctl(VIDIOC__QBUF) failed - ");
  }
  return rtn;
}

//-----------------------------------------------------------------------------

void *v4l2_dque_buffer(struct _v4l2 *video)
{
  void *rtn = NULL;

  if(ioctl(video->dev, VIDIOC_DQBUF, &video->mmaps[video->bufidx].buf) == -1)
  {
    report_error("error : ioctl(VIDIOC__DQBUF) failed - ");
  }
  else
  {
    rtn = v4l2_get_pointer(video);
    video->bufidx++;
    if(video->bufidx >= video->bufcnt)
    {
      video->bufidx = 0;
    }
  }
  return rtn;
}

//-----------------------------------------------------------------------------

int v4l2_start_stream(struct _v4l2 *video)
{
  int rtn;
  enum v4l2_buf_type type;

  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  report(dbglevl_msg, "Enabling video stream type=%i.\n", type);
  rtn = ioctl(video->dev, VIDIOC_STREAMON, &type);
  if(rtn == -1)
  {
    report_error("error : ioctl(VIDIOC_STREAMON, %i) failed - ", type);
  }
  return rtn;
}

//-----------------------------------------------------------------------------

int v4l2_stop_stream(struct _v4l2 *video)
{
  int rtn;
  enum v4l2_buf_type type;

  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  report(dbglevl_msg, "Disabling video stream type=%i.\n", type);
  rtn = ioctl(video->dev, VIDIOC_STREAMOFF, &type);
  if(rtn == -1)
  {
    report_error("error : ioctl(VIDIOC_STREAMOFF, %i) failed - ", type);
  }
  return rtn;
}

//-----------------------------------------------------------------------------

void v4l2_destroy_capture(struct _v4l2 **pvideo)
{
  struct _v4l2 *video = *pvideo;
  if(video->dev)
  {
    report(dbglevl_debug, "v4l2_destroy_capture():close(%i).\n", video->dev);
    close(video->dev);
  }
  if(video->mmaps)
  {
    v4l2_kill_buffers(video);
  }
  report(dbglevl_debug, "v4l2_destroy_capture():free(video).\n");
  free(video);
  *pvideo = NULL;
}

//-----------------------------------------------------------------------------

struct _v4l2 *v4l2_create_capture(const char *device, int *width, int *height, int rate)
{
  int rtn = -1;
  struct _v4l2 *video;

  video = malloc(sizeof(struct _v4l2));
  if(video)
  {
    memset(video, 0, sizeof(struct _v4l2));
    video->bufcnt = 4;
    strncpy(video->name, device, sizeof(video->name) - 1);
    video->name[sizeof(video->name) - 1] = '\0';
    video->dev = open(video->name, O_RDWR);
    if(video->dev != -1)
    {
      // FIXME: Split open/init.
      report(dbglevl_debug, "v4l2_create_capture():open(%s) - %i.\n", video->name, video->dev);
      video->inputs = v4l2_show_inputs(video);
      //report(dbglevl_debug, "v4l2_create_capture():v4l2_show_inputs() - %i.\n", video->inputs);
      if(video->inputs > 0)
      {
	// Negotiation Opportunity.
	if(v4l2_set_input(video, 0) != -1)
	{
	  if(v4l2_get_caps(video) != -1)
	  {
	    if(video->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
	    {
	      v4l2_show_formats(video);
	      v4l2_get_format(video);
	      video->format.fmt.pix.width = *width;
	      video->format.fmt.pix.height = *height;
	      if(v4l2_get_pixel_format(video) == V4L2_PIX_FMT_JPEG)
	      {
		v4l2_get_jpeg_compression(video);
	      }
	      v4l2_show_framesize(video);
	      v4l2_show_controls(video);
	      if(v4l2_set_format(video) != -1)
	      {
		*width = video->format.fmt.pix.width;
		*height = video->format.fmt.pix.height;
		v4l2_show_frameinterval(video);
		v4l2_show_stream_parm(video);
		video->stream.parm.capture.timeperframe.numerator = 1;
		video->stream.parm.capture.timeperframe.denominator = rate;
		v4l2_set_stream_parm(video);
		v4l2_get_buffers(video);
		rtn = 0;
	      }
	      else
	      {
		// format.
		*width = 0;
		*height = 0;
	      }
	    }
	    else
	    {
	      // not capture device.
	    }
	  }
	  else
	  {
	    // caps
	  }
	}
	else
	{
	  // inputs.
	}
      } // previously reported.
    }
    else
    {
      report_error("error : v4l2_create_capture():open(%s) failed - ", video->name);
    }
    if(rtn < 0)
    {
      v4l2_destroy_capture(&video);
    }
  }
  return video;
}

//-----------------------------------------------------------------------------
// end: v4l2hlpr.c

  

And The Companion Header...

//-----------------------------------------------------------------------------
// v4l2hlpr.h
//
//  Video 4 Linux 2 Helper Definition.
//
// Copyright (c) 2014 - No Fun Farms A.K.A. www.smegware.com
//
//  All Smegware software is free; 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 software 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.
//
//-----------------------------------------------------------------------------
//
// History...
//
//   $Source$
//   $Author$
// $Revision$
//
// $Log$
//
//-----------------------------------------------------------------------------

typedef struct _v4l2 v4l2, *pv4l2;

//-----------------------------------------------------------------------------

extern pv4l2 v4l2_create_capture(const char*, int*, int*, int);
extern void v4l2_destroy_capture(pv4l2*);

extern unsigned int v4l2_get_pixel_format(pv4l2);
extern int  v4l2_get_caps(pv4l2);
extern int  v4l2_get_enc_index(pv4l2);
extern int  v4l2_get_format(pv4l2);
extern int  v4l2_get_input(pv4l2, unsigned);
extern void v4l2_get_jpeg_compression(pv4l2);

extern int  v4l2_set_format(pv4l2);
extern void v4l2_set_frame_rate(pv4l2, int);
extern int  v4l2_set_input(pv4l2, unsigned);
extern void v4l2_set_jpeg_compression(pv4l2);

extern void v4l2_show_controls(pv4l2);
extern void v4l2_show_enc_index(pv4l2);
extern int  v4l2_show_formats(pv4l2);
extern void v4l2_show_frameinterval(pv4l2);
extern void v4l2_show_framesize(pv4l2);
extern int  v4l2_show_inputs(pv4l2);
extern void v4l2_show_stream_parm(pv4l2);

extern void *v4l2_get_pointer(pv4l2);
extern int  v4l2_get_length(pv4l2);
extern int  v4l2_get_buffers(pv4l2);
extern int  v4l2_que_buffer(pv4l2);
extern void *v4l2_dque_buffer(pv4l2);
extern void v4l2_kill_buffers(pv4l2);

extern int  v4l2_start_stream(pv4l2);
extern int  v4l2_stop_stream(pv4l2);

//-----------------------------------------------------------------------------
// end: v4l2hlpr.h

  

The Code Demonstrates The Following Programming Techniques...

Conclusion...

It is known that this code has many 'Freshman' mistakes in it. It is not claimed to be good code. Hopefully though; it demonstrates some of the less obvious interface mechanisms when communicating with both a Window-Manager and a Client program. A definition of how to use the code is also missing. Not purposely, an interface spec just can't be fit into my schedule. For now. Besides... This code is not really meant to be used. Although I use this code it is presented here purely for educational purposes.

11220