topical media & game development
lib-of-vs-libs-openFrameworks-video-ofV4LUtils.h / h
/*
* Revised openFrameworks v4L code
*
* This version uses openCV conversions of YUV420/YUV420P and YUV411P colour models,
* therefore the license agreement must accompany this file.
* Dec 9 2007
* Pierre Proske
*/
//////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//
ifndef _V4L_UTILS
define _V4L_UTILS
include <ofConstants.h>
include <stdio.h>
include <stdlib.h>
include <string.h>
include <errno.h>
include <fcntl.h>
include <unistd.h>
include <sys/ioctl.h>
include <sys/stat.h>
include <sys/mman.h>
include <sys/types.h>
include <time.h>
//#include <asm/types.h> /* for videodev2.h */
include <linux/videodev.h>
define HAVE_V4L 1
/* V4L structures */
static struct video_capability capability;
static struct video_window window;
static struct video_picture imageProperties;
static struct video_mbuf mbuf;
static struct video_mmap vmmap;
static int deviceHandle;
static int frameIndex;
static bool bFirstFrame;
static unsigned char *bigbuf;
/* V4L functions */
bool openV4L_device(const char *device_name);
void initialiseV4L_device(const char *device_name);
bool initV4L(int width, int height,const char *devname);
bool queryV4L_imageProperties(void);
bool setV4L_palette(int palette, int depth);
bool setV4L_imageProperties(void);
bool setV4L_videoSize(int width, int height);
bool getFrameV4L(unsigned char * pixels);
void closeV4L(void);
bool mmapV4L(void);
int getV4L_Height();
int getV4L_Width();
/*
* Turn a YUV4:2:0 block into an RGB block
*
* Video4Linux order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
*
* Y values are given for all 4 pixels, but the U (Pb)
* and V (Pr) are assumed constant over the 2x2 block.
*
* To avoid floating point arithmetic, the color conversion
* coefficients are scaled into 16.16 fixed-point integers.
* They were determined as follows:
*
* double brightness = 1.0; (0->black; 1->full scale)
* double saturation = 1.0; (0->greyscale; 1->full color)
* double fixScale = brightness * 256 * 256;
* int rvScale = (int)(1.402 * saturation * fixScale);
* int guScale = (int)(-0.344136 * saturation * fixScale);
* int gvScale = (int)(-0.714136 * saturation * fixScale);
* int buScale = (int)(1.772 * saturation * fixScale);
* int yScale = (int)(fixScale);
*/
/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
static inline void
move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int rowPixels, unsigned char * rgb)
{
const int rvScale = 91881;
const int guScale = -22553;
const int gvScale = -46801;
const int buScale = 116129;
const int yScale = 65536;
int r, g, b;
g = guScale * u + gvScale * v;
r = rvScale * v;
b = buScale * u;
yTL *= yScale; yTR *= yScale;
yBL *= yScale; yBR *= yScale;
/* Write out top two pixels */
rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
rgb[2] = LIMIT(r+yTL);
rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
rgb[5] = LIMIT(r+yTR);
/* Skip down to next line to write out bottom two pixels */
rgb += 3 * rowPixels;
rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
rgb[2] = LIMIT(r+yBL);
rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
rgb[5] = LIMIT(r+yBR);
}
static inline void
move_411_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
int rowPixels, unsigned char * rgb)
{
const int rvScale = 91881;
const int guScale = -22553;
const int gvScale = -46801;
const int buScale = 116129;
const int yScale = 65536;
int r, g, b;
g = guScale * u + gvScale * v;
r = rvScale * v;
b = buScale * u;
yTL *= yScale; yTR *= yScale;
yBL *= yScale; yBR *= yScale;
/* Write out top two first pixels */
rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
rgb[2] = LIMIT(r+yTL);
rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
rgb[5] = LIMIT(r+yTR);
/* Write out top two last pixels */
rgb += 6;
rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
rgb[2] = LIMIT(r+yBL);
rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
rgb[5] = LIMIT(r+yBR);
}
// Consider a YUV420P image of 8x2 pixels.
//
// A plane of Y values A B C D E F G H
// I J K L M N O P
//
// A plane of U values 1 2 3 4
// A plane of V values 1 2 3 4 ....
//
// The U1/V1 samples correspond to the ABIJ pixels.
// U2/V2 samples correspond to the CDKL pixels.
//
/* Converts from planar YUV420P to RGB24. */
static void
yuv420p_to_rgb24(int width, int height, unsigned char *pIn0, unsigned char *pOut0)
{
const int numpix = width * height;
const int bytes = 24 >> 3;
int i, j, y00, y01, y10, y11, u, v;
unsigned char *pY = pIn0;
unsigned char *pU = pY + numpix;
unsigned char *pV = pU + numpix / 4;
unsigned char *pOut = pOut0;
for (j = 0; j <= height - 2; j += 2) {
for (i = 0; i <= width - 2; i += 2) {
y00 = *pY;
y01 = *(pY + 1);
y10 = *(pY + width);
y11 = *(pY + width + 1);
u = (*pU++) - 128;
v = (*pV++) - 128;
move_420_block(y00, y01, y10, y11, u, v,
width, pOut);
pY += 2;
pOut += 2 * bytes;
}
pY += width;
pOut += width * bytes;
}
}
// Consider a YUV411P image of 8x2 pixels.
//
// A plane of Y values as before.
//
// A plane of U values 1 2
// 3 4
//
// A plane of V values 1 2
// 3 4
//
// The U1/V1 samples correspond to the ABCD pixels.
// U2/V2 samples correspond to the EFGH pixels.
//
/* Converts from planar YUV411P to RGB24. */
/* untested... */
static void
yuv411p_to_rgb24(int width, int height,
unsigned char *pIn0, unsigned char *pOut0)
{
const int numpix = width * height;
const int bytes = 24 >> 3;
int i, j, y00, y01, y10, y11, u, v;
unsigned char *pY = pIn0;
unsigned char *pU = pY + numpix;
unsigned char *pV = pU + numpix / 4;
unsigned char *pOut = pOut0;
for (j = 0; j <= height; j++) {
for (i = 0; i <= width - 4; i += 4) {
y00 = *pY;
y01 = *(pY + 1);
y10 = *(pY + 2);
y11 = *(pY + 3);
u = (*pU++) - 128;
v = (*pV++) - 128;
move_411_block(y00, y01, y10, y11, u, v,
width, pOut);
pY += 4;
pOut += 4 * bytes;
}
}
}
static void errno_exit (const char *s)
{
printf ("\%s error \%d, \%s\n",s, errno, strerror (errno));
exit (EXIT_FAILURE);
}
static int xioctl (int deviceHandle, int request, void *arg)
{
int r;
do r = ioctl (deviceHandle, request, arg);
while (-1 == r && EINTR == errno);
return r;
}
bool openV4L_device(const char *device_name)
{
struct stat st;
if (stat (device_name, &st) == -1) {
printf("V4L : Cannot identify '\%s': \%d, \%s\n", device_name, errno, strerror (errno));
exit (EXIT_FAILURE);
}
if (!S_ISCHR (st.st_mode)) {
printf("V4L : \%s is no device\n", device_name);
exit (EXIT_FAILURE);
}
deviceHandle = open(device_name, O_RDWR | O_NONBLOCK, 0);
if (deviceHandle == -1) {
printf("V4L : Cannot open '\%s': \%d, \%s\n", device_name, errno, strerror (errno));
close(deviceHandle);
exit (EXIT_FAILURE);
}
return true;
}
bool queryV4L_imageProperties(void) {
memset(&imageProperties, 0, sizeof(struct video_picture)); // clear v4l image properties
if (xioctl(deviceHandle, VIDIOCGPICT, &imageProperties) == -1) {
printf("V4L : Unable to determine image properties\n");
errno_exit ("VIDIOCGPICT");
}
printf("V4L : Brightness\t= \%u\n",imageProperties.brightness);
printf("V4L : Hue\t\t= \%u\n",imageProperties.hue);
printf("V4L : Colour\t\t= \%u\n",imageProperties.colour);
printf("V4L : Depth\t\t= \%u bits\n",imageProperties.depth);
printf("V4L : Palette\t\t= ");
switch(imageProperties.palette) {
case VIDEO_PALETTE_GREY: // 1
printf("VIDEO_PALETTE_GREY\n");
break;
case VIDEO_PALETTE_HI240: // 2
printf("VIDEO_PALETTE_HI240\n");
break;
case VIDEO_PALETTE_RGB565: // 3
printf("VIDEO_PALETTE_RGB565\n");
break;
case VIDEO_PALETTE_RGB24: // 4
printf("VIDEO_PALETTE_RGB24\n");
break;
case VIDEO_PALETTE_RGB32: // 5
printf("VIDEO_PALETTE_RGB32\n");
break;
case VIDEO_PALETTE_RGB555: // 6
printf("VIDEO_PALETTE_RGB555\n");
break;
case VIDEO_PALETTE_YUV422: // 7
printf("VIDEO_PALETTE_YUV422\n");
break;
case VIDEO_PALETTE_YUYV: // 8
printf("VIDEO_PALETTE_YUYV\n");
break;
case VIDEO_PALETTE_UYVY: // 9
printf("VIDEO_PALETTE_UYVY\n");
break;
case VIDEO_PALETTE_YUV420: // 10
printf("VIDEO_PALETTE_YUV420\n");
break;
case VIDEO_PALETTE_YUV411: // 11
printf("VIDEO_PALETTE_YUV411\n");
break;
case VIDEO_PALETTE_RAW: // 12
printf("VIDEO_PALETTE_RAW\n");
break;
case VIDEO_PALETTE_YUV422P: // 13
printf("VIDEO_PALETTE_YUV422P\n");
break;
case VIDEO_PALETTE_YUV411P: // 14
printf("VIDEO_PALETTE_YUV411P\n");
break;
case VIDEO_PALETTE_YUV420P: // 15
printf("VIDEO_PALETTE_YUV420P\n");
break;
case VIDEO_PALETTE_YUV410P: // 16
printf("VIDEO_PALETTE_YUV410P\n");
break;
default:
printf(" Couldn't read palette\n");
break;
}
return true;
}
bool setV4L_palette(int palette, int depth) {
imageProperties.palette = palette;
imageProperties.depth = depth;
if (xioctl(deviceHandle, VIDIOCSPICT, &imageProperties) < 0) {
printf("V4L : Failed to set image properties : \%d, \%s\n", errno, strerror (errno));
return false;
}
if ((imageProperties.palette == palette) && (imageProperties.depth == depth)) {
return true;
}
return false;
}
bool setV4L_imageProperties(void) {
if (setV4L_palette(VIDEO_PALETTE_RGB24, 24)) {
printf("V4L : Changed current palette to VIDEO_PALETTE_RGB24\n");
}
else
if (setV4L_palette(VIDEO_PALETTE_YUV420, 16)) {
printf("V4L : Changed current palette to VIDEO_PALETTE_YUV420\n");
}
else
if (setV4L_palette(VIDEO_PALETTE_YUV420P, 16)) {
printf("V4L : Changed current palette to VIDEO_PALETTE_YUV420P\n");
}
else
if (setV4L_palette(VIDEO_PALETTE_YUV411P, 16)) {
printf("V4L : Changed current palette to VIDEO_PALETTE_YUV411P\n");
}
else {
printf("V4L : ERROR : Unable to change to a suitable palette\n");
return false;
}
return true;
}
void initialiseV4L_device(const char *device_name) {
if (xioctl(deviceHandle, VIDIOCGCAP, &capability) == -1) {
if (errno == EINVAL) {
printf("V4L : \%s is no V4L device\n",device_name);
exit (EXIT_FAILURE);
} else {
errno_exit ("VIDIOC_QUERYCAP");
}
}
if (!(capability.type & VID_TYPE_CAPTURE)) {
printf("V4L : \%s is no video capture device\n", device_name);
exit (EXIT_FAILURE);
} else {
printf("V4L : Name = '\%s'\n",capability.name);
printf("V4L : Dimensions (\%i x \%i) - (\%i x \%i)\n", capability.minwidth, capability.minheight, capability.maxwidth,capability.maxheight);
printf("V4L : Capability :\n");
if (capability.type & VID_TYPE_CAPTURE ) printf(" - CAPTURE\n");
if (capability.type & VID_TYPE_TUNER ) printf(" - TUNER\n");
if (capability.type & VID_TYPE_TELETEXT ) printf(" - TELETEXT\n");
if (capability.type & VID_TYPE_OVERLAY ) printf(" - OVERLAY\n");
if (capability.type & VID_TYPE_CHROMAKEY ) printf(" - CHROMAKEY\n");
if (capability.type & VID_TYPE_CLIPPING ) printf(" - CLIPPING\n");
if (capability.type & VID_TYPE_FRAMERAM ) printf(" - FRAMERAM\n");
if (capability.type & VID_TYPE_SCALES ) printf(" - SCALES\n");
if (capability.type & VID_TYPE_MONOCHROME ) printf(" - MONOCHROME\n");
if (capability.type & VID_TYPE_SUBCAPTURE ) printf(" - SUBCAPTURE\n");
}
queryV4L_imageProperties();
setV4L_imageProperties();
}
void writePPM(unsigned char *data, char *filename, int width, int height) {
int num;
int size = width * height * 3;
FILE *fp = fopen(filename, "w");
if (!fp) {printf("V4L : cannot open file for writing!\n");}
fprintf(fp, "P6\n\%d \%d\n\%d\n", width, height, 255);
num = fwrite((void *) data, 1, (size_t) size, fp);
if (num != size) {printf("V4L : cannot write image data to file\n");}
fclose(fp);
}
void swap_rgb24(unsigned char *mem, int n)
{
unsigned char c;
unsigned char *p = mem;
int i = n;
while(--i) {
c = p[0];
p[0] = p[2];
p[2] = c;
p += 3;
}
}
bool getFrameV4L(unsigned char * pixels) {
/* Check space for grabbing RGB data */
if (pixels == NULL) {
printf("V4L : ERROR : pixel data not not allocated.\n");
return false;
}
if (bFirstFrame) {
bFirstFrame = false;
/* Queue all available buffers for capture */
for (frameIndex = 0;frameIndex < (mbuf.frames);++frameIndex) {
vmmap.frame = frameIndex;
vmmap.width = window.width;
vmmap.height = window.height;
vmmap.format = imageProperties.palette;
if (xioctl(deviceHandle, VIDIOCMCAPTURE, &vmmap) == -1) {
printf("V4L : ERROR: Could not make initial capture\n");
return false;
}
}
/* reset frame index*/
frameIndex = 0;
}
/* Sync to video */
if (xioctl(deviceHandle, VIDIOCSYNC, &frameIndex) == -1) {
printf("V4L : ERROR: VIDIOCSYNC failed. \%s\n", strerror(errno));
}
/* Save current frame */
switch(imageProperties.palette) {
case VIDEO_PALETTE_RGB24:
{
int imagesize = window.width*window.height*(imageProperties.depth/8);
memcpy((unsigned char*)pixels, (unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),imagesize);
swap_rgb24(pixels, window.width * window.height);
}
break;
case VIDEO_PALETTE_YUV420P:
{
// convert yuv420p_to_rgb24
yuv420p_to_rgb24(window.width,window.height,(unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),(unsigned char*) pixels);
swap_rgb24(pixels, window.width * window.height);
}
break;
case VIDEO_PALETTE_YUV420:
{
// convert yuv420_to_rgb24....yuv420 is the same as yuv420p from what I've gathered...
yuv420p_to_rgb24(window.width,window.height,(unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),(unsigned char*) pixels);
swap_rgb24(pixels, window.width * window.height);
}
break;
case VIDEO_PALETTE_YUV411P:
{
// convert yuv411p_to_rgb24
yuv411p_to_rgb24(window.width,window.height,(unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),(unsigned char*) pixels);
swap_rgb24(pixels, window.width * window.height);
}
break;
default:
printf("V4L : ERROR: Cannot convert from palette \%d to RGB\n",imageProperties.palette);
return false;
}
vmmap.frame = frameIndex;
vmmap.width = window.width;
vmmap.height = window.height;
vmmap.format = imageProperties.palette;
/* Capture video */
if (xioctl (deviceHandle, VIDIOCMCAPTURE, &vmmap) == -1) {
printf("V4L : ERROR: Could not capture...\n");
return false;
}
++frameIndex;
if (frameIndex == mbuf.frames) {
frameIndex = 0;
}
return true;
}
bool setV4L_videoSize(int w, int h) {
if (!(capability.type & VID_TYPE_CAPTURE)) {
printf("V4L : Not a capture device! Exiting...\n");
return false;
}
if (xioctl(deviceHandle, VIDIOCGWIN, &window) < 0) {
printf("V4L : Could not get video size : \%d, \%s\n", errno, strerror (errno));
return false;
}
if (w > capability.maxwidth) w = capability.maxwidth;
if (h > capability.maxheight) h = capability.maxheight;
window.width = w;
window.height= h;
window.x = window.y = window.chromakey = window.flags = 0;
if (xioctl(deviceHandle, VIDIOCSWIN, &window) < 0) {
printf("V4L : ERROR : Could not set video size : \%d, \%s\n", errno, strerror (errno));
return false;
}
/* Get the video capabilities to check that the set worked */
if (xioctl(deviceHandle, VIDIOCGWIN, &window) < 0) {
printf("V4L : Could not get video size : \%d, \%s\n", errno, strerror (errno));
return false;
}
printf("V4L : Video size changed to width=\%i height=\%i\n",window.width,window.height);
return true;
}
bool mmapV4L(void) {
if (xioctl(deviceHandle, VIDIOCGMBUF, &mbuf) < 0) {
printf("V4L : Could not use mmap : \%d, \%s\n", errno, strerror (errno));
}
/* Get memory mapped io */
/* create a large buffer that can hold up to the maximum frames at the maximum resolution*/
bigbuf = (unsigned char*) mmap(0, mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, deviceHandle, 0);
if (bigbuf == MAP_FAILED) {
printf("V4L : Could not use mmap buffer.\n");
closeV4L();
}
return true;
}
void closeV4L(void) {
printf("V4L : Shutting down....\n");
close(deviceHandle); // Close device
munmap(bigbuf, mbuf.size); // Un-mmap memory
}
bool initV4L(int width, int height, const char *devname) {
bFirstFrame = true;
bigbuf = NULL;
openV4L_device(devname);
initialiseV4L_device(devname);
if(!setV4L_videoSize(width,height))
exit(1);
mmapV4L();
return true;
}
int getV4L_Height(void) {
if(window.height)
return window.height;
}
int getV4L_Width(void) {
if(window.width)
return window.width;
}
endif
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.