Catturare un'immagine con una webcam mediante le funzioni esterne del API di libv4l2
La risorsa libv4l è una raccolta di librerie per la gestione dei dispositivi video4linux2 evitando che si debba scrivere del codice separato, nella stessa classe, per i diversi dispositivi. libv4l è composta da tre librerie differenti: libv4lconvert, libv4l1 e libv4l2.
La libreria libv4l2, che qui ci interessa, mette a disposizione l'API v4l2 per i dispositivi v4l2.
Per poter fruire delle risorse fornite dalla libreria v4l2 è necessario richiamare nell'applicazione Gambas la libreria condivisa: "libv4l2.so.0.0.0 ".
Mostriamo di seguito un possibile codice per la cattura di un'immagine attraverso una webcam.
L'immagine sarà salvata in un file immagine di formato "PPM":
Public Struct Buffer start As Pointer length As Integer End Struct Library "libc:6" Public Struct timeval tv_sec As Long tv_usec As Long End Struct Public Struct v4l2_requestbuffers count As Integer type As Integer memory_ As Integer capabilities As Integer flags As Byte reserved[3] As Byte End Struct Public Struct v4l2_timecode type As Integer flags As Integer frames As Byte seconds As Byte minutes As Byte hours As Byte userbits[4] As Byte End Struct Public Struct v4l2_buffer index As Integer type As Integer bytesused As Integer flags As Integer field As Integer timestamp As Struct Timeval timecode As Struct V4l2_timecode sequence As Integer memory_ As Integer m As Long length As Integer reserved2 As Integer request_fd As Integer End Struct Private Enum O_RDONLY = 0, O_WRONLY, O_RDWR, O_NONBLOCK = &2000 Private Enum PROT_NONE = 0, PROT_READ, PROT_WRITE Private Enum V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_BUF_TYPE_VBI_OUTPUT, V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_BUF_TYPE_SDR_CAPTURE, V4L2_BUF_TYPE_SDR_OUTPUT, V4L2_BUF_TYPE_META_CAPTURE, V4L2_BUF_TYPE_META_OUTPUT Private Enum V4L2_FIELD_ANY, V4L2_FIELD_NONE, V4L2_FIELD_TOP, V4L2_FIELD_BOTTOM, V4L2_FIELD_INTERLACED, V4L2_FIELD_SEQ_TB, V4L2_FIELD_SEQ_BT, V4L2_FIELD_ALTERNATE, V4L2_FIELD_INTERLACED_TB, V4L2_FIELD_INTERLACED_BT Private Enum V4L2_MEMORY_MMAP = 1, V4L2_MEMORY_USERPTR, V4L2_MEMORY_OVERLAY, V4L2_MEMORY_DMABUF Private Const V4L2_PIX_FMT_RGB24 As Integer = 859981650 Private Const VIDIOC_S_FMT As Long = 3234878981 Private Const VIDIOC_REQBUFS As Long = 3222558216 Private Const VIDIOC_QUERYBUF As Long = 3227014665 Private Const VIDIOC_QBUF As Long = 3227014671 Private Const VIDIOC_STREAMON As Long = 1074026002 Private Const VIDIOC_STREAMOFF As Long = 1074026003 Private Const VIDIOC_DQBUF As Long = 3227014673 Private Const MAP_SHARED As Integer = 1 ' int select (int __nfds, fd_set *__restrict __readfds, fd_set *__restrict __writefds, fd_set *__restrict __exceptfds, struct timeval *__restrict __timeout) ' Blocks the calling process until there is activity on any of the specified sets of file descriptors, or until the timeout period has expired. Private Extern select_C(nfds As Integer, readfds As Pointer, writefds As Pointer, exceptfds As Pointer, timeout As Timeval) As Integer Exec "select" Library "libv4l2:0.0.0" ' int v4l2_open(const char *file, int oflag, ...) ' Open a V4L2 device. Private Extern v4l2_open(fl As String, oflag As Integer, alterum As Integer) As Integer ' int v4l2_ioctl(int fd, unsigned long int request, ...) ' Program a V4L2 device. Private Extern v4l2_ioctl_pointer(fd As Integer, request As Long, arg As Pointer) As Integer Exec "v4l2_ioctl" ' int v4l2_ioctl(int fd, unsigned long int request, ...) ' Program a V4L2 device. Private Extern v4l2_ioctl_buffer(fd As Integer, request As Long, arg As V4l2_buffer) As Integer Exec "v4l2_ioctl" ' int v4l2_ioctl(int fd, unsigned long int request, ...) ' Program a V4L2 device. Private Extern v4l2_ioctl_request(fd As Integer, request As Long, arg As V4l2_requestbuffers) As Integer Exec "v4l2_ioctl" ' void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, int64_t offset) ' Map device memory into application address space. Private Extern v4l2_mmap(start As Pointer, lengthI As Integer, prot As Integer, flags As Integer, fd As Integer, offset As Long) As Pointer ' int v4l2_munmap(void *start, size_t length) ' Unmap device memory. Private Extern v4l2_munmap(startP As Pointer, lengthI As Integer) As Integer ' int v4l2_close(int fd) ' Close a V4L2 device. Private Extern v4l2_close(fd As Integer) Public Sub Main() Dim buf As New V4l2_buffer Dim req As New V4l2_requestbuffers Dim buffers As Buffer[] Dim tv As New Timeval Dim fout As File Dim fmt, p As Pointer Dim st As Stream Dim fd, r, width, height, pix, type As Integer Dim ppm As String If Not Exist("/tmp/V4L") Then Mkdir "/tmp/V4L" Write "\e[31m\e[5mAcquisizione immagine...\e[0m" Flush fd = v4l2_open("/dev/video0", O_RDWR Or O_NONBLOCK, 0) fmt = Alloc(SizeOf(gb.Byte), 208) st = Memory fmt For Read Write Write #st, V4L2_BUF_TYPE_VIDEO_CAPTURE As Integer Seek #st, 8 Write #st, 640 As Integer Write #st, 480 As Integer Write #st, V4L2_PIX_FMT_RGB24 As Integer Write #st, V4L2_FIELD_INTERLACED As Integer Repeat r = v4l2_ioctl_pointer(fd, VIDIOC_S_FMT, fmt) If r = -1 Then Error.Raise("Errore !") Until r > -1 Seek #st, 8 Read #st, width Read #st, height Read #st, pix If (width <> 640) Or (height <> 480) Then Print "Attenzione: il dispositivo imposterà l'immagine alle dimensioni";; width; "x"; height If pix <> V4L2_PIX_FMT_RGB24 Then Error.Raise("Libv4l non accetta il formato RGB24.\nImpossibile procedere !") st.Close Free(fmt) With req .count = 1 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE .memory_ = V4L2_MEMORY_MMAP End With Repeat r = v4l2_ioctl_request(fd, VIDIOC_REQBUFS, req) If r = -1 Then Error.Raise("Errore !") Until r > -1 buffers = New Buffer[req.count] For n_buffers As Integer = 0 To req.count - 1 With buf .index = n_buffers .type = V4L2_BUF_TYPE_VIDEO_CAPTURE .memory_ = V4L2_MEMORY_MMAP End With Repeat r = v4l2_ioctl_buffer(fd, VIDIOC_QUERYBUF, buf) If r = -1 Then Error.Raise("Errore !") Until r > -1 With buffers[n_buffers] = New Buffer .length = buf.length .start = v4l2_mmap(Null, buf.length, PROT_READ Or PROT_WRITE, MAP_SHARED, fd, buf.m) End With Next For i As Integer = 0 To n_buffers - 1 With buf .index = i .type = V4L2_BUF_TYPE_VIDEO_CAPTURE .memory_ = V4L2_MEMORY_MMAP End With Repeat r = v4l2_ioctl_buffer(fd, VIDIOC_QBUF, buf) If r = -1 Then Error.Raise("Errore !") Until r > -1 Next type = V4L2_BUF_TYPE_VIDEO_CAPTURE Repeat r = v4l2_ioctl_pointer(fd, VIDIOC_STREAMON, VarPtr(type)) If r = -1 Then Error.Raise("Errore !") Until r > -1 Repeat tv.tv_sec = 1 r = select_C(fd + 1, Null, Null, Null, tv) If r = -1 Then Error.Raise("Errore !") Until r > -1 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE buf.memory_ = V4L2_MEMORY_MMAP Repeat r = v4l2_ioctl_buffer(fd, VIDIOC_DQBUF, buf) If r = -1 Then Error.Raise("Errore !") Until r > -1 ppm = "/tmp/V4L" &/ Format(Now, "dd-mm-yyyy_hh:nn:ss") & ".ppm" ' Scrive i dati d'intestazione del file immagine PPM: File.Save(ppm, "P6\x0A" & CStr(width) & Chr(32) & CStr(height) & " 255\x0A") fout = Open ppm For Write Append p = buffers[buf.index].start If p == 0 Then Error.Raise("Errore !") ' Aggiunge i dati immagine al file PPM: Write #fout, p, buf.bytesused ' [nota 1] fout.Close ' Va in chiusura: i = V4L2_BUF_TYPE_VIDEO_CAPTURE Repeat r = v4l2_ioctl_pointer(fd, VIDIOC_STREAMOFF, VarPtr(i)) If r = -1 Then Error.Raise("Errore !") Until r > -1 For i = 0 To n_buffers - 1 v4l2_munmap(buffers[i].start, buffers[i].length) Next v4l2_close(fd) Write "\rImmagine acquisita ! " End
Note
[1] Per questo uso del Puntatore con WRITE vedere: https://www.gambas-it.org/wiki/index.php/Write#Scrivere_in_un_file_i_dati_contenuti_in_un.27area_di_memoria_puntata_da_un_Puntatore