while working on an update for ffmpeg, I noticed it's ability to record from a video(4) device is now completely broken because video(4) doesn't yet support VIDIOC_{S,G}_PARM.
so I added support for the VIDIOC_{S,G}_PARM ioctls to video(4) and uvideo(4). but then ffmpeg still couldn't record from a video device. more investigation found that uvideo(4) doesn't fill in the timetamp for frames when using the mmap() interface. so I added that too. but still, recording did not work correctly. it would work the first time after plugging in a camera, but not any time after that. eventually I found that the pointer to the current frame in the mmap() buffer does not get reset when the mmap() buffer is freed and reallocate once that was fixed, recording video from a video(4) device then worked like it should. so here's a diff that includes all the above mentioned changes, plus a few more that seemed appropriate: * the mjpeg and uncompreeesed frame descriptors are the same, so use a single struct, instead of two different one. also unify the handling of frame descriptors. * really quit using the frame index from the frame descriptor as an index to our array of frame descriptors. this means the array now starts at 0 instead of 1, which makes the code less confusing. * replace magic numbers with appropriate value (sizeof(struct usb_video_frame_desc) to be exact) * fill in uvideo_enum_fivals(), which is used for VIDIOC_ENUM_FRAMEINTERVALS ioctl please test with uvideo(4) devices to be sure this does not break anything that is currently working. this will be committed in pieces, but I wanted to get this full diff out for testing purposes. thanks. -- jake...@sdf.lonestar.org SDF Public Access UNIX System - http://sdf.lonestar.org Index: usb/uvideo.c =================================================================== RCS file: /cvs/src/sys/dev/usb/uvideo.c,v retrieving revision 1.149 diff -u -p usb/uvideo.c --- usb/uvideo.c 25 Jan 2011 20:03:36 -0000 1.149 +++ usb/uvideo.c 20 Mar 2011 21:10:57 -0000 @@ -48,6 +48,7 @@ #include <dev/video_if.h> +/* #define UVIDEO_DEBUG */ #ifdef UVIDEO_DEBUG int uvideo_debug = 1; #define DPRINTF(l, x...) do { if ((l) <= uvideo_debug) printf(x); } while (0) @@ -93,10 +94,8 @@ usbd_status uvideo_vs_parse_desc_format_mjpeg(struct u usbd_status uvideo_vs_parse_desc_format_uncompressed(struct uvideo_softc *, const usb_descriptor_t *); usbd_status uvideo_vs_parse_desc_frame(struct uvideo_softc *); -usbd_status uvideo_vs_parse_desc_frame_mjpeg(struct uvideo_softc *, +usbd_status uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *, const usb_descriptor_t *); -usbd_status uvideo_vs_parse_desc_frame_uncompressed(struct uvideo_softc *, - const usb_descriptor_t *); usbd_status uvideo_vs_parse_desc_alt(struct uvideo_softc *, int, int, int); usbd_status uvideo_vs_set_alt(struct uvideo_softc *, usbd_interface_handle, int); @@ -152,13 +151,11 @@ void uvideo_dump_desc_cs_endpoint(struct uvideo_softc const usb_descriptor_t *); void uvideo_dump_desc_colorformat(struct uvideo_softc *, const usb_descriptor_t *); -void uvideo_dump_desc_frame_mjpeg(struct uvideo_softc *, - const usb_descriptor_t *); void uvideo_dump_desc_format_mjpeg(struct uvideo_softc *, const usb_descriptor_t *); void uvideo_dump_desc_format_uncompressed(struct uvideo_softc *, const usb_descriptor_t *); -void uvideo_dump_desc_frame_uncompressed(struct uvideo_softc *, +void uvideo_dump_desc_frame(struct uvideo_softc *, const usb_descriptor_t *); void uvideo_dump_desc_processing(struct uvideo_softc *, const usb_descriptor_t *); @@ -178,6 +175,8 @@ int uvideo_enum_fsizes(void *, struct v4l2_frmsizeenu int uvideo_enum_fivals(void *, struct v4l2_frmivalenum *); int uvideo_s_fmt(void *, struct v4l2_format *); int uvideo_g_fmt(void *, struct v4l2_format *); +int uvideo_s_parm(void *, struct v4l2_streamparm *); +int uvideo_g_parm(void *, struct v4l2_streamparm *); int uvideo_enum_input(void *, struct v4l2_input *); int uvideo_s_input(void *, int); int uvideo_reqbufs(void *, struct v4l2_requestbuffers *); @@ -225,6 +224,8 @@ struct video_hw_if uvideo_hw_if = { uvideo_enum_fivals, /* VIDIOC_ENUM_FRAMEINTERVALS */ uvideo_s_fmt, /* VIDIOC_S_FMT */ uvideo_g_fmt, /* VIDIOC_G_FMT */ + uvideo_s_parm, /* VIDIOC_S_PARM */ + uvideo_g_parm, /* VIDIOC_G_PARM */ uvideo_enum_input, /* VIDIOC_ENUMINPUT */ uvideo_s_input, /* VIDIOC_S_INPUT */ uvideo_reqbufs, /* VIDIOC_REQBUFS */ @@ -553,7 +554,7 @@ uvideo_attach_hook(void *arg) /* init mmap queue */ SIMPLEQ_INIT(&sc->sc_mmap_q); - sc->sc_mmap_cur = 0; + sc->sc_mmap_cur = -1; sc->sc_mmap_count = 0; DPRINTF(1, "uvideo_attach: doing video_attach_mi\n"); @@ -634,7 +635,8 @@ uvideo_vc_parse_desc(struct uvideo_softc *sc) break; case UDESCSUB_VC_PROCESSING_UNIT: /* XXX do correct length calculation */ - if (desc->bLength < 25) { + if (desc->bLength < + sizeof(struct usb_video_frame_desc)) { (void)uvideo_vc_parse_desc_pu(sc, desc); } break; @@ -951,7 +953,6 @@ uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc printf("%s: too many format descriptors found!\n", DEVNAME(sc)); return (USBD_INVAL); } - sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format = (struct uvideo_format_desc *)d; if (d->bDefaultFrameIndex > d->bNumFrameDescriptors || @@ -1037,28 +1038,14 @@ uvideo_vs_parse_desc_frame(struct uvideo_softc *sc) usb_desc_iter_init(sc->sc_udev, &iter); desc = usb_desc_iter_next(&iter); while (desc) { - if (desc->bDescriptorType != UDESC_CS_INTERFACE) { - desc = usb_desc_iter_next(&iter); - continue; - } - - switch (desc->bDescriptorSubtype) { - case UDESCSUB_VS_FRAME_MJPEG: - error = uvideo_vs_parse_desc_frame_mjpeg(sc, desc); + if (desc->bDescriptorType == UDESC_CS_INTERFACE && + desc->bLength > sizeof(struct usb_video_frame_desc) && + (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG || + desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED)) { + error = uvideo_vs_parse_desc_frame_sub(sc, desc); if (error != USBD_NORMAL_COMPLETION) return (error); - break; - case UDESCSUB_VS_FRAME_UNCOMPRESSED: - /* XXX do correct length calculation */ - if (desc->bLength > 25) { - error =uvideo_vs_parse_desc_frame_uncompressed( - sc, desc); - if (error != USBD_NORMAL_COMPLETION) - return (error); - } - break; } - desc = usb_desc_iter_next(&iter); } @@ -1066,80 +1053,34 @@ uvideo_vs_parse_desc_frame(struct uvideo_softc *sc) } usbd_status -uvideo_vs_parse_desc_frame_mjpeg(struct uvideo_softc *sc, +uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *sc, const usb_descriptor_t *desc) { - struct usb_video_frame_mjpeg_desc *d; - int fmtidx; - - d = (struct usb_video_frame_mjpeg_desc *)(uint8_t *)desc; - - if (d->bFrameIndex == UVIDEO_MAX_FRAME) { - printf("%s: too many MJPEG frame descriptors found!\n", - DEVNAME(sc)); - return (USBD_INVAL); - } - - fmtidx = sc->sc_fmtgrp_idx; - sc->sc_fmtgrp[fmtidx].frame[d->bFrameIndex] = d; - - if (sc->sc_fmtgrp[fmtidx].format_dfidx == d->bFrameIndex) { - sc->sc_fmtgrp[fmtidx].frame_cur = - sc->sc_fmtgrp[fmtidx].frame[d->bFrameIndex]; - } - - sc->sc_fmtgrp[fmtidx].frame_num++; - - if (sc->sc_fmtgrp[fmtidx].frame_num == - sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) - sc->sc_fmtgrp_idx++; - - /* store max value */ - if (UGETDW(d->dwMaxVideoFrameBufferSize) > sc->sc_max_fbuf_size) - sc->sc_max_fbuf_size = UGETDW(d->dwMaxVideoFrameBufferSize); - - return (USBD_NORMAL_COMPLETION); -} - -usbd_status -uvideo_vs_parse_desc_frame_uncompressed(struct uvideo_softc *sc, - const usb_descriptor_t *desc) -{ - struct usb_video_frame_uncompressed_desc *fd = - (struct usb_video_frame_uncompressed_desc *)(uint8_t *)desc; + struct usb_video_frame_desc *fd = + (struct usb_video_frame_desc *)(uint8_t *)desc; int fmtidx, frame_num; uint32_t fbuf_size; fmtidx = sc->sc_fmtgrp_idx; - - frame_num = sc->sc_fmtgrp[fmtidx].frame_num + 1; + frame_num = sc->sc_fmtgrp[fmtidx].frame_num; if (frame_num >= UVIDEO_MAX_FRAME) { - printf("%s: too many UNCOMPRESSED frame descriptors found!\n", - DEVNAME(sc)); + printf("%s: too many %s frame descriptors found!\n", + DEVNAME(sc), + desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ? + "MJPEG" : "UNCOMPRESSED"); return (USBD_INVAL); } - sc->sc_fmtgrp[fmtidx].frame_num = frame_num; - - sc->sc_fmtgrp[fmtidx].frame[frame_num] = - (struct usb_video_frame_mjpeg_desc *)fd; - + sc->sc_fmtgrp[fmtidx].frame[frame_num] = fd; if (sc->sc_fmtgrp[fmtidx].frame_cur == NULL || - sc->sc_fmtgrp[fmtidx].format_dfidx == frame_num) { - sc->sc_fmtgrp[fmtidx].frame_cur = - sc->sc_fmtgrp[fmtidx].frame[frame_num]; - } + sc->sc_fmtgrp[fmtidx].format_dfidx == fd->bFrameIndex) + sc->sc_fmtgrp[fmtidx].frame_cur = fd; - if (sc->sc_fmtgrp[fmtidx].frame_num == - sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) { - sc->sc_fmtgrp_idx++; - } - /* * On some broken device, dwMaxVideoFrameBufferSize is not correct. * So fix it by frame width/height (XXX YUV2 format only). */ if (sc->sc_quirk && - sc->sc_quirk->flags & UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE && + (sc->sc_quirk->flags & UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE) && sc->sc_fmtgrp[fmtidx].pixelformat == V4L2_PIX_FMT_YUYV) { fbuf_size = UGETW(fd->wWidth) * UGETW(fd->wHeight) * 4; DPRINTF(1, "wWidth = %d, wHeight = %d\n", @@ -1151,6 +1092,15 @@ uvideo_vs_parse_desc_frame_uncompressed(struct uvideo_ if (fbuf_size > sc->sc_max_fbuf_size) sc->sc_max_fbuf_size = fbuf_size; + /* + * Increment frame count. If this is the last frame in the + * format group, go on to next group. + */ + if (++sc->sc_fmtgrp[fmtidx].frame_num == + sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) { + sc->sc_fmtgrp_idx++; + } + return (USBD_NORMAL_COMPLETION); } @@ -1338,7 +1288,7 @@ uvideo_find_res(struct uvideo_softc *sc, int idx, int size_want = width * height; - for (i = 1; i <= sc->sc_fmtgrp[idx].frame_num; i++) { + for (i = 0; i < sc->sc_fmtgrp[idx].frame_num; i++) { w = UGETW(sc->sc_fmtgrp[idx].frame[i]->wWidth); h = UGETW(sc->sc_fmtgrp[idx].frame[i]->wHeight); size_is = w * h; @@ -1346,13 +1296,13 @@ uvideo_find_res(struct uvideo_softc *sc, int idx, int diff = size_is - size_want; else diff = size_want - size_is; - if (i == 1) + if (i == 0) diff_best = diff; if (diff <= diff_best) { diff_best = diff; r->width = w; r->height = h; - r->fidx = sc->sc_fmtgrp[idx].frame[i]->bFrameIndex; + r->fidx = i; } DPRINTF(1, "%s: %s: frame index %d: width=%d, height=%d\n", DEVNAME(sc), __func__, i, w, h); @@ -1363,13 +1313,19 @@ usbd_status uvideo_vs_negotiation(struct uvideo_softc *sc, int commit) { struct usb_video_probe_commit *pc; + struct uvideo_format_group *fmtgrp; + uint8_t *p, *cur; uint8_t probe_data[34]; + uint32_t frame_ival, nivals, min, max, step, diff; usbd_status error; + int i, ival_bytes; pc = (struct usb_video_probe_commit *)probe_data; + fmtgrp = sc->sc_fmtgrp_cur; + /* check if the format descriptor contains frame descriptors */ - if (sc->sc_fmtgrp_cur->frame_num == 0) { + if (fmtgrp->frame_num == 0) { printf("%s: %s: no frame descriptors found!\n", __func__, DEVNAME(sc)); return (USBD_INVAL); @@ -1377,12 +1333,63 @@ uvideo_vs_negotiation(struct uvideo_softc *sc, int com /* set probe */ bzero(probe_data, sizeof(probe_data)); + /* hint that dwFrameInterval should be favored over other parameters */ USETW(pc->bmHint, 0x1); - pc->bFormatIndex = sc->sc_fmtgrp_cur->format->bFormatIndex; - pc->bFrameIndex = sc->sc_fmtgrp_cur->format_dfidx; + pc->bFormatIndex = fmtgrp->format->bFormatIndex; + pc->bFrameIndex = fmtgrp->frame_cur->bFrameIndex; /* dwFrameInterval: 30fps=333333, 15fps=666666, 10fps=1000000 */ - USETDW(pc->dwFrameInterval, - UGETDW(sc->sc_fmtgrp_cur->frame_cur->dwDefaultFrameInterval)); + frame_ival = UGETDW(fmtgrp->frame_cur->dwDefaultFrameInterval); + if (sc->sc_frame_rate != 0) { + frame_ival = 10000000 / sc->sc_frame_rate; + /* find closest matching interval the device supports */ + p = (uint8_t *)fmtgrp->frame_cur; + p += sizeof(struct usb_video_frame_desc); + nivals = fmtgrp->frame_cur->bFrameIntervalType; + ival_bytes = fmtgrp->frame_cur->bLength - + sizeof(struct usb_video_frame_desc); + if (!nivals && (ival_bytes >= sizeof(uDWord) * 3)) { + /* continuous */ + min = UGETDW(p); + p += sizeof(uDWord); + max = UGETDW(p); + p += sizeof(uDWord); + step = UGETDW(p); + p += sizeof(uDWord); + if (frame_ival <= min) + frame_ival = min; + else if (frame_ival >= max) + frame_ival = max; + else { + for (i = min; i + step/2 < frame_ival; i+= step) + ; /* nothing */ + frame_ival = i; + } + } else if (nivals > 0 && ival_bytes >= sizeof(uDWord)) { + /* discrete */ + cur = p; + min = UINT_MAX; + for (i = 0; i < nivals; i++) { + if (ival_bytes < sizeof(uDWord)) { + /* short descriptor ? */ + break; + } + diff = abs(UGETDW(p) - frame_ival); + if (diff < min) { + min = diff; + cur = p; + if (diff == 0) + break; + } + p += sizeof(uDWord); + ival_bytes -= sizeof(uDWord); + } + frame_ival = UGETDW(cur); + } else { + DPRINTF(1, "%s: %s: bad frame ival descriptor\n", + DEVNAME(sc), __func__); + } + } + USETDW(pc->dwFrameInterval, frame_ival); error = uvideo_vs_set_probe(sc, probe_data); if (error != USBD_NORMAL_COMPLETION) return (error); @@ -1594,6 +1601,12 @@ uvideo_vs_free_frame(struct uvideo_softc *sc) free(sc->sc_mmap_buffer, M_DEVBUF); sc->sc_mmap_buffer = NULL; } + + while (!SIMPLEQ_EMPTY(&sc->sc_mmap_q)) + SIMPLEQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames); + + sc->sc_mmap_cur = -1; + sc->sc_mmap_count = 0; } usbd_status @@ -2099,6 +2112,10 @@ uvideo_vs_decode_stream_header_isight(struct uvideo_so void uvideo_mmap_queue(struct uvideo_softc *sc, uint8_t *buf, int len) { + if (sc->sc_mmap_cur < 0 || sc->sc_mmap_count == 0 || + sc->sc_mmap_buffer == NULL) + panic("%s: mmap buffers not allocated", __func__); + /* find a buffer which is ready for queueing */ while (sc->sc_mmap_cur < sc->sc_mmap_count) { if (sc->sc_mmap[sc->sc_mmap_cur].v4l2_buf.flags & @@ -2114,6 +2131,9 @@ uvideo_mmap_queue(struct uvideo_softc *sc, uint8_t *bu bcopy(buf, sc->sc_mmap[sc->sc_mmap_cur].buf, len); sc->sc_mmap[sc->sc_mmap_cur].v4l2_buf.bytesused = len; + /* timestamp it */ + getmicrotime(&sc->sc_mmap[sc->sc_mmap_cur].v4l2_buf.timestamp); + /* queue it */ SIMPLEQ_INSERT_TAIL(&sc->sc_mmap_q, &sc->sc_mmap[sc->sc_mmap_cur], q_frames); @@ -2214,11 +2234,11 @@ uvideo_dump_desc_all(struct uvideo_softc *sc) printf("bDescriptorSubtype=0x%02x", desc->bDescriptorSubtype); /* XXX do correct length calculation */ - if (desc->bLength > 25) { + if (desc->bLength > + sizeof(struct usb_video_frame_desc)) { printf(" (UDESCSUB_VS_FRAME_" "UNCOMPRESSED)\n"); - uvideo_dump_desc_frame_uncompressed( - sc, desc); + uvideo_dump_desc_frame(sc, desc); } else { printf(" (UDESCSUB_VC_PROCESSING_" "UNIT)\n"); @@ -2244,9 +2264,10 @@ uvideo_dump_desc_all(struct uvideo_softc *sc) printf("bDescriptorSubtype=0x%02x", desc->bDescriptorSubtype); printf(" (UDESCSUB_VS_FRAME_MJPEG)\n"); - if (desc->bLength > 26) { + if (desc->bLength > + sizeof(struct usb_video_frame_desc)) { printf("|\n"); - uvideo_dump_desc_frame_mjpeg(sc, desc); + uvideo_dump_desc_frame(sc, desc); } break; case UDESCSUB_VS_COLORFORMAT: @@ -2483,32 +2504,7 @@ uvideo_dump_desc_colorformat(struct uvideo_softc *sc, d->bTransferCharacteristics); printf("bMatrixCoefficients=0x%02x\n", d->bMatrixCoefficients); } - void -uvideo_dump_desc_frame_mjpeg(struct uvideo_softc *sc, - const usb_descriptor_t *desc) -{ - struct usb_video_frame_mjpeg_desc *d; - - d = (struct usb_video_frame_mjpeg_desc *)(uint8_t *)desc; - - printf("bLength=%d\n", d->bLength); - printf("bDescriptorType=0x%02x\n", d->bDescriptorType); - printf("bDescriptorSubtype=0x%02x\n", d->bDescriptorSubtype); - printf("bFrameIndex=0x%02x\n", d->bFrameIndex); - printf("bmCapabilities=0x%02x\n", d->bmCapabilities); - printf("wWidth=%d\n", UGETW(d->wWidth)); - printf("wHeight=%d\n", UGETW(d->wHeight)); - printf("dwMinBitRate=%d\n", UGETDW(d->dwMinBitRate)); - printf("dwMaxBitRate=%d\n", UGETDW(d->dwMaxBitRate)); - printf("dwMaxVideoFrameBufferSize=%d\n", - UGETDW(d->dwMaxVideoFrameBufferSize)); - printf("dwDefaultFrameInterval=%d\n", - UGETDW(d->dwDefaultFrameInterval)); - printf("bFrameIntervalType=0x%02x\n", d->bFrameIntervalType); -} - -void uvideo_dump_desc_format_mjpeg(struct uvideo_softc *sc, const usb_descriptor_t *desc) { @@ -2530,12 +2526,13 @@ uvideo_dump_desc_format_mjpeg(struct uvideo_softc *sc, } void -uvideo_dump_desc_frame_uncompressed(struct uvideo_softc *sc, - const usb_descriptor_t *desc) +uvideo_dump_desc_frame(struct uvideo_softc *sc, const usb_descriptor_t *desc) { - struct usb_video_frame_uncompressed_desc *d; + struct usb_video_frame_desc *d; + uint8_t *p; + int length, i; - d = (struct usb_video_frame_uncompressed_desc *)(uint8_t *)desc; + d = (struct usb_video_frame_desc *)(uint8_t *)desc; printf("bLength=%d\n", d->bLength); printf("bDescriptorType=0x%02x\n", d->bDescriptorType); @@ -2551,6 +2548,35 @@ uvideo_dump_desc_frame_uncompressed(struct uvideo_soft printf("dwDefaultFrameInterval=%d\n", UGETDW(d->dwDefaultFrameInterval)); printf("bFrameIntervalType=0x%02x\n", d->bFrameIntervalType); + + p = (uint8_t *)d; + p += sizeof(struct usb_video_frame_desc); + + if (!d->bFrameIntervalType) { + /* continuous */ + if (d->bLength < 38) { + printf("invalid frame desc length for continuous\n"); + } else { + printf("dwMinFrameInterval = %d\n", UGETDW(p)); + p += sizeof(uDWord); + printf("dwMaxFrameInterval = %d\n", UGETDW(p)); + p += sizeof(uDWord); + printf("dwFrameIntervalStep = %d\n", UGETDW(p)); + p += sizeof(uDWord); + } + } else { + /* discrete */ + length = d->bLength - sizeof(struct usb_video_frame_desc); + for (i = 0; i < d->bFrameIntervalType; i++) { + if (length <= 0) { + printf("more frame intervals, but desc ended\n"); + break; + } + printf("dwFrameInterval = %d\n", UGETDW(p)); + p += sizeof(uDWord); + length -= sizeof(uDWord); + } + } } void @@ -2767,7 +2793,7 @@ int uvideo_enum_fsizes(void *v, struct v4l2_frmsizeenum *fsizes) { struct uvideo_softc *sc = v; - int i, idx, found = 0; + int idx, found = 0; for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) { if (sc->sc_fmtgrp[idx].pixelformat == fsizes->pixel_format) { @@ -2778,16 +2804,14 @@ uvideo_enum_fsizes(void *v, struct v4l2_frmsizeenum *f if (found == 0) return (EINVAL); - i = fsizes->index + 1; - if (i > sc->sc_fmtgrp[idx].frame_num) - /* no more frames left */ + if (fsizes->index >= sc->sc_fmtgrp[idx].frame_num) return (EINVAL); fsizes->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsizes->un.discrete.width = - UGETW(sc->sc_fmtgrp[idx].frame[i]->wWidth); + UGETW(sc->sc_fmtgrp[idx].frame[fsizes->index]->wWidth); fsizes->un.discrete.height = - UGETW(sc->sc_fmtgrp[idx].frame[i]->wHeight); + UGETW(sc->sc_fmtgrp[idx].frame[fsizes->index]->wHeight); return (0); } @@ -2796,20 +2820,61 @@ int uvideo_enum_fivals(void *v, struct v4l2_frmivalenum *fivals) { struct uvideo_softc *sc = v; - int idx, found = 0; + int idx; + struct uvideo_format_group *fmtgrp = NULL; + struct usb_video_frame_desc *frame = NULL; + uint8_t *p; for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) { if (sc->sc_fmtgrp[idx].pixelformat == fivals->pixel_format) { - found = 1; + fmtgrp = &sc->sc_fmtgrp[idx]; break; } } - if (found == 0) + if (fmtgrp == NULL) return (EINVAL); - /* TODO */ + for (idx = 0; idx < fmtgrp->frame_num; idx++) { + if (UGETW(fmtgrp->frame[idx]->wWidth) == fivals->width && + UGETW(fmtgrp->frame[idx]->wHeight) == fivals->height) { + frame = fmtgrp->frame[idx]; + break; + } + } + if (frame == NULL) + return (EINVAL); - return (EINVAL); + /* byte-wise pointer to start of frame intervals */ + p = (uint8_t *)frame; + p += sizeof(struct usb_video_frame_desc); + + if (frame->bFrameIntervalType == 0) { + if (fivals->index != 0) + return (EINVAL); + fivals->type = V4L2_FRMIVAL_TYPE_STEPWISE; + fivals->un.stepwise.min.numerator = UGETDW(p); + fivals->un.stepwise.min.denominator = 10000000; + p += sizeof(uDWord); + fivals->un.stepwise.max.numerator = UGETDW(p); + fivals->un.stepwise.max.denominator = 10000000; + p += sizeof(uDWord); + fivals->un.stepwise.step.numerator = UGETDW(p); + fivals->un.stepwise.step.denominator = 10000000; + p += sizeof(uDWord); + } else { + if (fivals->index >= frame->bFrameIntervalType) + return (EINVAL); + p += sizeof(uDWord) * fivals->index; + if (p > frame->bLength + (uint8_t *)frame) { + printf("%s: frame desc too short?\n", __func__); + return (EINVAL); + } + fivals->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fivals->un.discrete.numerator = UGETDW(p); + fivals->un.discrete.denominator = 10000000; + } + + return (0); } int @@ -2817,7 +2882,7 @@ uvideo_s_fmt(void *v, struct v4l2_format *fmt) { struct uvideo_softc *sc = v; struct uvideo_format_group *fmtgrp_save; - struct usb_video_frame_mjpeg_desc *frame_save; + struct usb_video_frame_desc *frame_save; struct uvideo_res r; int found, i; usbd_status error; @@ -2854,10 +2919,11 @@ uvideo_s_fmt(void *v, struct v4l2_format *fmt) /* save a copy of current fromat group in case of negotiation fails */ fmtgrp_save = sc->sc_fmtgrp_cur; frame_save = sc->sc_fmtgrp_cur->frame_cur; - /* set new format group */ + + /* set new format group and frame */ sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[i]; sc->sc_fmtgrp[i].frame_cur = sc->sc_fmtgrp[i].frame[r.fidx]; - sc->sc_fmtgrp[i].format_dfidx = r.fidx; + /* do device negotiation with commit */ error = uvideo_vs_negotiation(sc, 1); if (error != USBD_NORMAL_COMPLETION) { @@ -2900,6 +2966,53 @@ uvideo_g_fmt(void *v, struct v4l2_format *fmt) } int +uvideo_s_parm(void *v, struct v4l2_streamparm *parm) +{ + struct uvideo_softc *sc = v; + usbd_status error; + + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (parm->parm.capture.timeperframe.numerator == 0 || + parm->parm.capture.timeperframe.denominator == 0) + return (EINVAL); + + /* only whole number frame rates for now */ + sc->sc_frame_rate = + parm->parm.capture.timeperframe.numerator / + parm->parm.capture.timeperframe.denominator; + } else + return (EINVAL); + + /* renegotiate if necessary */ + if (sc->sc_negotiated_flag) { + error = uvideo_vs_negotiation(sc, 1); + if (error != USBD_NORMAL_COMPLETION) + return (error); + } + + return (0); +} + +int +uvideo_g_parm(void *v, struct v4l2_streamparm *parm) +{ + struct uvideo_softc *sc = v; + int ns; + + ns = UGETDW(sc->sc_desc_probe.dwFrameInterval); + if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.capturemode = 0; + parm->parm.capture.timeperframe.numerator = + (ns == 0) ? 0 : 10000000 / ns; + parm->parm.capture.timeperframe.denominator = 1; + } else + return (EINVAL); + + return (0); +} + +int uvideo_enum_input(void *v, struct v4l2_input *input) { if (input->index != 0) @@ -2930,6 +3043,9 @@ uvideo_reqbufs(void *v, struct v4l2_requestbuffers *rb DPRINTF(1, "%s: %s: count=%d\n", DEVNAME(sc), __func__, rb->count); + if (sc->sc_mmap_count > 0 || sc->sc_mmap_buffer != NULL) + panic("%s: mmap buffers already allocated", __func__); + /* limit the buffers */ if (rb->count > UVIDEO_MAX_BUFFERS) sc->sc_mmap_count = UVIDEO_MAX_BUFFERS; @@ -2970,6 +3086,9 @@ uvideo_reqbufs(void *v, struct v4l2_requestbuffers *rb /* tell how many buffers we have really allocated */ rb->count = sc->sc_mmap_count; + + /* start with the first buffer */ + sc->sc_mmap_cur = 0; return (0); } Index: usb/uvideo.h =================================================================== RCS file: /cvs/src/sys/dev/usb/uvideo.h,v retrieving revision 1.51 diff -u -p usb/uvideo.h --- usb/uvideo.h 27 Nov 2010 00:38:29 -0000 1.51 +++ usb/uvideo.h 20 Mar 2011 21:10:57 -0000 @@ -343,8 +343,8 @@ struct usb_video_format_mjpeg_desc { uByte bCopyProtect; } __packed; -/* Table 3-2: Motion-JPEG Video Frame Descriptor */ -struct usb_video_frame_mjpeg_desc { +/* Table 3-2: Video Frame Descriptor (same for mjpeg and uncompressed)*/ +struct usb_video_frame_desc { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -357,7 +357,7 @@ struct usb_video_frame_mjpeg_desc { uDWord dwMaxVideoFrameBufferSize; uDWord dwDefaultFrameInterval; uByte bFrameIntervalType; - /* TODO add continous/discrete frame intervals (Table 3-3/3-4) */ + /* uDWord ivals[]; frame intervals, length varies */ } __packed; /* @@ -379,23 +379,6 @@ struct usb_video_format_uncompressed_desc { uByte bCopyProtect; } __packed; -/* Table 3-2: Uncompressed Video Frame Descriptor */ -struct usb_video_frame_uncompressed_desc { - uByte bLength; - uByte bDescriptorType; - uByte bDescriptorSubtype; - uByte bFrameIndex; - uByte bmCapabilities; - uWord wWidth; - uWord wHeight; - uDWord dwMinBitRate; - uDWord dwMaxBitRate; - uDWord dwMaxVideoFrameBufferSize; - uDWord dwDefaultFrameInterval; - uByte bFrameIntervalType; - /* TODO add continous/discrete frame intervals (Table 3-3/3-4) */ -} __packed; - /* * Driver specific private definitions. */ @@ -481,8 +464,8 @@ struct uvideo_format_group { struct uvideo_format_desc *format; /* frame descriptors for mjpeg and uncompressed are identical */ #define UVIDEO_MAX_FRAME 32 - struct usb_video_frame_mjpeg_desc *frame_cur; - struct usb_video_frame_mjpeg_desc *frame[UVIDEO_MAX_FRAME]; + struct usb_video_frame_desc *frame_cur; + struct usb_video_frame_desc *frame[UVIDEO_MAX_FRAME]; int frame_num; }; @@ -687,6 +670,7 @@ struct uvideo_softc { int sc_dying; int sc_max_fbuf_size; int sc_negotiated_flag; + int sc_frame_rate; struct uvideo_frame_buffer sc_frame_buffer; Index: video.c =================================================================== RCS file: /cvs/src/sys/dev/video.c,v retrieving revision 1.26 diff -u -p video.c --- video.c 26 Dec 2010 15:41:00 -0000 1.26 +++ video.c 20 Mar 2011 21:10:57 -0000 @@ -231,6 +231,16 @@ videoioctl(dev_t dev, u_long cmd, caddr_t data, int fl error = (sc->hw_if->g_fmt)(sc->hw_hdl, (struct v4l2_format *)data); break; + case VIDIOC_S_PARM: + if (sc->hw_if->s_parm) + error = (sc->hw_if->s_parm)(sc->hw_hdl, + (struct v4l2_streamparm *)data); + break; + case VIDIOC_G_PARM: + if (sc->hw_if->g_parm) + error = (sc->hw_if->g_parm)(sc->hw_hdl, + (struct v4l2_streamparm *)data); + break; case VIDIOC_ENUMINPUT: if (sc->hw_if->enum_input) error = (sc->hw_if->enum_input)(sc->hw_hdl, Index: video_if.h =================================================================== RCS file: /cvs/src/sys/dev/video_if.h,v retrieving revision 1.16 diff -u -p video_if.h --- video_if.h 24 Aug 2008 11:05:02 -0000 1.16 +++ video_if.h 20 Mar 2011 21:10:57 -0000 @@ -40,6 +40,8 @@ struct video_hw_if { int (*enum_fivals)(void *, struct v4l2_frmivalenum *); int (*s_fmt)(void *, struct v4l2_format *); int (*g_fmt)(void *, struct v4l2_format *); + int (*s_parm)(void *, struct v4l2_streamparm *); + int (*g_parm)(void *, struct v4l2_streamparm *); int (*enum_input)(void *, struct v4l2_input *); int (*s_input)(void *, int); int (*reqbufs)(void *, struct v4l2_requestbuffers *);