ARCHIV |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Software (10844)
Distribuce (131)
Skripty (697)
Menu
Diskuze
Informace
|
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ń. 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:
Podane rozwiązanie jest pewnym przyjętym standardem, by nie powodować niepotrzebnego zamieszania, niemniej jednak, jest to sprawa administratora systemu.
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: 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: 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ądzeniaV4L2 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. 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:
Pole type zawiera informacje o flagach:
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:
Pole capabilities:
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ądzeniaV4L 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:
Mamy następujące flagi (flags):
Mamy następujące typy (type):
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 type:
__u32 status:
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 obrazuPrzechwytywany 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:
Następujące wartości dla palette są zdefiniowane:
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:
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:
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 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:
YUV formats:
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:
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. PodsumowanieCelem 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: Paweł Pustelnik, 31-08-2004. [pawelpus@wp.pl]
|
Vyhledávání software
Vyhledávání článků
28.11.2018 23:56 /František Kučera 12.11.2018 21:28 /Redakce Linuxsoft.cz 6.11.2018 2:04 /František Kučera 4.10.2018 21:30 /Ondřej Čečák 18.9.2018 23:30 /František Kučera 9.9.2018 14:15 /Redakce Linuxsoft.cz 12.8.2018 16:58 /František Kučera 16.7.2018 1:05 /František Kučera
Poslední diskuze
31.7.2023 14:13 /
Linda Graham 30.11.2022 9:32 /
Kyle McDermott 13.12.2018 10:57 /
Jan Mareš 2.12.2018 23:56 /
František Kučera 5.10.2018 17:12 /
Jakub Kuljovsky | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze |