Wprowadzenie do programowania Video4Linux (V4L oraz V4L2)

Urządzenia video, takie jak: karty telewizyjne, kamerki internetowe, karty DVB, tunery radiowe itp. działają w oparciu o jeden wspólny interfejs - Video4Linux. Począwszy od jąder 2.6 mamy do dyspozycji dwie wersje Video4Linux. Przejście z starego standardu na nowy wymaga jednak trochę czasu i dlatego wciąż istnieje możliwość wyboru pomiędzy nimi. Zaleca się jednak używanie Video4Linux2 (V4L2) i odchodzenie od Video4Linux (V4L). W artykule przedstawiam sposób programowania w oparciu o oba interfejsy, by pokazać różnice, wady i zalety nowych rozwiązań.

8.9.2004 14:00 | Paweł Pustelnik | přečteno 29021×

Urządzenia video, takie jak: karty telewizyjne, kamerki internetowe, karty DVB, tunery radiowe itp. działają w oparciu o jeden wspólny interfejs - Video4Linux. Począwszy od jąder 2.6 mamy do dyspozycji dwie wersje Video4Linux. Przejście z starego standardu na nowy wymaga jednak trochę czasu i dlatego wciąż istnieje możliwość wyboru pomiędzy nimi. Zaleca się jednak używanie Video4Linux2 (V4L2) i odchodzenie od Video4Linux (V4L). W artykule przedstawiam sposób programowania w oparciu o oba interfejsy, by pokazać różnice, wady i zalety nowych rozwiązań. Często nie tłumaczę tekstu, gdyż mogłoby to niepotrzebnie utrudnić zrozumienie, a czasem po prostu niełatwo rozsądnie przetłumaczyć pewne techniczne czynności na język polski.

W systemie urządzenia video/radio mają liczbę major = 81, natomiast minory to wartości od 0 do 255 i mogą wyglądać jak poniżej:

Nazwa Wartości minorów Typ
/dev/video 0-63 Video Capture Interface
/dev/radio 64-127 AM/FM Radio Devices
/dev/vtx 192-223 Teletext Interface Chips
/dev/vbi 224-255 Raw VBI Data (Intercast/teletext)

Podane rozwiązanie jest pewnym przyjętym standardem, by nie powodować niepotrzebnego zamieszania, niemniej jednak, jest to sprawa administratora systemu.
Uwaga: podane wartości dotyczą urządzeń Video4Linux i nie są tożsame z Linux DVB, które mają major = 212.

Jeżeli w naszym Linuksie nie mamy utworzonych odpowiednich plików urządzeń, to możemy to zrobić ręcznie. Przykładowo, by utworzyć urządzenie video:

mknod /dev/video0 c 81 0
chmod 666 /dev/video0
ln -s /dev/video0 /dev/video

Teraz Video4Linux widzi pod /dev/video nasz pierwszy odbiornik video (np. z karty TV bądź kamerki).

Analogicznie możemy utworzyć urządzenia dla radia:

mknod /dev/radio0 c 81 64
chmod 666 /dev/radio0
ln -s /dev/radio0 /dev/radio

Drugi sposób, to wykorzystanie gotowego skryptu. Możemy go znaleźć w źródłach jądra w katalogu: /Documentation/video4linux/bttv. Plik nazywa się MAKEDEV. Być może trzeba będzie go uczynić wykonywalnym.

1. Otwieranie/Zamykanie urządzenia

V4L2 w przeciwieństwie do V4L udostępnia wielokrotne otwieranie tego samego urządzenia w tym samym czasie. Pozwala to na równoległe wykonywanie różnych czynności, np. jeden program działa jako prosty aplet do zmiany jasności obrazu, a w tym samym czasie inny program nagrywa obraz z kamery. Należy jednak pamiętać, iż to od sterownika zależy, czy i w jakim stopniu możemy wykorzystać te możliwości. Co więcej, sterowniki nie powinny pozwalać na jednoczesne czytanie/zapisywanie danych na urządzeniu poprzez kopiowanie buforów itp.

