📄 videobook.tmpl
字号:
</para> <para> Our depth is 24, as this is in bits. We will be returning RGB24 format. This has one byte of red, then one of green, then one of blue. This then repeats for every other pixel in the image. The other common formats the interface defines are </para> <table frame=all><title>Framebuffer Encodings</title> <tgroup cols=2 align=left> <tbody> <row> <entry>GREY</><entry>Linear greyscale. This is for simple cameras and the like</> </row><row> <entry>RGB565</><entry>The top 5 bits hold 32 red levels, the next six bits hold green and the low 5 bits hold blue. </> </row><row> <entry>RGB555</><entry>The top bit is clear. The red green and blue levels each occupy five bits.</> </row> </tbody> </tgroup> </table> <para> Additional modes are support for YUV capture formats. These are common for TV and video conferencing applications. </para> <para> The VIDIOCSPICT ioctl allows a user to set some of the picture parameters. Exactly which ones are supported depends heavily on the card itself. It is possible to support many modes and effects in software. In general doing this in the kernel is a bad idea. Video capture is a performance-sensitive application and the programs can often do better if they aren't being 'helped' by an overkeen driver writer. Thus for our device we will report RGB24 only and refuse to allow a change. </para> <programlisting> case VIDIOCSPICT: { struct video_picture v; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if(v.depth!=24 || v.palette != VIDEO_PALETTE_RGB24) return -EINVAL; set_hardware_brightness(v.brightness); set_hardware_hue(v.hue); set_hardware_saturation(v.colour); set_hardware_brightness(v.contrast); return 0; } </programlisting> <para> We check the user has not tried to change the palette or the depth. We do not want to carry out some of the changes and then return an error. This may confuse the application which will be assuming no change occurred. </para> <para> In much the same way as you need to be able to set the picture controls to get the right capture images, many cards need to know what they are displaying onto when generating overlay output. In some cases getting this wrong even makes a nasty mess or may crash the computer. For that reason the VIDIOCSBUF ioctl used to set up the frame buffer information may well only be usable by root. </para> <para> We will assume our card is one of the old ISA devices with feature connector and only supports a couple of standard video modes. Very common for older cards although the PCI devices are way smarter than this. </para> <programlisting>static struct video_buffer capture_fb; case VIDIOCGFBUF: { if(copy_to_user(arg, &capture_fb, sizeof(capture_fb))) return -EFAULT; return 0; } </programlisting> <para> We keep the frame buffer information in the format the ioctl uses. This makes it nice and easy to work with in the ioctl calls. </para> <programlisting> case VIDIOCSFBUF: { struct video_buffer v; if(!capable(CAP_SYS_ADMIN)) return -EPERM; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if(v.width!=320 && v.width!=640) return -EINVAL; if(v.height!=200 && v.height!=240 && v.height!=400 && v.height !=480) return -EINVAL; memcpy(&capture_fb, &v, sizeof(v)); hardware_set_fb(&v); return 0; } </programlisting> <para> The capable() function checks a user has the required capability. The Linux operating system has a set of about 30 capabilities indicating privileged access to services. The default set up gives the superuser (uid 0) all of them and nobody else has any. </para> <para> We check that the user has the SYS_ADMIN capability, that is they are allowed to operate as the machine administrator. We don't want anyone but the administrator making a mess of the display. </para> <para> Next we check for standard PC video modes (320 or 640 wide with either EGA or VGA depths). If the mode is not a standard video mode we reject it as not supported by our card. If the mode is acceptable we save it so that VIDIOCFBUF will give the right answer next time it is called. The hardware_set_fb() function is some undescribed card specific function to program the card for the desired mode. </para> <para> Before the driver can display an overlay window it needs to know where the window should be placed, and also how large it should be. If the card supports clipping it needs to know which rectangles to omit from the display. The video_window structure is used to describe the way the image should be displayed. </para> <table frame=all><title>struct video_window fields</title> <tgroup cols=2 align=left> <tbody> <row> <entry>width</><entry>The width in pixels of the desired image. The card may use a smaller size if this size is not available</> </row><row> <entry>height</><entry>The height of the image. The card may use a smaller size if this size is not available.</> </row><row> <entry>x</><entry> The X position of the top left of the window. This is in pixels relative to the left hand edge of the picture. Not all cards can display images aligned on any pixel boundary. If the position is unsuitable the card adjusts the image right and reduces the width.</> </row><row> <entry>y</><entry> The Y position of the top left of the window. This is counted in pixels relative to the top edge of the picture. As with the width if the card cannot display starting on this line it will adjust the values.</> </row><row> <entry>chromakey</><entry>The colour (expressed in RGB32 format) for the chromakey colour if chroma keying is being used. </> </row><row> <entry>clips</><entry>An array of rectangles that must not be drawn over.</> </row><row> <entry>clipcount</><entry>The number of clips in this array.</> </row> </tbody> </tgroup> </table> <para> Each clip is a struct video_clip which has the following fields </para> <table frame=all><title>video_clip fields</title> <tgroup cols=2 align=left> <tbody> <row> <entry>x, y</><entry>Co-ordinates relative to the display</> </row><row> <entry>width, height</><entry>Width and height in pixels</> </row><row> <entry>next</><entry>A spare field for the application to use</> </row> </tbody> </tgroup> </table> <para> The driver is required to ensure it always draws in the area requested or a smaller area, and that it never draws in any of the areas that are clipped. This may well mean it has to leave alone. small areas the application wished to be drawn. </para> <para> Our example card uses chromakey so does not have to address most of the clipping. We will add a video_window structure to our global variables to remember our parameters, as we did with the frame buffer. </para> <programlisting> case VIDIOCGWIN: { if(copy_to_user(arg, &capture_win, sizeof(capture_win))) return -EFAULT; return 0; } case VIDIOCSWIN: { struct video_window v; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if(v.width > 640 || v.height > 480) return -EINVAL; if(v.width < 16 || v.height < 16) return -EINVAL; hardware_set_key(v.chromakey); hardware_set_window(v); memcpy(&capture_win, &v, sizeof(v)); capture_w = v.width; capture_h = v.height; return 0; } </programlisting> <para> Because we are using Chromakey our setup is fairly simple. Mostly we have to check the values are sane and load them into the capture card. </para> <para> With all the setup done we can now turn on the actual capture/overlay. This is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument where 0 is on and 1 is off. </para> <programlisting> case VIDIOCCAPTURE: { int v; if(get_user(v, (int *)arg)) return -EFAULT; if(v==0) hardware_capture_off(); else { if(capture_fb.width == 0 || capture_w == 0) return -EINVAL; hardware_capture_on(); } return 0; } </programlisting> <para> We grab the flag from user space and either enable or disable according to its value. There is one small corner case we have to consider here. Suppose that the capture was requested before the video window or the frame buffer had been set up. In those cases there will be unconfigured fields in our card data, as well as unconfigured hardware settings. We check for this case and return an error if the frame buffer or the capture window width is zero. </para> <programlisting> default: return -ENOIOCTLCMD; }} </programlisting> <para> We don't need to support any other ioctls, so if we get this far, it is time to tell the video layer that we don't now what the user is talking about. </para> </sect1> <sect1 id="endvid"> <title>Other Functionality</title> <para> The Video4Linux layer supports additional features, including a high performance mmap() based capture mode and capturing part of the image. These features are out of the scope of the book. You should however have enough example code to implement most simple video4linux devices for radio and TV cards. </para> </sect1> </chapter> <chapter id="bugs"> <title>Known Bugs And Assumptions</title> <para> <variablelist> <varlistentry><term>Multiple Opens</term> <listitem> <para> The driver assumes multiple opens should not be allowed. A driver can work around this but not cleanly. </para> </listitem></varlistentry> <varlistentry><term>API Deficiencies</term> <listitem> <para> The existing API poorly reflects compression capable devices. There are plans afoot to merge V4L, V4L2 and some other ideas into a better interface. </para> </listitem></varlistentry> </variablelist> </para> </chapter> <chapter id="pubfunctions"> <title>Public Functions Provided</title>!Edrivers/media/video/videodev.c </chapter></book>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -