Dr. Malinowski showed me an interesting example that he found online called Video 4 Linux 2 or V4L2. The mainpage for V4L2 is located here and here are the documentation and video capture example provided via links from the mainpage. Another useful link from TI’s website is the OMAP35x Linux PSP. Dr. Malinowski already tweaked the video capture example so that it would work with a /dev/video0 devices.
This code produces a Segmentation error and after going through it and debugging I figured out that it was missing CLEAR(buf) before declaring buf.type, buf.memory, and buf.index. The CLEAR(buf) can be replace with the line of code:
struct v4l2_buffer buf;
memset(&buf, sizeof(buf), 0);
Using the Video4Linux example with the corrections the following code captures 2 images and saves them as a .ppm file.
//* WebCam access example based on video4linux v2 *
/*When compiling this program on your system type in "gcc -Wall Name_of_your_file.c /usr/lib/libm.a -o Name_of_your_program"
without the "" marks. If you don't have the /usr/lib/libm.a file available on your system typing "gcc Name_of_your_file.c -lm"
without the "" marks and just run the a.out file that it gives you by typing "./a.out" without the "" marks. As of right now
the function uses the YUYV format since the RGB24 format has some issues with sizing. */
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h> //mmap
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <linux/types.h>
#include <linux/videodev2.h>
struct buffer {
void * start;
size_t length;
};
const char VideoDeviceName0[] = "/dev/video0";
#define fname_maxlen (64)
const char CapturedImageNameIO[] = "image_io"; // .ppm will be appended
const char CapturedImageNameMM[] = "image_mm"; // .ppm will be appended
static int xioctl (int fd, int request, void* arg)
{
int status;
do { status = ioctl (fd, request, arg); } while (-1==status && EINTR==errno);
return status;
}
int ppm_save_rgb24(const char* name, int cols, int rows, void* buffer, int bufferSize);
int ppm_save_yuyv(const char* name, int cols, int rows, void* buffer, int bufferSize);
int main(int argc, char **argv)
{
const char* VideoDeviceName = VideoDeviceName0;
int status;
if (argc>1)
{
VideoDeviceName = argv[1];
}
// Open the device
int webcam = open(VideoDeviceName, O_RDWR|O_NONBLOCK, 0);
if (-1==webcam)
{
fprintf(stderr, "ERROR: cannot open the device %s\n", VideoDeviceName);
exit(EXIT_FAILURE);
}
// Query the device capacity
struct v4l2_capability cap;
status = xioctl (webcam, VIDIOC_QUERYCAP, &cap);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_QUERYCAP returned status of %d\n", status);
}
if ( !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) )
{
fprintf(stderr, "ERROR: the device does not support image capture\n");
exit(EXIT_FAILURE);
}
if ( !(cap.capabilities & V4L2_CAP_READWRITE) )
{
fprintf(stderr, "WARNING: the device does not support read i/o\n");
}
if ( !(cap.capabilities & V4L2_CAP_STREAMING) )
{
fprintf(stderr, "WARNING: the device does not support streaming\n");
}
// Select video input and video standard, and/or tune TV card hare
struct v4l2_cropcap cropcap;
memset(&cropcap, sizeof(cropcap), 0);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
status = xioctl (webcam, VIDIOC_CROPCAP, &cropcap);
if (0==status)
{
struct v4l2_crop crop;
// memset(&crop, sizeof(crop), 0);
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; // reset to default
status = xioctl (webcam, VIDIOC_S_CROP, &crop);
if (0!=status)
{
if (EINVAL==errno)
fprintf(stderr, "WARNING: the device does not support cropping\n");
else ; // Errors ignored
}
} else {
// Errors ignored
}
struct v4l2_format form;
memset(&form, sizeof(form), 0);
form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
form.fmt.pix.width = 1280;//Here you can change your resolution to whatever your camera is best fit for!!!!!!
form.fmt.pix.height = 960;
// common format: V4L2_PIX_FMT_YUYV, desired format: V4L2_PIX_FMT_RGB24
form.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//As of right now the program uses the YUYV formate to do the color distance calculations
// form.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
form.fmt.pix.field = V4L2_FIELD_INTERLACED;
status = xioctl (webcam, VIDIOC_S_FMT, &form);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_S_FMT returned status of %d (format %d is not supported)\n",
status, (int)form.fmt.pix.pixelformat);
exit(EXIT_FAILURE);
}
// Note VIDIOC_S_FMT may change width and height!
int min = form.fmt.pix.width * 2;
if (form.fmt.pix.bytesperline < min)
form.fmt.pix.bytesperline = min;
min = form.fmt.pix.bytesperline * form.fmt.pix.height;
if (form.fmt.pix.sizeimage < min)
form.fmt.pix.sizeimage = min;
// Demonstrate reading using memory map io (streaming)
if ( cap.capabilities & V4L2_CAP_STREAMING )
{
struct buffer* buffers = NULL;
unsigned int n_buffers = 0;
unsigned int i;
// Initialize buffer
struct v4l2_requestbuffers req;
memset(&req, sizeof(req), 0);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
status = xioctl (webcam, VIDIOC_REQBUFS, &req);
if (0!=status )
{
fprintf(stderr, "ERROR: ioctl VIDIOC_REQBUFS returned status %d\n", status);
exit (EXIT_FAILURE);
}
if (req.count < 2) {
fprintf (stderr, "ERROR: Insufficient buffer memory on %s\n", VideoDeviceName);
exit (EXIT_FAILURE);
}
n_buffers = req.count;
buffers = calloc(n_buffers, sizeof(*buffers));
if (0==buffers) {
fprintf (stderr, "ERROR: Out of memory\n");
exit (EXIT_FAILURE);
}
for (i = 0; i < n_buffers; ++i) {
struct v4l2_buffer buf;
memset(&buf, sizeof(buf), 0);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
status = xioctl (webcam, VIDIOC_QUERYBUF, &buf);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_QUERYBUF for buffer %d returned status %d\n", i, status);
exit (EXIT_FAILURE);
}
buffers[i].length = buf.length;
buffers[i].start = mmap (NULL /* start anywhere */, buf.length,
PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */,
webcam, buf.m.offset);
if (MAP_FAILED==buffers[i].start)
{
fprintf(stderr, "ERROR: MemoryMap failed for buffer %d of %d\n", i, n_buffers);
exit (EXIT_FAILURE);
}
} // end of for loop for each buffer
// Start capturing
for (i = 0; i < n_buffers; ++i)
{
struct v4l2_buffer buf;
memset(&buf, sizeof(buf), 0);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
status = xioctl (webcam, VIDIOC_QBUF, &buf);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_QBUF returned status %d\n", status);
exit (EXIT_FAILURE);
}
}
enum v4l2_buf_type type1;
type1 = V4L2_BUF_TYPE_VIDEO_CAPTURE;
status = xioctl (webcam, VIDIOC_STREAMON, &type1);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_STREAMON returned status %d\n", status);
exit (EXIT_FAILURE);
}
// Let's take 2 frames NOTE: you can change how many frames you take with this for loop but make sure that you have
//enough space if you are taking a very large amount of pictures.
for (i=0; i<2; ++i)
{
// Wait for a new frame to become available
struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
fd_set fds;
FD_ZERO (&fds);
FD_SET (webcam, &fds);
status = select (webcam+1, &fds, NULL, NULL, &timeout);
if (-1==status)
{
if (EINTR==errno) continue;
fprintf (stderr, "ERROR: select failed\n");
exit (EXIT_FAILURE);
}
if (0==status)
{
fprintf (stderr, "ERROR: webcam tiemout\n");
exit (EXIT_FAILURE);
}
// Read one frame
struct v4l2_buffer buf;
memset(&buf, sizeof(buf), 0);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
// Hold onto the buffer that holds the most recently captured image
status = xioctl (webcam, VIDIOC_DQBUF, &buf);
if (0!=status)
{
if (EAGAIN==errno) continue; // read again
else
{
fprintf(stderr, "ERROR: ioctl VIDIOC_DQBUF returned status %d\n", status);
exit(EXIT_FAILURE);
}
}
assert (buf.index < n_buffers);
// process_image (buffers[buf.index].start, buffers[buf.index].length);
{
char* buffer = buffers[buf.index].start;
int bytes_read = buffers[buf.index].length;
char fname[fname_maxlen];
sprintf(fname, "%s-%03d.ppm", CapturedImageNameMM, i);
if (V4L2_PIX_FMT_RGB24==form.fmt.pix.pixelformat)
{
status = ppm_save_rgb24(fname, form.fmt.pix.width, form.fmt.pix.height, buffer, bytes_read);
if (0!=status)
{
fprintf(stderr, "ERROR: cannot write to file %s\n", fname);
free(buffer);
exit(EXIT_FAILURE);
}
} else if (V4L2_PIX_FMT_YUYV==form.fmt.pix.pixelformat){
status = ppm_save_yuyv(fname, form.fmt.pix.width, form.fmt.pix.height, buffer, bytes_read);
if (0!=status)
{
fprintf(stderr, "ERROR: cannot write to file %s\n", fname);
free(buffer);
exit(EXIT_FAILURE);
}
} else {
status=-1; /* format not supported by us */
}
}
// Release hod of the locked recent buffer
status = xioctl (webcam, VIDIOC_QBUF, &buf);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_QBUF returned status %d\n", status);
// exit (EXIT_FAILURE);
}
}
// Stop capturing
enum v4l2_buf_type type2;
type2 = V4L2_BUF_TYPE_VIDEO_CAPTURE;
status = xioctl (webcam, VIDIOC_STREAMOFF, &type2);
if (0!=status)
{
fprintf(stderr, "ERROR: ioctl VIDIOC_STREAMON returned status %d\n", status);
exit (EXIT_FAILURE);
}
// Shutdown buffers
for (i = 0; i < n_buffers; ++i)
status = munmap (buffers[i].start, buffers[i].length);
if (0!=status)
{
fprintf(stderr, "ERROR: MemoryMap unmapping failed\n");
exit (EXIT_FAILURE);
}
}
close (webcam);
return(EXIT_SUCCESS);
}
int ppm_save_rgb24(const char* name, int cols, int rows, void* buffer, int bufferSize)
{
char header[128];
sprintf(header, "P6\n%d %d 255\n", cols, rows);
int file = open (name, O_WRONLY|O_CREAT, 0666);
if (file==0) return(-1);
write (file, header, strlen(header));
write (file, buffer, bufferSize);
close (file);
return(0);
}
static int clamp(int i)
{
if (i<0) return(0);
else if (i<256) return(i);
else return(255);
}
int ppm_save_yuyv(const char* name, int cols, int rows, void* buffer, int bufferSize)
{
char header[128];
sprintf(header, "P6\n%d %d 255\n", cols, rows);
FILE* file = fopen (name, "w");
if (file==0) return(-1);
fputs(header, file);
{
int Y0, Y1, Cb, Cr; // gamma pre-corrected input [0;255]
int y0,y1, pb, pr; // temporaries
char r0,r1,g0,g1,b0,b1; // temporaries
unsigned long long int sumH=0, sumW=0, sumD=0; // use int64_t not 32-bit long int
int dr,dg,db,distance; //dr,dg,db must be signed!
int iterations = cols*rows/2;
int i = 0,row = 0, col = 0;
while( i < iterations )
{
unsigned int packed_value = *((int*)buffer+i);
if(row>rows){row = 0; col++;}
Y0 = (char)(packed_value & 0xFF);
Cb = (char)((packed_value >> 8 ) & 0xFF);
Y1 = (char)((packed_value >> 16 ) & 0xFF);
Cr = (char)((packed_value >> 24 ) & 0xFF);
// Strip sign values after shift (i.e. unsigned shift)
Y0 = Y0 & 0xFF;
Cb = Cb & 0xFF;
Y1 = Y1 & 0xFF;
Cr = Cr & 0xFF;
y0 = 255*(Y0 - 16)/219;
y1 = 255*(Y1 - 16)/219;
pb = 255*(Cb - 128)/224;
pr = 255*(Cr - 128)/224;
// Generate first pixel
r0 = clamp(( 298 * y0 + 409 * pr + 128) >> 8);
g0 = clamp(( 298 * y0 - 100 * pb - 208 * pr + 128) >> 8);
b0 = clamp(( 298 * y0 + 516 * pb + 128) >> 8);
// Generate next pixel - must reuse pb & pr as 4:2:2
r1 = clamp(( 298 * y1 + 409 * pr + 128) >> 8);
g1 = clamp(( 298 * y1 - 100 * pb - 208 * pr + 128) >> 8);
b1 = clamp(( 298 * y1 + 516 * pb + 128) >> 8);
//prints the updated pixel information to the .ppm file
fprintf( file, "%c%c%c%c%c%c",r0,g0,b0,r1,g1,b1); // Output two pixels
i++;
}
}
fclose (file);
return(0);
}