Otwieranie, zamykanie, wywoływanie odpowiednich funkcji odbywa się tak samo dla obu interfejsów. Otwieramy urządzenie przy użyciu funkcji open(), zamykamy przez close(), a do urządzenia odwołujemy się poprzez ioctl().

Aby móc korzystać z Video4Linux, należy załączać plik nagłówkowy <linux/videodev.h>.

Poniżej prezentuję przykład otwarcia i zamknięcia urządzenia video.

Przykład 1. Otwieranie/Zamykanie urządzenia video:

const char * device = "/dev/video" ; //domyslnie /dev/video
int fd = open (device, O_RDONLY); //otwieramy tylko do odczytu

//sprawdzamy czy proba otwarcia sie powiodla
if (fd < 0 )
{
    perror ("open"); //pokaz blad
    return - 1 ;
}

/* fd - nasz deskryptor pliku urzadzenia, w kolejnych przykladach bedziemy sie do niego odwolywac */

2. Możliwości urządzenia

Pierwszą czynnością jaką należy wykonać po otwarciu urządzenia jest sprawdzenie jego możliwości. Wiele urządzeń tego samego typu różni się bowiem między sobą, jedne karty TV mają tuner radiowy, drugie tuner radiowy i telewizyjny itp.
Test ten pozwoli nam także wykryć, którego interfejsu używa nasze urządzenie - który interfejs musimy stosować, by móc odwoływać się do tego urządzenia.

W V4L należy użyć wywołania VIDIOCGCAP (get capabilities - pobierz informacje o możliwościach naszego urządzenia) . Następnie sprawdzamy wartości poszczególnych danych w strukturze video_capability.

Struktura video_capability:

name[32] nazwa kanoniczna interfejsu
type typ interfejsu
channels liczba kanałów (nie kanałów telewizyjnych,ale wejść) radio/tv
audios liczba urządzeń audio
maxwidth maksymalna przechwytywana szerokość obrazu w pikselach
maxheight maksymalna przechwytywana wysokość obrazu w pikselach
minwidth minimalna przechwytywana szerokość obrazu w pikselach
minheight minimalna przechwytywana wysokość obrazu w pikselach

Pole type zawiera informacje o flagach:

VID_TYPE_CAPTURE może przechwytywać
VID_TYPE_TUNER posiada tuner
VID_TYPE_TELETEXT posiada teletext
VID_TYPE_OVERLAY Can overlay its image onto the frame buffer
VID_TYPE_CHROMAKEY Overlay is Chromakeyed
VID_TYPE_CLIPPING Overlay clipping is supported
VID_TYPE_FRAMERAM Overlay overwrites frame buffer memory
VID_TYPE_SCALES obraz jest skalowalny
VID_TYPE_MONOCHROME tyko w skali szarości
VID_TYPE_SUBCAPTURE przechwycony obraz może być tylko częścią całego obrazu

Poniżej podaję przykład zapytania o możliwości urządzenia i wyświetlenie wyników.

Przykład 2. V4L - Zapytanie o możliwości urządzenia:

struct video_capability vidcap;

//spr czy mozemy otrzymac informacje o urzadzeniu
if ( ioctl (fd,VIDIOCGCAP, &vidcap) == - 1 )
{
    perror ( "VIDIOCGCAP" );
    close (fd); //zamknij plik urzadzenia
    return - 1 ;
}

//pokaz informacje o urzadzeniu
printf ( "Using v4l: \n " );
printf ( " \n ***VIDEO PROPETERIES*** \n " );
printf ("Name: %s\n", vidcap.name);
printf("Type: %d\n", vidcap.type);
printf("Minimum Width: %d\n", vidcap.minwidth);
printf("Maximum Width: %d\n", vidcap.maxwidth);
printf("Minimum Height: %d\n", vidcap.minheight);
printf("Maximum Height: %d\n", vidcap.maxheight);
printf("X: %d\n", vidwin.x);
printf("Y: %d\n", vidwin.y);
printf("Width: %d\n", vidwin.width);
printf("Height: %d\n", vidwin.height);
printf("Depth: %d\n", vidpic.depth);

