
Contents of /v4l2-0.1/_v4l2.cmod:
/* Video 4 Linux v2 module for Pike
Copyright Ian Carr-de Avelon 2004-2006
GPL applies
*/
#define VIDEOMAX 1000000
#include "util.h"
#include "math.h"
//Includes for Pike
#include "global.h"
#include "image.h"
#include "interpret.h"
#include "svalue.h"
#include "module.h"
#include "stralloc.h"
#include "array.h"
#include "pike_macros.h"
#include "program.h"
#include "object.h"
#include "interpret.h"
#include "pike_error.h"
#include "pike_types.h"
#include "threads.h"
#include "dynamic_buffer.h"
#include "builtin_functions.h"
//Includes for v4l2
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h> /* for videodev2.h */
#include "videodev2.h"
#include <fcntl.h> /* low-level i/o */
#include <errno.h> /* for errno surprise! */
//#define HAVE_STDINT_H
#define CLEAR(x) memset (&(x), 0, sizeof (x))
struct buffer {
void * start;
size_t length;
};
struct v4l2_state {
struct buffer * buffers;
struct v4l2_buffer buf;
int n_buffers;
};
static int xioctl(int fd, int request, void * arg){
int r;
do r = ioctl (fd, request, arg);
while (-1 == r && EINTR == errno);
return r;
}
PIKECLASS lowlevel {
/* Global variables */
CVAR struct v4l2_state state;
PIKEFUN mapping init(int fd){
int out;
struct v4l2_capability cap;
out=ioctl (fd, VIDIOC_QUERYCAP, &cap);
if(out==-1)
RETURN(0);
//FIXME add checking
pop_n_elems(1);
push_text("driver");
push_string(make_shared_string(cap.driver));
push_text("card");
push_string(make_shared_string(cap.card));
push_text("bus_info");
push_string(make_shared_string(cap.bus_info));
push_text("version");
push_int(cap.version);
push_text("capabilities");
push_int(cap.capabilities);
f_aggregate_mapping(10);
}
PIKEFUN mapping enumerate_inputs(int fd, int index){
int out;
struct v4l2_input in;
in.index=index;
pop_n_elems(2);
out=ioctl (fd, VIDIOC_ENUMINPUT, &in);
if(out==-1)
RETURN(0);
//FIXME add checking
push_text("index");
push_int(in.index);
push_text("name");
push_string(make_shared_string(in.name));
push_text("type");
push_int(in.type);
push_text("audioset");
push_int(in.audioset);
push_text("tuner");
push_int(in.tuner);
// push_text("v4l2_std_id");
// push_int(in.v4l2_std_id);
push_text("status");
push_int(in.status);
f_aggregate_mapping(12);
}
PIKEFUN int current_input(int fd){
int out,*in;
pop_n_elems(1);
out=ioctl (fd, VIDIOC_G_INPUT, in);
if(out==-1)
RETURN(0);
//FIXME add checking
RETURN(*in);
}
PIKEFUN int select_input(int fd){
int out,*in;
pop_n_elems(1);
out=ioctl (fd, VIDIOC_S_INPUT, in);
if(out==-1)
RETURN(0);
//FIXME add checking
RETURN(1);
}
PIKEFUN mapping enumerate_audio(int fd, int index){
int out;
struct v4l2_audio audio;
audio.reserved[0]=0;
audio.reserved[1]=0;
audio.index=index;
out=ioctl (fd, VIDIOC_ENUMAUDIO, &audio);
pop_n_elems(2);
if(out==-1)
RETURN(0);
//FIXME add checking
push_text("index");
push_int(audio.index);
push_text("name");
push_string(make_shared_string(audio.name));
push_text("capability");
push_int(audio.capability);
push_text("mode");
push_int(audio.mode);
f_aggregate_mapping(8);
}
PIKEFUN mapping current_audio(int fd){
int out;
struct v4l2_audio audio;
audio.reserved[0]=0;
audio.reserved[1]=0;
out=ioctl (fd, VIDIOC_G_AUDIO, &audio);
if(out==-1)
RETURN(0);
//FIXME add checking
pop_n_elems(1);
push_text("index");
push_int(audio.index);
push_text("name");
push_string(make_shared_string(audio.name));
push_text("capability");
push_int(audio.capability);
push_text("mode");
push_int(audio.mode);
f_aggregate_mapping(8);
}
PIKEFUN int set_audio(int fd, int index, int mode){
int out;
struct v4l2_audio audio;
audio.reserved[0]=0;
audio.reserved[1]=0;
audio.index=index;
audio.mode=mode;
pop_n_elems(3);
out=ioctl (fd, VIDIOC_S_AUDIO, &audio);
if(out==-1)
RETURN(0);
RETURN(1);
}
PIKEFUN mapping query_tuner(int fd, int index){
int out;
struct v4l2_tuner tuner;
tuner.index=index;
out=ioctl (fd, VIDIOC_G_TUNER, &tuner);
if(out==-1)
RETURN(0);
pop_n_elems(2);
push_text("index");
push_int(tuner.index);
push_text("name");
push_string(make_shared_string(tuner.name));
push_text("type");
push_int((int)tuner.type);
push_text("capability");
push_int(tuner.capability);
push_text("rangelow");
push_int(tuner.rangelow);
push_text("rangehigh");
push_int(tuner.rangehigh);
push_text("rxsubchans");
push_int(tuner.rxsubchans);
f_aggregate_mapping(14);
}
PIKEFUN int set_tuner_mode(int fd, int index, int mode){
int out;
struct v4l2_audio audio;
audio.index=index;
audio.mode=mode;
out=ioctl (fd, VIDIOC_S_TUNER, &audio);
pop_n_elems(3);
if(out==-1)
RETURN(0);
RETURN(1);
}
//FIXME and the frequency
PIKEFUN string current_standard(int fd){
int out;
v4l2_std_id standard;
pop_n_elems(1);
out=ioctl (fd, VIDIOC_G_STD, &standard);
if(out==-1)
RETURN(0);
push_string(make_shared_binary_string((char*)&standard,8));
}
PIKEFUN string set_standard(int fd, string newstd){
int out;
//IAN check string is 8 characters
out=ioctl (fd, VIDIOC_G_STD, &newstd->str);
pop_n_elems(2);
if(out==-1)
RETURN(0);
push_string(make_shared_binary_string((char*)newstd->str,8));
}
PIKEFUN string query_controls(int fd, int id){
int out;
struct v4l2_queryctrl query;
query.id=id;
out=ioctl (fd, VIDIOC_QUERYCTRL, &query);
pop_n_elems(2);
if(out==-1)
RETURN(0);
push_text("id");
push_int(query.id);
push_text("type");
push_int((int)query.type);
push_text("name");
push_string(make_shared_string(query.name));
push_text("minimum");
push_int(query.minimum);
push_text("maximum");
push_int(query.maximum);
push_text("step");
push_int(query.step);
push_text("default_value");
push_int(query.default_value);
push_text("flags");
push_int(query.flags);
f_aggregate_mapping(16);
}
PIKEFUN string query_menu(int fd, int id, int index){
int out;
struct v4l2_querymenu query;
query.id=id;
query.index=index;
out=ioctl (fd, VIDIOC_QUERYMENU, &query);
pop_n_elems(3);
if(out==-1)
RETURN(0);
push_text("id");
push_int(query.id);
push_text("index");
push_int(query.index);
push_text("name");
push_string(make_shared_string(query.name));
f_aggregate_mapping(6);
}
PIKEFUN int set_control(int fd, int id, int value){
int out;
struct v4l2_control set;
set.id=id;
set.value=value;
out=ioctl (fd, VIDIOC_S_CTRL, &set);
pop_n_elems(3);
if(out==-1)
RETURN(0);
RETURN(1);
}
PIKEFUN string enumerate_formats(int fd, int index, int type){
int out;
struct v4l2_fmtdesc query;
query.index=index;
query.type=(enum v4l2_buf_type)type;
out=ioctl (fd, VIDIOC_ENUM_FMT, &query);
pop_n_elems(3);
if(out==-1)
RETURN(0);
push_text("index");
push_int(query.index);
push_text("type");
push_int((int)query.type);
push_text("flags");
push_int(query.flags);
push_text("description");
push_string(make_shared_string(query.description));
push_text("pixelformat");
push_int(query.pixelformat);
f_aggregate_mapping(10);
}
PIKEFUN mapping get_format(int fd){
int out;
struct v4l2_format query;
out=ioctl (fd, VIDIOC_G_FMT, &query);
//if(out==-1)
// RETURN(0);
pop_n_elems(1);
push_text("type");
push_int((int)query.type);
/* FIXME check which type and add information in the union
//v4l2_pix_format
push_text("width");
push_int(query.pix.width);
push_text("height");
push_int(query.pix.height);
push_text("pixelformat");
push_int(query.pix.pixelformat);
push_text("field");
push_int((int *)query.pix.field);
push_text("bytesperline");
push_int(query.pix.bytesperline);
push_text("sizeimage");
push_int(query.pix.sizeimage);
push_text("colorspace");
push_int((int *)query.pix.colorspace);
//v4l2_window
//v4l2_rect
push_text("left");
push_int(query.win.w.left);
push_text("top");
push_int(query.win.w.top);
push_text("width");
push_int(query.win.w.width);
push_text("height");
push_int(query.win.w.height);
push_text("field");
push_int((int *)query.win.field);
push_text("chromakey");
push_int(query.win.chromakey);
//FIXME there can be multiple cliping frames
//v4l2_clip
//v4l2_rect
push_text("clip left");
push_int(query.win.clips.c.left);
push_text("clip top");
push_int(query.win.clips.c.top);
push_text("clip width");
push_int(query.win.clips.c.width);
push_text("clip height");
push_int(query.win.clips.c.height);
push_text("clipcount");
push_int(query.win.clipcount);
f_aggregate_mapping(38);
*/
f_aggregate_mapping(2);
}
PIKEFUN int device_init(int fd, int width, int height){
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
unsigned int min;
pop_n_elems(3);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
switch (errno) {
case EINVAL:
/* Cropping not supported. */
break;
default:
/* Errors ignored. */
break;
}
}
} else {
/* Errors ignored. */
}
CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = width;
fmt.fmt.pix.height = height;
//saa7134 V4L2_PIX_FMT_RGB24 snafu
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)){
push_int(-1);
return;
}
/* Note VIDIOC_S_FMT may change width and height. */
/* Buggy driver paranoia. */
min = fmt.fmt.pix.width * 2;
if (fmt.fmt.pix.bytesperline < min)
fmt.fmt.pix.bytesperline = min;
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if (fmt.fmt.pix.sizeimage < min)
fmt.fmt.pix.sizeimage = min;
push_int(1);
return;
}
//start grabbing frames using user pointers.
//"no" is the number of buffers to setup and queue
PIKEFUN int start(int fd, int no){
int i;
enum v4l2_buf_type type;
struct v4l2_requestbuffers reqbuf;
pop_n_elems(2);
memset (&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = no;
if (-1 == ioctl (fd, VIDIOC_REQBUFS, &reqbuf)) {
if (errno == EINVAL){
push_int(-1);
return;
}
}
THIS->state.buffers = calloc (reqbuf.count, sizeof (*THIS->state.buffers));
//assert (THIS->state.buffers != NULL);
if(THIS->state.buffers == NULL){
push_int(-10);
return;
}
for (i = 0; i < reqbuf.count; i++) {
struct v4l2_buffer buffer;
memset (&buffer, 0, sizeof (buffer));
buffer.type = reqbuf.type;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buffer)) {
push_int(-2);
return;
}
THIS->state.buffers[i].length = buffer.length; /* remember for munmap() */
THIS->state.buffers[i].start = mmap (NULL, buffer.length,
PROT_READ | PROT_WRITE, /* required */
/* MAP_SHARED, / * recommended */
/* MAP_PRIVATE|MAP_ANONYMOUS, / * other Pike uses */
MAP_SHARED,
fd, buffer.m.offset);
if (THIS->state.buffers[i].start == MAP_FAILED) {
/* You may need to unmap and free the so far
mapped buffers here. */
push_int(errno);
// push_int(-9);
return;
}
}
for (i = 0; i < reqbuf.count; ++i) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)){
// push_int(-2*i);
push_int(errno);
return;
}
}
THIS->state.n_buffers=reqbuf.count;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)){
push_int(-4);
return;
}
push_int(reqbuf.count);
return;
}
PIKEFUN int stop(int fd){
enum v4l2_buf_type type;
int i;
pop_n_elems(1);
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type)){
push_int(0);
return;
}
for (i = 0; i < THIS->state.n_buffers; ++i) {
munmap (THIS->state.buffers[i].start, THIS->state.buffers[i].length);
}
RETURN(1);
}
PIKEFUN string get_data(int fd){
int i;
struct v4l2_buffer buf;
pop_n_elems(1);
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
push_string(make_shared_string((char *)strerror(errno)));
return;
}
if(buf.index >= THIS->state.n_buffers){
push_int(-2);
return;
}
//process_image ((void *) buf.m.userptr);
push_string(make_shared_binary_string((char *)THIS->state.buffers[buf.index].start,THIS->state.buffers[buf.index].length));
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)){
push_int(-3);
return;
}
return;
}
PIKEFUN object get_image(object rgb, int fd){
struct image *img = NULL;
img=(struct image *)rgb->storage;
rgb_group *pixels;
pixels=img->img;
int i;
char save;
COLORTYPE *data;
struct v4l2_buffer buf;
pop_n_elems(2);
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
push_string(make_shared_string((char *)strerror(errno)));
return;
}
if(buf.index >= THIS->state.n_buffers){
push_int(-2);
return;
}
//memcpy(img->img,(rgb_group *)THIS->state.buffers[buf.index].start,(size_t)THIS->state.buffers[buf.index].length);
//The kernel module gives us RGB, and could give us BGR, but Pike seems to
//use GRB
data=(__u8*)THIS->state.buffers[buf.index].start;
// 3green shown blue, 2red shown green,
for(i=0;i<img->xsize*img->ysize;i++){
pixels->b=*data++;
pixels->r=*data++;
pixels->g=*data++;
pixels++;
data++;
}
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)){
push_int(-3);
return;
}
ref_push_object(rgb);
return;
}
}//end class