Video4Linux HowTo: Come realizzare un semplice frame grabber
Da MelugWiki.
by emitrax_at_gmail_dot_com
Indice |
Premessa
Questo semplice howto, ha lo scopo di dare un' introduzione alle video4linux (versione 2 a.k.a. v4l2) tramite la realizzazione di un semplice frame grabber da poter utilizzare con una qualsiasi webcam supportata da linux.
Introduzione
Video4linux è un sottosistema del kernel linux il cui obiettivo è quello di fornire un'interfaccia standard e quindi delle API per la gestione delle periferiche come webcams, schede tv e schede radio. Esistono due versioni: le V4L e le V4L2. Le ultime sono una versione successiva delle prime, e risolvono alcuni problemi di design. Purtoppo le due versioni hanno interfacce completamente diverse tra di loro, quindi applicazioni sviluppate con le v4l2 non funzioneranno con driver che supportano solo le v4l. Esiste un layer per la compatibilità tra le due versioni, ma pecca di alcune funzione e personalmente non mi è mai funzionato.
In ogni caso il meccanismo di funzionamento è fondamentalmente lo stesso, cambiano solo le strutture e le ioctls . E' ovviamente consigliato l'utilizzo delle v4l2, in quanto le v4l a breve scompariranno, anche se a tutt'oggi molti driver per le webcam sono ancora solo v4l compliant.
Open
La prima cosa da fare è aprire il file che rappresenta il nostro dispositivo. In genere si tratta di /dev/video0 per una webcam o una scheda tv. Quindi è sufficiente il seguente codice
int video_fd;
if( (video_fd = open( "/dev/video0", O_RDWR ) ) < 1 )
{
perror( "Open " );
exit(EXIT_FAILURE);
}
video_fd da adesso in poi sarà il vostro riferimento al dispositivo.
Metodi di acquisizione
Fondamentalmente esistono 3 metodi di acquisizione. Per sapere quali supporta il vostro driver bisogna interrogarlo con la seguente ioctl ed analizzare la risposta facendo l'AND logico con alcune macro.
struct v4l2_capability vcap;
if( ( ioctl( video_fd, VIDIOC_QUERYCAP, &vcap )) != 0 )
{
perror( "VIDIOC_QUERYCAP" );
exit(EXIT_FAILURE);
}
printf( "\nDriver name : %s" \
"\nCard name : %s" \
"\nBus Info : %s" \
"\nCapabilities:"
, vcap.driver, vcap.card, vcap.bus_info );
if( vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE )
printf(" V4L2_CAP_VIDEO_CAPTURE ");
if( vcap.capabilities & V4L2_CAP_READWRITE )
printf(" V4L2_CAP_READWRITE ");
if( vcap.capabilities & V4L2_CAP_STREAMING )
printf(" V4L2_CAP_STREAMING ");
Per avere una lista completa delle capability guardate il file videodev2.h presente negli include del kernel linux.
Read/Write
Se la VIDIOC_QUERYCAP ioctl restituisce la V4L2_CAP_READWRITE significa che possiamo usare il metodo piu semplice di acquisizione che esista. Basta infatti aprire il dispositivo ed inziare ad usare la read per acquisire dati dalla webcam. Ovviamente si tratta di un metodo molto spartano e poco utilizzato. Per lo piu credo che serva per debuggare ed acquisire qualche frame in maniera molto sbrigativa. Un esempio potrebbe essere
dd if=/dev/video of=myimage.422 bs=76800 count=1
nel caso il frame di default abbia una dimensione di 360x240.
Memory mapping
Questo è il metodo piu usato e che utilizzerò in questo howto. Per poterlo utilizzare bisogna prima assicurarsi che venga restituita V4L2_CAP_STREAMING da VIDIOC_QUERYCAP e poi chiamare la VIDIOC_REQBUFS passando il parametro V4L2_MEMORY_MMAP per vedere se è supportato questo metodo di streaming, in quanto esistono due metodi di streaming.
struct v4l2_requestbuffers reqbuf;
memset (&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 3;
if (-1 == ioctl (fd, VIDIOC_REQBUFS, &reqbuf))
{
if (errno == EINVAL)
printf ("Video capturing or mmap-streaming is not supported\n");
else
perror ("VIDIOC_REQBUFS");
exit (EXIT_FAILURE);
}
Con questo metodo si mappa il file che rappresenta il dispositivo in memoria.
User pointers
Questo è un altro metodo di streaming I/O, e come nel caso precedente, prima di poterlo utilizzare dobbiamo assicurarci che sia supportato dal driver che utilizziamo noi e quindi controllare che venga restituita V4L2_CAP_STREAMING da VIDIOC_QUERYCAP e poi chiamare la VIDIOC_REQBUFS con V4L2_MEMORY_USERPTR.
struct v4l2_requestbuffers reqbuf;
memset (&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_USERPTR;
if (ioctl (fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
if (errno == EINVAL)
printf ("Video capturing or user pointer streaming is not supported\n");
else
perror ("VIDIOC_REQBUFS");
exit (EXIT_FAILURE);
}
questo metodo combina i vantaggi dei due precenti metodi di I/O. I buffers vengono allocati in user space dall'applicazione e vengono passati soltanto i puntatori alle aree di memoria e le meta informazioni con v4l2_buffer.
Settiamo i buffers
Avendo deciso di utilizzare il secondo metodo di acquisizione, quello con mmap, per prima cosa dobbiamo allocare i buffers per poi mappare la memoria.
struct {
void *start;
size_t length;
} *buffers;
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))
{
perror ("VIDIOC_QUERYBUF");
exit (EXIT_FAILURE);
}
buffers[i].length = buffer.length; /* remember for munmap() */
buffers[i].start = mmap (NULL, buffer.length,
PROT_READ | PROT_WRITE, /* required */
MAP_SHARED, /* recommended */
fd, buffer.m.offset);
if (buffers[i].start == MAP_FAILED)
{
/* You may need to unmap and free the so far
* mapped buffers here. */
perror ("mmap");
exit (EXIT_FAILURE);
}
else
{
if( (ioctl(fd, VIDIOC_QBUF, &buffer)) != 0 )
{
perror("VIDIOC_QBUF");
exit(EXIT_FAILURE);
}
}
}