if(vidcap.
type & VID_TYPE_MONOCHROME)
    printf("Color false\n");
else
    printf("Color true\n");

W V4L2 musimy użyć wywołania VIDIOC_QUERYCAP. Następnie sprawdzamy strukturę v4l2_capability.

Struktura v4l2_capability:

__u8 driver[16] nazwa sterownika
__u8 card[32]  nazwa urządzenia
__u8 bus_info[32]  lokalizacja urządzenia
__u32 version  numer wersji sterownika
__u32 capabilities flagi urządzenia, patrz niżej
__u32 reserved[4] pole zarezerwowane na przyszłe rozwiązania

Pole capabilities:
V4L2_CAP_VIDEO_CAPTURE 0x00000001 The device supports the video capture interface.
V4L2_CAP_VIDEO_OUTPUT 0x00000002 The device supports the video output interface.
V4L2_CAP_VIDEO_OVERLAY 0x00000004 The device supports the video overlay interface.
V4L2_CAP_VBI_CAPTURE 0x00000010 The device supports the VBI capture interface
V4L2_CAP_VBI_OUTPUT 0x00000020 The device supports the VBI output interface
V4L2_CAP_RDS_CAPTURE 0x00000100  
V4L2_CAP_TUNER 0x00010000  
V4L2_CAP_AUDIO 0x00020000  
V4L2_CAP_READWRITE 0x01000000 The device supports the read() and/or write() I/O methods.
V4L2_CAP_ASYNCIO 0x02000000 The device supports the asynchronous I/O methods.
V4L2_CAP_STREAMING 0x04000000 The device supports the streaming I/O method.

Poniżej podaję przykład zapytania o możliwości urządzenia wyświetlenie wyników:

Przykład 3. V4L2 - Zapytanie o możliwości urządzenia:

