Sophie

Sophie

distrib > Mandriva > 9.0 > i586 > by-pkgid > 98e91bc877e03cf3582cd163550eb7e3 > files > 1096

kernel-doc-html-2.4.19-16mdk.i586.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML
><HEAD
><TITLE
>Video Ioctl Handling</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="Video4Linux Programming"
HREF="book1.html"><LINK
REL="UP"
TITLE="Video Capture Devices"
HREF="c261.html"><LINK
REL="PREVIOUS"
TITLE="Reading The Video Image"
HREF="x325.html"><LINK
REL="NEXT"
TITLE="Other Functionality"
HREF="x483.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Video4Linux Programming</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="x325.html"
ACCESSKEY="P"
>&#60;&#60;&#60; Previous</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Video Capture Devices</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="x483.html"
ACCESSKEY="N"
>Next &#62;&#62;&#62;</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="IOCVID"
></A
>Video Ioctl Handling</H1
><P
>        As with the radio driver the major control interface is via the ioctl()
        function. Video capture devices support the same tuner calls as a radio
        device and also support additional calls to control how the video functions
        are handled. In this simple example the card has no tuners to avoid making
        the code complex. 
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;

static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
        switch(cmd)
        {
                case VIDIOCGCAP:
                {
                        struct video_capability v;
                        v.type = VID_TYPE_CAPTURE|\
                                 VID_TYPE_CHROMAKEY|\
                                 VID_TYPE_SCALES|\
                                 VID_TYPE_OVERLAY;
                        v.channels = 1;
                        v.audios = 0;
                        v.maxwidth = 640;
                        v.minwidth = 16;
                        v.maxheight = 480;
                        v.minheight = 16;
                        strcpy(v.name, "My Camera");
                        if(copy_to_user(arg, &#38;v, sizeof(v)))
                                return -EFAULT;
                        return 0;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        The first ioctl we must support and which all video capture and radio
        devices are required to support is VIDIOCGCAP. This behaves exactly the same
        as with a radio device. This time, however, we report the extra capabilities
        we outlined earlier on when defining our video_dev structure.
  </P
><P
>        We now set the video flags saying that we support overlay, capture,
        scaling and chromakey. We also report size limits - our smallest image is
        16x16 pixels, our largest is 640x480. 
  </P
><P
>        To keep things simple we report no audio and no tuning capabilities at all.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>        

                case VIDIOCGCHAN:
                {
                        struct video_channel v;
                        if(copy_from_user(&#38;v, arg, sizeof(v)))
                                return -EFAULT;
                        if(v.channel != 0)
                                return -EINVAL;
                        v.flags = 0;
                        v.tuners = 0;
                        v.type = VIDEO_TYPE_CAMERA;
                        v.norm = VIDEO_MODE_AUTO;
                        strcpy(v.name, "Camera Input");break;
                        if(copy_to_user(&#38;v, arg, sizeof(v)))
                                return -EFAULT;
                        return 0;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        This follows what is very much the standard way an ioctl handler looks
        in Linux. We copy the data into a kernel space variable and we check that the
        request is valid (in this case that the input is 0). Finally we copy the
        camera info back to the user.
  </P
><P
>        The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
        inputs to the video card). Our example card has a single camera input. The
        fields in the structure are
  </P
><DIV
CLASS="TABLE"
><A
NAME="AEN344"
></A
><P
><B
>Table 2. struct video_channel fields</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>channel</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The channel number we are selecting</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>name</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The name for this channel. This is intended
                   to describe the port to the user.
                   Appropriate names are therefore things like
                   "Camera" "SCART input"</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>flags</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Channel properties</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>type</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Input type</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>norm</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The current television encoding being used
                   if relevant for this channel.
    </TD
></TR
></TBODY
></TABLE
></DIV
><DIV
CLASS="TABLE"
><A
NAME="AEN363"
></A
><P
><B
>Table 3. struct video_channel flags</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_VC_TUNER</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Channel has a tuner.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_VC_AUDIO</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Channel has audio.</TD
></TR
></TBODY
></TABLE
></DIV
><DIV
CLASS="TABLE"
><A
NAME="AEN373"
></A
><P
><B
>Table 4. struct video_channel types</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_TYPE_TV</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Television input.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_TYPE_CAMERA</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Fixed camera input.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>0</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Type is unknown.</TD
></TR
></TBODY
></TABLE
></DIV
><DIV
CLASS="TABLE"
><A
NAME="AEN386"
></A
><P
><B
>Table 5. struct video_channel norms</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_MODE_PAL</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>PAL encoded Television</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_MODE_NTSC</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>NTSC (US) encoded Television</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_MODE_SECAM</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>SECAM (French) Television </TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>VIDEO_MODE_AUTO</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Automatic switching, or format does not
                                matter</TD
></TR
></TBODY
></TABLE
></DIV
><P
>        The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
        request the norm is changed - for example to switch between a PAL or an NTSC
        format camera.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
                case VIDIOCSCHAN:
                {
                        struct video_channel v;
                        if(copy_from_user(&#38;v, arg, sizeof(v)))
                                return -EFAULT;
                        if(v.channel != 0)
                                return -EINVAL;
                        if(v.norm != VIDEO_MODE_AUTO)
                                return -EINVAL;
                        return 0;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        The implementation of this call in our driver is remarkably easy. Because we
        are assuming fixed format hardware we need only check that the user has not
        tried to change anything. 
  </P
><P
>        The user also needs to be able to configure and adjust the picture they are
        seeing. This is much like adjusting a television set. A user application
        also needs to know the palette being used so that it knows how to display
        the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
        calls provide this information.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
                case VIDIOCGPICT
                {
                        struct video_picture v;
                        v.brightness = hardware_brightness();
                        v.hue = hardware_hue();
                        v.colour = hardware_saturation();
                        v.contrast = hardware_brightness();
                        /* Not settable */
                        v.whiteness = 32768;
                        v.depth = 24;           /* 24bit */
                        v.palette = VIDEO_PALETTE_RGB24;
                        if(copy_to_user(&#38;v, arg, 
                             sizeof(v)))
                                return -EFAULT;
                        return 0;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        The brightness, hue, color, and contrast provide the picture controls that
        are akin to a conventional television. Whiteness provides additional
        control for greyscale images. All of these values are scaled between 0-65535
        and have 32768 as the mid point setting. The scaling means that applications
        do not have to worry about the capability range of the hardware but can let
        it make a best effort attempt.
  </P
><P
>        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
  </P
><DIV
CLASS="TABLE"
><A
NAME="AEN409"
></A
><P
><B
>Table 6. Framebuffer Encodings</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>GREY</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Linear greyscale. This is for simple cameras and the
                        like</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>RGB565</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The top 5 bits hold 32 red levels, the next six bits 
                        hold green and the low 5 bits hold blue. </TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>RGB555</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The top bit is clear. The red green and blue levels
                        each occupy five bits.</TD
></TR
></TBODY
></TABLE
></DIV
><P
>        Additional modes are support for YUV capture formats. These are common for
        TV and video conferencing applications.
  </P
><P
>        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.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
                case VIDIOCSPICT:
                {
                        struct video_picture v;
                        if(copy_from_user(&#38;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;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        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.
  </P
><P
>        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.
  </P
><P
>        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.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
static struct video_buffer capture_fb;

                case VIDIOCGFBUF:
                {
                        if(copy_to_user(arg, &#38;capture_fb, 
                             sizeof(capture_fb)))
                                return -EFAULT;
                        return 0;
                        
                }


  </PRE
></TD
></TR
></TABLE
><P
>        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.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;                case VIDIOCSFBUF:
                {
                        struct video_buffer v;

                        if(!capable(CAP_SYS_ADMIN))
                                return -EPERM;

                        if(copy_from_user(&#38;v, arg, sizeof(v)))
                                return -EFAULT;
                        if(v.width!=320 &#38;&#38; v.width!=640)
                                return -EINVAL;
                        if(v.height!=200 &#38;&#38; v.height!=240 
                                &#38;&#38; v.height!=400
                                &#38;&#38; v.height !=480)
                                return -EINVAL;
                        memcpy(&#38;capture_fb, &#38;v, sizeof(v));
                        hardware_set_fb(&#38;v);
                        return 0;
                }



  </PRE
></TD
></TR
></TABLE
><P
>        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.
  </P
><P
>        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.
  </P
><P
>        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.
  </P
><P
>        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. 
   </P
><DIV
CLASS="TABLE"
><A
NAME="AEN435"
></A
><P
><B
>Table 7. struct video_window fields</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>width</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The width in pixels of the desired image. The card
                        may use a smaller size if this size is not available</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>height</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The height of the image. The card may use a smaller
                        size if this size is not available.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>x</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>   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.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>y</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>   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.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>chromakey</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The colour (expressed in RGB32 format) for the
                        chromakey colour if chroma keying is being used. </TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>clips</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>An array of rectangles that must not be drawn
			over.</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>clipcount</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>The number of clips in this array.</TD
></TR
></TBODY
></TABLE
></DIV
><P
>        Each clip is a struct video_clip which has the following fields
   </P
><DIV
CLASS="TABLE"
><A
NAME="AEN461"
></A
><P
><B
>Table 8. video_clip fields</B
></P
><TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
CELLSPACING="0"
CELLPADDING="4"
CLASS="CALSTABLE"
><TBODY
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>x, y</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Co-ordinates relative to the display</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>width, height</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>Width and height in pixels</TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
>next</TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
>A spare field for the application to use</TD
></TR
></TBODY
></TABLE
></DIV
><P
>        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.
  </P
><P
>        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.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
                case VIDIOCGWIN:
                {
                        if(copy_to_user(arg, &#38;capture_win, 
                            sizeof(capture_win)))
                                return -EFAULT;
                        return 0;
                }


                case VIDIOCSWIN:
                {
                        struct video_window v;
                        if(copy_from_user(&#38;v, arg, sizeof(v)))
                                return -EFAULT;
                        if(v.width &#62; 640 || v.height &#62; 480)
                                return -EINVAL;
                        if(v.width &#60; 16 || v.height &#60; 16)
                                return -EINVAL;
                        hardware_set_key(v.chromakey);
                        hardware_set_window(v);
                        memcpy(&#38;capture_win, &#38;v, sizeof(v));
                        capture_w = v.width;
                        capture_h = v.height;
                        return 0;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        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.
  </P
><P
>        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.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
                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;
                }


  </PRE
></TD
></TR
></TABLE
><P
>        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.
  </P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><PRE
CLASS="PROGRAMLISTING"
>&#13;
                default:
                        return -ENOIOCTLCMD;
        }
}
  </PRE
></TD
></TR
></TABLE
><P
>&#13;        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.
  </P
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="x325.html"
ACCESSKEY="P"
>&#60;&#60;&#60; Previous</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="book1.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="x483.html"
ACCESSKEY="N"
>Next &#62;&#62;&#62;</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Reading The Video Image</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="c261.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Other Functionality</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>