[media] gspca - sonixj: Better scanning of isochronous packets
authorJean-François Moine <moinejf@free.fr>
Thu, 10 Feb 2011 10:15:24 +0000 (07:15 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 21 Mar 2011 23:32:06 +0000 (20:32 -0300)
A marker 'ff ff 00 c4 c4 96' indicates an end of frame.
It is 62 bytes long and may be splitted on 2 packets.
It contains a flag 'USB full' which indicates that the frame is truncated.

Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/sonixj.c

index c414707..21f0330 100644 (file)
@@ -57,6 +57,8 @@ struct sd {
        atomic_t avg_lum;
        u32 exposure;
 
+       s8 short_mark;
+
        u8 quality;                     /* image quality */
 #define QUALITY_MIN 60
 #define QUALITY_MAX 95
@@ -2787,39 +2789,104 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int sof;
-
-       /* the image ends on a 64 bytes block starting with
-        *      ff d9 ff ff 00 c4 c4 96
-        * and followed by various information including luminosity */
-       /* this block may be splitted between two packets */
-       /* a new image always starts in a new packet */
-       switch (gspca_dev->last_packet_type) {
-       case DISCARD_PACKET:            /* restart image building */
-               sof = len - 64;
-               if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9)
-                       gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
-               return;
-       case LAST_PACKET:               /* put the JPEG 422 header */
+       int i;
+
+       /*
+        * A frame ends on the marker
+        *              ff ff 00 c4 c4 96 ..
+        * which is 62 bytes long and is followed by various information
+        * including statuses and luminosity.
+        *
+        * A marker may be splitted on two packets.
+        *
+        * The 6th byte of a marker contains the bits:
+        *      0x08: USB full
+        *      0xc0: frame sequence
+        * When the bit 'USB full' is set, the frame must be discarded;
+        * this is also the case when the 2 bytes before the marker are
+        * not the JPEG end of frame ('ff d9').
+        */
+
+/*fixme: assumption about the following code:
+ *     - there can be only one marker in a packet
+ */
+
+       /* skip the remaining bytes of a short marker */
+       i = sd->short_mark;
+       if (i != 0) {
+               sd->short_mark = 0;
+               if (i < 0       /* if 'ff' at end of previous packet */
+                && data[0] == 0xff
+                && data[1] == 0x00)
+                       goto marker_found;
+               if (data[0] == 0xff && data[1] == 0xff) {
+                       i = 0;
+                       goto marker_found;
+               }
+               len -= i;
+               if (len <= 0)
+                       return;
+               data += i;
+       }
+
+       /* search backwards if there is a marker in the packet */
+       for (i = len - 1; --i >= 0; ) {
+               if (data[i] != 0xff) {
+                       i--;
+                       continue;
+               }
+               if (data[i + 1] == 0xff) {
+
+                       /* (there may be 'ff ff' inside a marker) */
+                       if (i + 2 >= len || data[i + 2] == 0x00)
+                               goto marker_found;
+               }
+       }
+
+       /* no marker found */
+       /* add the JPEG header if first fragment */
+       if (data[len - 1] == 0xff)
+               sd->short_mark = -1;
+       if (gspca_dev->last_packet_type == LAST_PACKET)
                gspca_frame_add(gspca_dev, FIRST_PACKET,
                                sd->jpeg_hdr, JPEG_HDR_SZ);
-               break;
-       }
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+       return;
+
+       /* marker found */
+       /* if some error, discard the frame */
+marker_found:
+       if (i > 2) {
+               if (data[i - 2] != 0xff || data[i - 1] != 0xd9) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+               }
+       } else if (i + 6 < len) {
+               if (data[i + 6] & 0x08) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+               }
+       }
 
-       data = gspca_dev->image;
-       if (data == NULL)
-               return;
-       sof = gspca_dev->image_len - 64;
-       if (data[sof] != 0xff
-        || data[sof + 1] != 0xd9)
+       gspca_frame_add(gspca_dev, LAST_PACKET, data, i);
+
+       /* if the marker is smaller than 62 bytes,
+        * memorize the number of bytes to skip in the next packet */
+       if (i + 62 > len) {                     /* no more usable data */
+               sd->short_mark = i + 62 - len;
                return;
+       }
 
-       /* end of image found - remove the trailing data */
-       gspca_dev->image_len = sof + 2;
-       gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
        if (sd->ag_cnt >= 0)
-               set_lum(sd, data + sof + 2);
+               set_lum(sd, data + i);
+
+       /* if more data, start a new frame */
+       i += 62;
+       if (i < len) {
+               data += i;
+               len -= i;
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
+                               sd->jpeg_hdr, JPEG_HDR_SZ);
+               gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+       }
 }
 
 static int sd_set_jcomp(struct gspca_dev *gspca_dev,