struct v4l2_capability cap;
if ( ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{
    perror("VIDIOC_QUERYCAP");
    fprintf(stderr, "%s doesn't appear to be a v4l2 device\n",device);
    return -
1;
}
//pokaz mozliwosci urzadzenia
printf("\n***V4L2 DEVICE PROPETERIES***\n");
printf("Driver: %s\n",cap.driver);
printf("Card: %s\n",cap.card);
printf("BusInfo: %s\n",cap.bus_info);
printf("V4L2_CAP_VIDEO_CAPTURE: %i\n",cap.capabilities & V4L2_CAP_VIDEO_CAPTURE);
printf("V4L2_CAP_VIDEO_OUTPUT: %i\n",cap.capabilities & V4L2_CAP_VIDEO_OUTPUT);
printf("V4L2_CAP_VIDEO_OVERLAY: %i\n",cap.capabilities & V4L2_CAP_VIDEO_OVERLAY);
printf("V4L2_CAP_VBI_CAPTURE: %i\n",cap.capabilities & V4L2_CAP_VBI_CAPTURE);
printf("V4L2_CAP_VBI_OUTPUT: %i\n",cap.capabilities & V4L2_CAP_VBI_OUTPUT);
printf("V4L2_CAP_RDS_CAPTURE: %i\n",cap.capabilities & V4L2_CAP_RDS_CAPTURE);
printf("V4L2_CAP_TUNER: %i\n",cap.capabilities & V4L2_CAP_TUNER);
printf("V4L2_CAP_AUDIO: %i\n",cap.capabilities & V4L2_CAP_AUDIO);
printf("V4L2_CAP_READWRITE: %i\n",cap.capabilities & V4L2_CAP_READWRITE);
printf("V4L2_CAP_ASYNCIO: %i\n",cap.capabilities & V4L2_CAP_ASYNCIO);
printf("V4L2_CAP_STREAMING: %i\n",cap.capabilities & V4L2_CAP_STREAMING);

3. Zmiana wejścia urządzenia

V4L udostępnia wywołania VIDIOCGCHAN (get channel) oraz VIDIOCSCHAN (set channel), używając struktury video_channel, by otrzymać dostęp do poszczególnych wejść/wyjść urządzenia. Przykładowo możemy odbierać obraz z tunera telewizyjnego, z wejścia S-VIDEO lub też COMPOSITE.

Struktura video_channel składa się z:

channel The channel number
name The input name - preferably reflecting the label on the card input itself
tuners Number of tuners for this input
flags Properties the tuner has
type Input type (if known)
norm The norm for this channel

Mamy następujące flagi (flags):

VIDEO_VC_TUNER Channel has tuners
VIDEO_VC_AUDIO Channel has audio
VIDEO_VC_NORM Channel has norm setting

Mamy następujące typy (type):

VIDEO_TYPE_TV The input is a TV input
VIDEO_TYPE_CAMERA The input is a camera

Poniżej podaję przykłady sprawdzenia informacji o aktualnie używanym wejściu oraz zmiany wejścia.

Przykład 4. V4L - Informacje o wejściu urządzenia:

struct video_channel vidchan;
if (-
1 == ioctl(fd,VIDIOCGCHAN,&vidchan))
{

    perror("VIDIOCGCHAN");
    return -
1;
}
else
    printf("***PROPETERIES***\n\n");
    printf("Number: %i\n",vidchan.channel);
    printf("Name: %s\n",vidchan.name);
    printf("Tuner Number: %i\n",vidchan.tuners);

Przykład 5. V4L2 - Zmiana wejścia urządzenia:

struct video_channel vidchan;
int inputNumber = 1;
vidchan.
channel = inputNumber;
if (
ioctl(fd, VIDIOCSCHAN, &vidchan) < 0)
{

    perror("VIDIOCSCHANNEL");
    return -
1;
}

    printf("Input changed\n");

W przypadku V4L2 sprawa wygląda nieco inaczej. Najpierw poprzez wywołanie VIDIOC_ENUMINPUT (jeśli interesują nas wejścia urządzenia) lub VIDIOC_ENUMOUTPUT (jeśli interesują nas wyjścia urządzenia) zapełniamy informacjami strukturę v4l2_input. Struktura ta zawiera dane o konkretnym wejściu/wyjściu. Jeśli chcemy jednak uzyskać numer wejścia/wyjścia bądź też je zmienić, to należy zastosować wywołania systemowe takie jak: VIDIOC_G_INPUT (get input), VIDIOC_S_INPUT (set input), VIDIOC_G_OUTPUT (get output), VIDIOC_S_OUTPUT (set output).

Struktura v4l2_input:

__u32 index Identyfikator wejścia
__u8 name[32] Nazwa wejścia video (np. Composite 1)
__u32 type Typ wejścia (TUNER, CAMERA)
__u32 audioset Wejście audio
__u32 tuner Identyfikator tunera,jeśli urządzenia posiada takowy
v4l2_std_id std Struktura standardu video (nie omawiana w tym artykule)
__u32 status Informacje o aktualnym stanie wejścia
__u32 reserved[4] Zarezerwowane na przyszłe funkcje

__u32 type:

V4L2_INPUT_TYPE_TUNER 1 Wejście używane przez tuner
V4L2_INPUT_TYPE_CAMERA 2 Wejście analogowe, np. CVBS / Composite Video, S-Video, RGB

__u32 status:

Podstawowe:
V4L2_IN_ST_NO_POWER 0x00000001 Attached device is off
V4L2_IN_ST_NO_SIGNAL 0x00000002  
V4L2_IN_ST_NO_COLOR 0x00000004  
Analogowe video:
V4L2_IN_ST_NO_H_LOCK 0x00000100 No horizontal sync lock
V4L2_IN_ST_COLOR_KILL 0x00000200 The color killer is enabled
V4L2_IN_ST_NO_COLOR 0x00000004 No color is detected
Cyfrowe video:
V4L2_IN_ST_NO_SYNC 0x00010000 No synchronization lock
V4L2_IN_ST_NO_EQU 0x00020000 No equalizer lock
V4L2_IN_ST_NO_CARRIER 0x00040000 Carrier recovery failed
VCR and Set-Top Box :
V4L2_IN_ST_MACROVISION 0x01000000 Macrovision protection has been detected
V4L2_IN_ST_NO_ACCESS 0x02000000 Conditional access denied
V4L2_IN_ST_VTR 0x04000000 VTR time constant

Poniżej prezentuję, podobnie jak dla V4L, przykłady sprawdzenia numeru wejścia + informacje o wejściu oraz zmiany wejścia na pierwsze w urządzeniu.

Przykład 6. V4L2 - Informacje o wejściu urządzenia:

struct v4l2_input input;
int index;
int inputNumber = 1;
//pobierz numer wejscia
if (-1 == ioctl (fd, VIDIOC_G_INPUT, &index))
{
    perror ("VIDIOC_G_INPUT");
    return -
1;
}

//wypelnie dane dla podanego numeru wejscia
input.index = index;
if (-
1 == ioctl (fd, VIDIOC_ENUMINPUT, &input))
{

    perror ("VIDIOC_ENUMINPUT");
    return -
1;
}

printf ("Input (%i): %s\n", id,input.name);

Przykład 7. V4L2 - Zmiana wejścia urządzenia:

struct v4l2_input input;
int index;
int inputNumber = 1;
//ustaw sie na obecne wejscie
if (-1 == ioctl (fd, VIDIOC_G_INPUT, &index))
{

    perror ("VIDIOC_G_INPUT");
    return -
1;
}
input.
index = index;
//pobierz informacje o aktualnym wejsciu
if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input))
{

    perror ("VIDIOC_ENUMINPUT");
    return -
1;
}

printf ("Previous input: %s\n", input.name);
//ustaw nowe wejscie
index = inputNumber;
if (-
1 == ioctl (fd, VIDIOC_S_INPUT, &index))
{

    perror ("VIDIOC_S_INPUT");
    return -
1;
}

printf("Input changed\n");

4. Zmiana parametrów obrazu

Przechwytywany obraz może nie być takiej jakości, jakiej oczekujemy. Często trzeba poprawić kontrast, balans kolorów itp. W V4L zmian tych można dokonać poprzez wywołanie VIDIOCGPICT (pobranie informacji o aktualnych ustawieniach) oraz VIDIOCSPICT (przekazanie nowych ustawień do urządzenia). W obu przypadkach dane znajdują się w strukturze video_picture.

Struktura video_picture:

brightness Picture brightness
hue Picture hue (colour only)
colour Picture colour (colour only)
contrast Picture contrast
whiteness The whiteness (greyscale only)
depth The capture depth (may need to match the frame buffer depth)
palette Reports the palette that should be used for this image

Następujące wartości dla palette są zdefiniowane:

VIDEO_PALETTE_GREY Skala szarości (0-255)
VIDEO_PALETTE_HI240
VIDEO_PALETTE_RGB565
VIDEO_PALETTE_RGB555
VIDEO_PALETTE_RGB24
VIDEO_PALETTE_RGB32
VIDEO_PALETTE_YUV422
VIDEO_PALETTE_YUYV
VIDEO_PALETTE_UYVY
VIDEO_PALETTE_YUV420
VIDEO_PALETTE_YUV411
VIDEO_PALETTE_RAW
VIDEO_PALETTE_YUV422P
VIDEO_PALETTE_YUV411P

Zmiana wartości pola w palette oznacza zmianę formatu otrzymywanych danych. Możemy zatem otrzymywać obraz tylko w skali szarości, mimo iż nasze urządzenie obsługuje kolory i może nam podawać dane w formacie RGB24 lub RGB32.

Przykład 8. V4L - Uzyskanie informacji o aktualnej wartości kontrastu:

struct video_picture vidpic;
if (
ioctl (fd, VIDIOCGPICT, &vidpic) < 0)
{

    perror("VIODCGPICT");
    return -
1;//fail
}
printf("Contrast [0-100] = %i\n",(int)(100.0*vidpic.contrast/65535));

Przykład 9. V4L2 - Zmiana aktualnej wartości jasności i palety:

struct video_picture vidpic;
int value = 50;//na 50% ustawiamy
vidpic.whiteness = int(65535*1.0*value/100);
if (
ioctl(fd, VIDIOCSPICT, &vidpic) < 0)
{

    perror("VIODCSPICT");
    return -
1;
}

V4L2 używa specjalnych, zdefiniowanych identyfikatorów (ID). Niektóre sterowniki mogą nawet zawierać swoje własne, typowe tylko dla danego urządzenia właściwości, do których możemy się odwoływać przez V4L2_CID_PRIVATE_BASE. Każdy identyfikator jest poprzedzony przedrostkiem V4L2_CID_ .

Poniżej lista wszystkich identyfikatorów:

ID Typ Opis
V4L2_CID_BASE Pierwszy zdefiniowany ID = V4L2_CID_BRIGHTNESS
V4L2_CID_BRIGHTNESS
integer
V4L2_CID_CONTRAST integer
V4L2_CID_SATURATION integer
V4L2_CID_HUE
integer
V4L2_CID_AUDIO_VOLUME integer
V4L2_CID_AUDIO_BALANCE
integer
V4L2_CID_AUDIO_BASS integer
V4L2_CID_AUDIO_TREBLE integer
V4L2_CID_AUDIO_MUTE
boolean
V4L2_CID_AUDIO_LOUDNESS
boolean
V4L2_CID_BLACK_LEVEL
integer
V4L2_CID_AUTO_WHITE_BALANCE
boolean
V4L2_CID_DO_WHITE_BALANCE
button
V4L2_CID_RED_BALANCE
integer
V4L2_CID_BLUE_BALANCE integer
V4L2_CID_GAMMA integer
V4L2_CID_WHITENESS integer
V4L2_CID_EXPOSURE integer
V4L2_CID_AUTOGAIN
boolean
V4L2_CID_GAIN integer
V4L2_CID_HFLIP
boolean
V4L2_CID_VFLIP
boolean
V4L2_CID_HCENTER integer
V4L2_CID_VCENTER integer
V4L2_CID_LASTP1
Ostatni ID = 1 + V4L2_CID_VCENTER
V4L2_CID_PRIVATE_BASE
Zdefiniowany przez sterownik

Sprawdzenie, czy dany identyfikator jest dostępny w naszym urządzeniu oraz jakie może przyjmować wartości, wymaga użycia kilku wywołań. VIDIOC_QUERYCTRL oraz IDIOC_QUERYMENU pozwalają na sprawdzenie poszczególnych właściwości. Zależność między nimi jest następująca: poprzez wywołanie VIDIOC_QUERYCTRL wypełniamy strukturę v4l2_queryctrl. Następnie sprawdzamy, czy wartość pola v4l2_ctrl_type jest równa V4L2_CTRL_TYPE_MENU. Jeżeli tak, to możemy się odwoływać do wartości pól tej struktury.

Operacje zmiany bądź odczytania aktualnej wartości dokonujemy wywołaniami VIDIOC_S_CTRL (set control) oraz VIDIOC_G_CTRL (get control), które zapisują/odczytują dane z struktury v4l2_format.

Struktura v4l2_format:

enum v4l2_buf_type type Typ strumienia danych
union  fmt :
struct v4l2_pix_format  pix format danych - patrz niżej
struct v4l2_window  win Definition of an overlaid image
struct v4l2_vbi_format  vbi Raw VBI capture or output parameters
    __u8  raw_data[200] Przyszłe funkcje

Przykład 10. V4L2 - Uzyskanie informacji o wszystkich dostępnych identyfikatorach:

/* PRZYKLAD Z V4L2 API */
struct v4l2_queryctrl queryctrl;
struct v4l2_querymenu querymenu;

//podaje informacje o polu struktury v4l2_queryctrl, ktore jest typem Menu
static void enumerate_menu (void)
{

    printf (" Menu items:\n");
    querymenu.
id = queryctrl.id;
    for (querymenu.
index = queryctrl.minimum;
       querymenu.
index <= queryctrl.maximum;
        querymenu.
index++) {
        if (
0 == ioctl (fd, VIDIOC_QUERYMENU, &querymenu)) {
            printf (" %s\n", querymenu.name);
        } else {

            perror ("VIDIOC_QUERYMENU");
            exit (EXIT_FAILURE);
        }
    }
}


//sprawdz pola standardowe
for (queryctrl.index = V4L2_CID_BASE;
    queryctrl.
index < V4L2_CID_LASTP1;
    queryctrl.
index++) {
    if (
0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
        if (queryctrl.
flags & V4L2_CTRL_FLAG_DISABLED)
            continue;

        printf ("Control %s\n", queryctrl.name);

            if (queryctrl.
type == V4L2_CTRL_TYPE_MENU)//typ menu
                enumerate_menu ();
    } else {
            if (errno == EINVAL)
                continue;

                perror ("VIDIOC_QUERYCTRL");
                exit (EXIT_FAILURE);
    }
}


//sprawdz pola udostepniane przez sterownik
for (queryctrl.index = V4L2_CID_PRIVATE_BASE;;
queryctrl.
index++) {
    if (
0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
        if (queryctrl.
flags & V4L2_CTRL_FLAG_DISABLED)
            continue;

        printf ("Control %s\n", queryctrl.name);

        if (queryctrl.
type == V4L2_CTRL_TYPE_MENU)
            enumerate_menu ();
        } else {
            if (errno == EINVAL)
                break;

            perror ("VIDIOC_QUERYCTRL");
            exit (EXIT_FAILURE);
        }
}

Przykład 11. V4L2 - Uzyskanie informacji o aktualnej wartości kontrastu:

struct v4l2_control control;
control.
id = V4L2_CID_CONTRAST;
if (
ioctl (fd, VIDIOC_G_CTRL, &control) < 0)
{
    perror("VIDIOC_G_CTRL");
    return -
1;
}
return
int(100.0*control.value/65535);//success

Przykład 12. V4L2 - Zmiana aktualnej wartości jasności:

struct v4l2_control control;
control.
id = V4L2_CID_WHITENESS;
control.
value = int(65535 * 1.0*value/100);
if (
ioctl (fd, VIDIOC_S_CTRL, &control) < 0)
{
    perror("VIDIOC_S_CTRL");
    return -
1;//fail
}
return
0;//success

W V4L mieliśmy w strukturze video_picture pole palette, które określało rodzaj palety kolorów - sposób w jaki otrzymujemy przechwytywane dane, np. jako ciąg bajtów RBGRBGRGB... (RGB24). Jednak wspomniane powyżej metody dla V4L2 nie udostępniają nam możliwości zmian palety. V4L2 udostępnia nam strukturę v4l2_pix_format oraz wywołania VIDIOC_S_FMT (set format) czy VIDIOC_G_FMT (get format). Jednak nie odwołujemy się bezpośrednio do v4l2_pix_format, ale poprzez v4l2_format - tabela powyżej.

Struktura v4l2_pix_format:

__u32 width szerokość w pikselach
__u32 height wysokość w pikselach
__u32 pixelformat format danych (paleta) - patrz niżej
enum v4l2_field field patrz niżej
__u32 bytesperline liczba bajtów w linii
__u32 sizeimage rozmiar obrazu
enum v4l2_colorspace colorspace patrz niżej
__u32 priv zastrzeżone na przyszłe funkcje

__u32 pixelformat - to właśnie zmiana tego pola pozwoli (jak dla V4L) zmienić paletę. Jednak w V4L2 mamy nieco inne nazwy niektórych pól, dokonano podziału na formaty RGB oraz YUV. Poniżej przedstawiam formaty używane przez V4l2:

RGB formats:

V4L2_PIX_FMT_RGB332
V4L2_PIX_FMT_RGB555
V4L2_PIX_FMT_RGB565b1
V4L2_PIX_FMT_RGB555X
V4L2_PIX_FMT_RGB565X
V4L2_PIX_FMT_BGR24
V4L2_PIX_FMT_RGB24
V4L2_PIX_FMT_BGR32
V4L2_PIX_FMT_RGB3

YUV formats:

V4L2_PIX_FMT_GREY
V4L2_PIX_FMT_YUYV
V4L2_PIX_FMT_UYVY
V4L2_PIX_FMT_Y41P
V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420
V4L2_PIX_FMT_YVU410, V4L2_PIX_FMT_YUV410
V4L2_PIX_FMT_YUV422P
V4L2_PIX_FMT_YUV411P
V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV21

enum v4l2_field field - obrazy video są bardzo często pełne interlace'u. Tutaj możemy określić sposób otrzymywania obu pól składających się na ramkę. Problem interlace'u nie jest celem tego artykułu. Na końcu wśród ciekawych linków można znaleźć informacje na ten temat. enum v4l2_colorspace colorspace - pole ustawiane przez sterownik. Więcej w API V4L2.

Wśród pól struktury v4l2_pix_format widzimy width oraz height. Pozwalają one na zmianę szerokości i wysokości obrazu.

Poniżej podaję przykłady dla wspomnianych wcześniej metod.

Przykład 13. V4L2 - Zmiana wysokości i szerokości obrazu:

struct v4l2_pix_format videoformat;
videoformat.
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoformat.
fmt.pix.height = 50;//wysokosc
videoformat.fmt.pix.width = 50;//szerokosc
if (-1 == ioctl(fd,VIDIOC_S_FMT,&videoformat))
{
    perror("VIDIOC_S_FMT");
    return -
1;
}
return
0;//success

Przykład 14. V4L2 - Uzyskanie informacji o aktualnej wysokości,szerokości oraz palecie obrazu:

struct v4l2_format videoformat;
struct v4l2_fmtdesc fmtdesc;
videoformat.
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-
1 == ioctl(fd,VIDIOC_G_FMT,&videoformat))
{
    perror("VIDIOC_G_FMT");
    return -
1;
}
//pobierz numer aktualnego formatu
fmtdesc.index = 0;
while (
0 == ioctl (fd, VIDIOC_ENUM_FMT, &fmtdesc))
{
    fmtdesc.
index++;
}
printf("Actual height: %i\n",videoformat.fmt.pix.height);
printf("Actual width: %i\n",videoformat.fmt.pix.width);
printf ("Actual Format: (%i): %s\n", id,fmtdesc.description);

Przykład 15. V4L2 - Zmiana palety na RGB24:

struct v4l2_format videoformat;
videoformat.
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
videoformat.
fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
if (-
1 == ioctl(fd,VIDIOC_S_FMT,&videoformat))
{
    
perror("VIDIOC_S_FMT");
    return -
1;
}
return
0;

Zmianę wysokości, szerokości w V4L dokonujemy wywołaniem VIDIOCGWIN oraz VIDIOCSWIN, które wypełniają pola width oraz height w strukturze video_window.

Struktura video_windowskłada się z następujących pól:

x
y
width
height
chromakey
flags
clips
clipcount

Przykład 16. V4L - Zmiana wysokości i szerokości obrazu:

struct video_window vidwin;
vidwin.
height = 50;//wysokosc = 50
vidwin.width = 50;//szerokosc = 50
if ( ioctl(fd, VIDIOCSWIN, &vidwin) < 0)
{
    perror("VIDIOCSWIN");
    return -
1;
}
return
0;

Przykład 17. V4L - Odczyt wysokości i szerokości obrazu:

struct video_window vidwin;
if (
ioctl(fd, VIDIOGSWIN, &vidwin) < 0)
{
    perror("VIDIOCGWIN");
    return -
1;
}
printf("Actual width: %i\n",vidwin.width);
printf("Actual height: %i\n",vidwin.height);

5. Podsumowanie

Celem niniejszego artykułu było przedstawienie podstaw V4L oraz V4L2. Oba interfejsy mają znacznie większe możliwości aniżeli pokazane przez autora. Być może - jeśli będzie taka potrzeba - powstanie seria artykułów, które będą prezentować pozostałe funkcje obu interfejsów. Polecam przeanalizowanie pliku videodev.h.

Użyteczne linki:

V4L API,
V4L2 API,
Interlace

Paweł Pustelnik, 31-08-2004. [pawelpus@wp.pl]

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=402