Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BaseUsbHost EthernetInterface WebSocketClient mbed-rtos mbed
Fork of BaseUsbHost_example by
Revision 7:5dc595bbff58, committed 2013-02-19
- Comitter:
- va009039
- Date:
- Tue Feb 19 15:50:06 2013 +0000
- Parent:
- 6:420a86583681
- Commit message:
- video streaming using websocket
Changed in this revision
| UvcCam/UvcCam.cpp | Show annotated file Show diff for this revision Revisions of this file |
| UvcCam/UvcCam.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UvcCam/UvcCam.cpp Tue Feb 19 15:50:06 2013 +0000
@@ -0,0 +1,620 @@
+// UvcCam.cpp 2013/2/11
+#include "mbed.h"
+#include "rtos.h"
+#include "BaseUsbHost.h"
+//#define DEBUG
+#include "BaseUsbHostDebug.h"
+#define TEST
+#include "BaseUsbHostTest.h"
+#include "UvcCam.h"
+#include <string>
+
+UvcCam::UvcCam(int formatIndex, int frameIndex, uint32_t interval, ControlEp* ctlEp)
+{
+ uint8_t buf[34];
+ int rc;
+ int alt;
+ int cfg2;
+
+ if (ctlEp == NULL) { // root hub
+ DBG_OHCI(LPC_USB->HcRhPortStatus1);
+ TEST_ASSERT_FALSE(LPC_USB->HcRhPortStatus1 & 0x200);
+ ctlEp = new ControlEp();
+ TEST_ASSERT_TRUE(ctlEp);
+ }
+ bool r = check(ctlEp);
+ TEST_ASSERT(r);
+ m_ctlEp = ctlEp;
+
+ rc = ctlEp->GetDescriptor(1, 0, buf, sizeof(StandardDeviceDescriptor));
+ TEST_ASSERT(rc == USB_OK);
+ StandardDeviceDescriptor* sd = reinterpret_cast<StandardDeviceDescriptor*>(buf);
+ VERBOSE("vid: %04x\n", sd->idVendor);
+ VERBOSE("pid: %04x\n", sd->idProduct);
+ string s = ctlEp->GetStringDescriptor(sd->iManufacturer);
+ VERBOSE("iManufacturer: %s\n", s.c_str());
+ s = ctlEp->GetStringDescriptor(sd->iProduct);
+ VERBOSE("iProduct: %s\n", s.c_str());
+ vid = sd->idVendor;
+ pid = sd->idProduct;
+
+ UvcCfg* cfg = new UvcCfg(formatIndex, frameIndex, ctlEp);
+ TEST_ASSERT(cfg);
+
+ int param_len = 34;
+ TEST_ASSERT(cfg->bcdUVC >= 0x0100);
+ if (cfg->bcdUVC == 0x0100) { // UVC ver. 1.0
+ param_len = 26;
+ }
+
+ int addr = m_ctlEp->GetAddr();
+ m_isoEp = new IsochronousEp(addr, cfg->bEndpointAddress, cfg->wMaxPacketSize);
+ TEST_ASSERT_TRUE(m_isoEp);
+
+//#define USE_PROBE
+
+#ifdef USE_PROBE
+ rc = Control(GET_INFO, VS_PROBE_CONTROL, 1, buf, 1);
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("GET_INFO Prob", buf, 1);
+
+ rc = Control(GET_DEF, VS_PROBE_CONTROL, 1, buf, param_len);
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("GET_DEF Probe", buf, param_len);
+
+ rc = Control(GET_MIN, VS_PROBE_CONTROL, 1, buf, param_len);
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("GET_MIN Probe", buf, param_len);
+
+ rc = Control(GET_MAX, VS_PROBE_CONTROL, 1, buf, param_len);
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("GET_MAX Probe", buf, param_len);
+
+ rc = Control(GET_CUR, VS_PROBE_CONTROL, 1, buf, param_len);
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("GET_CUR Probe", buf, param_len);
+#endif // USE_PROBE
+
+ rc = Control(GET_INFO, VS_COMMIT_CONTROL, 1, buf, 1);
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("GET_INFO Commit", buf, 1);
+
+ memset(buf, 0, param_len);
+ buf[2] = cfg->FormatIndex;
+ buf[3] = cfg->FrameIndex;
+ *reinterpret_cast<uint32_t*>(buf+4) = interval;
+
+ DBG_BYTES("SET_CUR Commit", buf, param_len);
+ rc = Control(SET_CUR, VS_COMMIT_CONTROL, 1, buf, param_len);
+ TEST_ASSERT(rc == USB_OK);
+
+ rc = Control(GET_CUR, VS_COMMIT_CONTROL, 1, buf, param_len);
+ TEST_ASSERT(rc == USB_OK);
+ TEST_ASSERT_EQUAL(buf[2], cfg->FormatIndex);
+ TEST_ASSERT_EQUAL(buf[3], cfg->FrameIndex);
+ TEST_ASSERT_EQUAL(*reinterpret_cast<uint32_t*>(buf+4), interval);
+ DBG_BYTES("GET_CUR Commit", buf, param_len);
+
+ rc = m_ctlEp->GetConfiguration(&cfg2);
+ TEST_ASSERT_EQUAL(rc, USB_OK);
+ DBG("config: %d\n", cfg2);
+
+ rc = m_ctlEp->SetConfiguration(1);
+ TEST_ASSERT_EQUAL(rc, USB_OK);
+
+ rc = m_ctlEp->GetConfiguration(&cfg2);
+ TEST_ASSERT_EQUAL(rc, USB_OK);
+ DBG("config: %d\n", cfg2);
+ TEST_ASSERT_EQUAL(cfg2, 1);
+
+ rc = m_ctlEp->GetInterface(cfg->bInterface, &alt);
+ TEST_ASSERT_EQUAL(rc, USB_OK);
+ DBG("alt: %d\n", alt);
+
+ rc = m_ctlEp->SetInterfaceAlternate(cfg->bInterface, cfg->bAlternate);
+ TEST_ASSERT_EQUAL(rc, USB_OK);
+
+ rc = m_ctlEp->GetInterface(cfg->bInterface, &alt);
+ TEST_ASSERT_EQUAL(rc, USB_OK);
+ DBG("alt: %d\n", alt);
+ TEST_ASSERT_EQUAL(alt, cfg->bAlternate);
+ delete cfg;
+
+ for(int i = 0; i < 16; i++) {
+ report_cc_count[i] = 0;
+ report_ps_cc_count[i] = 0;
+ }
+
+ LPC_USB->HcControl |= OR_CONTROL_PLE; // PeriodicListEnable
+ LPC_USB->HcControl |= OR_CONTROL_IE; // IsochronousEnable
+}
+
+bool UvcCam::check(ControlEp* ctlEp)
+{
+ if (ctlEp == NULL) {
+ return false;
+ }
+ uint8_t buf[18];
+ int r = ctlEp->GetDescriptor(1, 0, buf, 8);
+ if (r != USB_OK) {
+ return false;
+ }
+ DBG_HEX(buf, 8);
+ const uint8_t desc[] = {0x12,0x01,0x00,0x02,0xef,0x02,0x01};
+ if (memcmp(buf, desc, sizeof(desc)) != 0) {
+ return false;
+ }
+ r = ctlEp->GetDescriptor(1, 0, buf, sizeof(buf));
+ if (r != USB_OK) {
+ return false;
+ }
+ DBG_HEX(buf, 18);
+ return true;
+}
+
+#define DESCRIPTOR_TYPE_DEVICE 1
+#define DESCRIPTOR_TYPE_CONFIGURATION 2
+#define DESCRIPTOR_TYPE_STRING 3
+#define DESCRIPTOR_TYPE_INTERFACE 4
+#define DESCRIPTOR_TYPE_ENDPOINT 5
+
+#define DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION 0x0b
+
+#define DESCRIPTOR_TYPE_HID 0x21
+#define DESCRIPTOR_TYPE_REPORT 0x22
+#define DESCRIPTOR_TYPE_PHYSICAL 0x23
+#define DESCRIPTOR_TYPE_CS_INTERFACE 0x24
+#define DESCRIPTOR_TYPE_CS_ENDPOINT 0x25
+#define DESCRIPTOR_TYPE_HUB 0x29
+
+#define CLASS_AUDIO 0x02
+#define CLASS_HUB 0x09
+
+#define IF_EQ_THEN_PRINTF(A,B) if (A == B) {VERBOSE("%s\n", #A);
+#define ENDIF }
+
+#define CC_AUDIO 0x01
+#define SC_AUDIOCONTROL 0x01
+#define SC_AUDIOSTREAMING 0x02
+
+#define AC_HEADER 0x01
+#define AC_INPUT_TERMINAL 0x02
+#define AC_OUTPUT_TERMINAL 0x03
+#define AC_FEATURE_UNIT 0x06
+
+// Input Terminal Types
+#define ITT_CAMERA 0x0201
+
+static int LE16(const uint8_t* d)
+{
+ return d[0] | (d[1] << 8);
+}
+
+static int LE24(const uint8_t* d) {
+ return d[0] | (d[1]<<8) | (d[2] << 16);
+}
+
+static int LE32(const uint8_t* d) {
+ return d[0] |(d[1]<<8) | (d[2] << 16) |(d[3] << 24) ;
+}
+
+struct GUID {
+ uint8_t data[16];
+ string to_string() {
+ string s;
+ for(int i = 0; i < 16; i++) {
+ char buf[3];
+ snprintf(buf, sizeof(buf), "%02x", data[i]);
+ s += buf;
+ if (i == 3 || i == 5 || i == 7 || i == 9) {
+ s += "-";
+ }
+ }
+ return s;
+ }
+ bool isFormat(const char* fmt) {
+ int len = strlen(fmt);
+ return memcmp(data, fmt, len) == 0;
+ }
+};
+
+UvcCfg::UvcCfg(int formatIndex, int frameIndex, ControlEp* ctlEp):wMaxPacketSize(0),_ctlEp(ctlEp)
+{
+ TEST_ASSERT(ctlEp);
+ switch(formatIndex) {
+ case UVC_MJPEG: _payload = UVC_MJPEG; break;
+ case UVC_YUY2: _payload = UVC_YUY2; break;
+ default: _payload = UVC_MJPEG; break;
+ }
+
+ switch(frameIndex) {
+ case UVC_160x120: _width = 160; _height = 120; break;
+ case UVC_176x144: _width = 176; _height = 144; break;
+ case UVC_320x176: _width = 320; _height = 176; break;
+ case UVC_320x240: _width = 320; _height = 240; break;
+ case UVC_352x288: _width = 352; _height = 288; break;
+ case UVC_432x240: _width = 432; _height = 240; break;
+ case UVC_640x480: _width = 640; _height = 480; break;
+ case UVC_544x288: _width = 544; _height = 288; break;
+ case UVC_640x360: _width = 640; _height = 360; break;
+ case UVC_752x416: _width = 752; _height = 416; break;
+ case UVC_800x448: _width = 800; _height = 448; break;
+ case UVC_800x600: _width = 800; _height = 600; break;
+ default: _width = 160; _height = 120; break;
+ }
+ int index = 0;
+ uint8_t temp[4];
+ int rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, temp, sizeof(temp));
+ TEST_ASSERT(rc == USB_OK);
+ DBG_BYTES("Config Descriptor 4bytes", temp, sizeof(temp));
+ TEST_ASSERT(temp[0] == 9);
+ TEST_ASSERT(temp[1] == 0x02);
+ int TotalLength = LE16(temp+2);
+ DBG("TotalLength: %d\n", TotalLength);
+
+ uint8_t* buf = new uint8_t[TotalLength];
+ TEST_ASSERT(buf);
+ rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, buf, TotalLength);
+ TEST_ASSERT(rc == USB_OK);
+ if (rc != USB_OK) {
+ delete[] buf;
+ return;
+ }
+ _parserConfigurationDescriptor(buf, TotalLength);
+ delete[] buf;
+}
+
+void UvcCfg::_parserAudioControl(uint8_t* buf, int len) {
+ int subtype = buf[2];
+ IF_EQ_THEN_PRINTF(AC_HEADER, subtype)
+ VERBOSE("ADC: %04x\n", LE16(buf+3));
+ VERBOSE("TotalLength: %d\n", LE16(buf+5));
+ VERBOSE("InCollection: %d\n", buf[7]);
+ for (int n = 1; n <= buf[7]; n++) {
+ VERBOSE("aInterfaceNr(%d): %d\n", n, buf[8+n-1]);
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(AC_INPUT_TERMINAL, subtype)
+ VERBOSE("TerminalID: %d\n", buf[3]);
+ VERBOSE("TerminalType: %04X\n", LE16(buf+4));
+ VERBOSE("AssocTermianl: %d\n", buf[6]);
+ VERBOSE("NrChannels: %d\n", buf[7]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(AC_OUTPUT_TERMINAL, subtype)
+ VERBOSE("TerminalID: %d\n", buf[3]);
+ VERBOSE("TerminalType: %04X\n", LE16(buf+4));
+ VERBOSE("AssocTermianl: %d\n", buf[6]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(AC_FEATURE_UNIT, subtype)
+ VERBOSE("UnitID: %d\n", buf[3]);
+ VERBOSE("SourceID: %d\n", buf[4]);
+ VERBOSE("ControlSize: %d\n", buf[5]);
+ ENDIF
+}
+
+#define AS_GENERAL 0x01
+#define AS_FORMAT_TYPE 0x02
+
+void UvcCfg::_parserAudioStream(uint8_t* buf, int len) {
+ int subtype = buf[2];
+ IF_EQ_THEN_PRINTF(AS_GENERAL, subtype)
+ VERBOSE("TerminalLink: %d\n", buf[3]);
+ VERBOSE("Delay: %d\n", buf[4]);
+ VERBOSE("FormatTag: %04x\n", LE16(buf+5));
+ ENDIF
+ IF_EQ_THEN_PRINTF(AS_FORMAT_TYPE, subtype)
+ VERBOSE("FormatType: %d\n", buf[3]);
+ VERBOSE("NrChannels: %d\n", buf[4]);
+ VERBOSE("SubFrameSize: %d\n", buf[5]);
+ VERBOSE("BitResolution: %d\n", buf[6]);
+ VERBOSE("SamFreqType: %d\n", buf[7]);
+ VERBOSE("SamFreq(1): %d\n", LE24(buf+8));
+ ENDIF
+}
+
+#define CC_VIDEO 0x0e
+
+#define SC_VIDEOCONTROL 0x01
+#define SC_VIDEOSTREAMING 0x02
+
+#define VC_HEADER 0x01
+#define VC_INPUT_TERMINAL 0x02
+#define VC_OUTPUT_TERMINAL 0x03
+#define VC_SELECTOR_UNIT 0x04
+#define VC_PROCESSING_UNIT 0x05
+#define VC_EXTENSION_UNIT 0x06
+
+void UvcCfg::_parserVideoControl(uint8_t* buf, int len) {
+ int subtype = buf[2];
+ IF_EQ_THEN_PRINTF(VC_HEADER, subtype)
+ bcdUVC = LE16(buf+3);
+ VERBOSE("UVC: %04x\n", bcdUVC);
+ VERBOSE("TotalLength: %d\n", LE16(buf+5));
+ VERBOSE("ClockFrequency: %d\n", LE32(buf+7));
+ VERBOSE("InCollection: %d\n", buf[11]);
+ VERBOSE("aInterfaceNr(1): %d\n", buf[12]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(VC_INPUT_TERMINAL, subtype)
+ VERBOSE("TerminalID: %d\n", buf[3]);
+ uint16_t tt = LE16(buf+4);
+ VERBOSE("TerminalType: %04X\n", tt);
+ VERBOSE("AssocTerminal: %d\n", buf[6]);
+ VERBOSE("Terminal: %d\n", buf[7]);
+ if (tt == ITT_CAMERA) { // camera
+ int bControlSize = buf[14];
+ VERBOSE("ControlSize: %d\n", bControlSize);
+ for(int i = 0; i < bControlSize; i++) {
+ uint8_t bControls = buf[15+i];
+ VERBOSE("Controls(%d): %02X\n", i, bControls);
+ }
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(VC_OUTPUT_TERMINAL, subtype)
+ VERBOSE("TerminalID: %d\n", buf[3]);
+ VERBOSE("TerminalType: %04X\n", LE16(buf+4));
+ VERBOSE("AssocTerminal: %d\n", buf[6]);
+ VERBOSE("SourceID: %d\n", buf[7]);
+ VERBOSE("Terminal: %d\n", buf[8]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(VC_SELECTOR_UNIT, subtype)
+ VERBOSE("UnitID: %d\n", buf[3]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(VC_PROCESSING_UNIT, subtype)
+ VERBOSE("UnitID: %d\n", buf[3]);
+ VERBOSE("SourceID: %d\n", buf[4]);
+ VERBOSE("MaxMultiplier: %d\n", LE16(buf+5));
+ VERBOSE("ControlSize: %d\n", buf[7]);
+ int pos = 8;
+ for (int n = 1; n <= buf[7]; n++) {
+ VERBOSE("Controls(%d): %02X\n", n , buf[pos]);
+ pos++;
+ }
+ VERBOSE("Processing: %d\n", buf[pos]);
+ pos++;
+ VERBOSE("VideoStanders: %02X\n", buf[pos]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(VC_EXTENSION_UNIT, subtype)
+ VERBOSE("UnitID: %d\n", buf[3]);
+ GUID* guid = reinterpret_cast<GUID*>(buf+4);
+ string s = guid->to_string();
+ VERBOSE("guidExtensionCode: %s\n", s.c_str());
+ VERBOSE("NumControls: %d\n", buf[20]);
+ int p = buf[21];
+ VERBOSE("NrInPins: %d\n", p);
+ int pos = 22;
+ for (int n = 1; n <= p; n++) {
+ VERBOSE("SourceID(%d): %d\n", n, buf[pos]);
+ pos++;
+ }
+ int bControlSize = buf[pos];
+ pos++;
+ VERBOSE("ControlSize: %d\n", bControlSize);
+ pos++;
+ for(int i = 0; i < bControlSize; i++) {
+ VERBOSE("Control(%d): %02X\n", i, buf[pos]);
+ pos++;
+ }
+ VERBOSE("iExtension: %d\n", buf[pos]);
+ ENDIF
+}
+
+#define VS_INPUT_HEADER 0x01
+#define VS_STILL_FRAME 0x03
+#define VS_FORMAT_UNCOMPRESSED 0x04
+#define VS_FRAME_UNCOMPRESSED 0x05
+#define VS_FORMAT_MJPEG 0x06
+#define VS_FRAME_MJPEG 0x07
+#define VS_COLOR_FORMAT 0x0d
+
+void UvcCfg::_parserVideoStream(uint8_t* buf, int len) {
+ int subtype = buf[2];
+ IF_EQ_THEN_PRINTF(VS_INPUT_HEADER, subtype)
+ VERBOSE("NumFormats: %d\n", buf[3]);
+ VERBOSE("TotalLength: %d\n", LE16(buf+4));
+ VERBOSE("EndpointAddress: %02X\n", buf[6]);
+ VERBOSE("Info: %02X\n", buf[7]);
+ VERBOSE("TerminalLink: %d\n", buf[8]);
+ VERBOSE("StillCaptureMethod: %d\n", buf[9]);
+ VERBOSE("TriggerSupport: %d\n", buf[10]);
+ VERBOSE("TriggerUsage: %d\n", buf[11]);
+ VERBOSE("ControlSize: %d\n", buf[12]);
+ int pos = 13;
+ for (int n = 1; n <= buf[12]; n++) {
+ VERBOSE("Controls(%d): %02X\n", n, buf[pos]);
+ pos++;
+ }
+ bEndpointAddress = buf[6];
+ ENDIF
+ IF_EQ_THEN_PRINTF(VS_STILL_FRAME, subtype)
+ VERBOSE("EndpointAdress: %02X\n", buf[3]);
+ VERBOSE("NumImageSizePatterns: %d\n", buf[4]);
+ int ptn = buf[4];
+ int pos = 5;
+ for (int n = 1; n <= ptn; n++) {
+ VERBOSE("Width(%d): %d\n", n, LE16(buf+pos));
+ VERBOSE("Height(%d): %d\n", n, LE16(buf+pos+2));
+ pos += 4;
+ }
+ VERBOSE("NumCompressPtn: %d\n", buf[pos]);
+ ptn = buf[pos++];
+ for (int n = 1; n <= ptn; n++) {
+ VERBOSE("Compress(%d): %d\n", n, buf[pos]);
+ pos++;
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(VS_FORMAT_UNCOMPRESSED, subtype)
+ VERBOSE("FormatIndex: %d\n", buf[3]);
+ VERBOSE("NumFrameDescriptors: %d\n", buf[4]);
+ GUID* guid = reinterpret_cast<GUID*>(buf+5);
+ string s = guid->to_string();
+ if (guid->isFormat("YUY2")) {
+ VERBOSE("GUID: %s YUY2\n", s.c_str());
+ } else if (guid->isFormat("NV12")) {
+ VERBOSE("GUID: %s NV12\n", s.c_str());
+ } else {
+ VERBOSE("GUID: %s\n", s.c_str());
+ }
+ VERBOSE("DefaultFrameIndex: %d\n", buf[22]);
+ if (_payload == UVC_YUY2) {
+ FormatIndex = buf[3];
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(VS_FRAME_UNCOMPRESSED, subtype)
+ VERBOSE("FrameIndex: %d\n", buf[3]);
+ VERBOSE("Capabilites: %d\n", buf[4]);
+ VERBOSE("Width: %d\n", LE16(buf+5));
+ VERBOSE("Height: %d\n", LE16(buf+7));
+ VERBOSE("MinBitRate: %d\n", LE32(buf+9));
+ VERBOSE("MaxBitRate: %d\n", LE32(buf+13));
+ VERBOSE("MaxVideoFrameBufferSize: %d\n", LE32(buf+17));
+ VERBOSE("DefaultFrameInterval: %d\n", LE32(buf+21));
+ VERBOSE("FrameIntervalType: %d\n", buf[25]);
+ int it = buf[25];
+ uint32_t max_fi = 333333; // 30.0fps
+ if (it == 0) {
+ VERBOSE("FrameMinInterval: %d\n", buf[26]);
+ VERBOSE("FrameMaxInterval: %d\n", buf[30]);
+ VERBOSE("FrameIntervalStep: %d\n", buf[34]);
+ } else {
+ int pos = 26;
+ for (int n = 1; n <= it; n++) {
+ uint32_t fi = LE32(buf+pos);
+ if (fi >= max_fi) {
+ max_fi = fi;
+ }
+ float fps = 1e+7 / fi;
+ VERBOSE("FrameInterval(%u): %d (%.1f fps)\n", n, fi, fps);
+ pos += 4;
+ }
+ }
+ if (_payload == UVC_YUY2) {
+ if (_width == LE16(buf+5) && _height == LE16(buf+7)) {
+ FrameIndex = buf[3];
+ }
+ if (dwFrameInterval == 0) {
+ dwFrameInterval = max_fi;
+ }
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(VS_FORMAT_MJPEG, subtype)
+ VERBOSE("FormatIndex: %d\n", buf[3]);
+ VERBOSE("NumFrameDescriptors: %d\n", buf[4]);
+ VERBOSE("Flags: %d\n", buf[5]);
+ VERBOSE("DefaultFrameIndex: %d\n", buf[6]);
+ if (_payload == UVC_MJPEG) {
+ FormatIndex = buf[3];
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(VS_FRAME_MJPEG, subtype)
+ VERBOSE("FrameIndex: %d\n", buf[3]);
+ VERBOSE("Capabilites: %d\n", buf[4]);
+ VERBOSE("Width: %d\n", LE16(buf+5));
+ VERBOSE("Height: %d\n", LE16(buf+7));
+ VERBOSE("MinBitRate: %d\n", LE32(buf+9));
+ VERBOSE("MaxBitRate: %d\n", LE32(buf+13));
+ VERBOSE("MaxVideoFrameBufferSize: %d\n", LE32(buf+17));
+ VERBOSE("DefaultFrameInterval: %d\n", LE32(buf+21));
+ VERBOSE("FrameIntervalType: %d\n", buf[25]);
+ int it = buf[25];
+ uint32_t max_fi = 333333; // 30.0fps
+ if (it == 0) {
+ VERBOSE("FrameMinInterval: %d\n", buf[26]);
+ VERBOSE("FrameMaxInterval: %d\n", buf[30]);
+ VERBOSE("FrameIntervalStep: %d\n", buf[34]);
+ } else {
+ int pos = 26;
+ for (int n = 1; n <= it; n++) {
+ uint32_t fi = LE32(buf+pos);
+ if (fi >= max_fi) {
+ max_fi = fi;
+ }
+ float fps = 1e+7 / fi;
+ VERBOSE("FrameInterval(%u): %d (%.1f fps)\n", n, fi, fps);
+ pos += 4;
+ }
+ }
+ if (_payload == UVC_MJPEG) {
+ if (_width == LE16(buf+5) && _height == LE16(buf+7)) {
+ FrameIndex = buf[3];
+ }
+ if (dwFrameInterval == 0) {
+ dwFrameInterval = max_fi;
+ }
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(VS_COLOR_FORMAT, subtype)
+ ENDIF
+}
+
+void UvcCfg::_parserConfigurationDescriptor(uint8_t* buf, int len) {
+ int pos = 0;
+ _IfClass = 0;
+ _IfSubClass = 0;
+ while (pos < len) {
+ int type = buf[pos+1];
+ //DBG_BYTES(TYPE_Str(type), buf+pos, buf[pos]);
+ IF_EQ_THEN_PRINTF(DESCRIPTOR_TYPE_CONFIGURATION, type)
+ VERBOSE("NumInterfaces: %d\n", buf[pos+4]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, type)
+ VERBOSE("FirstInterface: %d\n", buf[pos+2]);
+ VERBOSE("InterfaceCount: %d\n", buf[pos+3]);
+ VERBOSE("FunctionClass: %02X\n", buf[pos+4]);
+ VERBOSE("FunctionSubClass: %02X\n", buf[pos+5]);
+ VERBOSE("FunctionProtocol: %02X\n", buf[pos+6]);
+ VERBOSE("Function: %d\n", buf[pos+7]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(DESCRIPTOR_TYPE_INTERFACE,type)
+ VERBOSE("InterfaceNumber: %d\n", buf[pos+2]);
+ VERBOSE("AlternateSetting: %d\n", buf[pos+3]);
+ VERBOSE("NumEndpoint: %d\n", buf[pos+4]);
+ VERBOSE("InterfaceClass: %02X\n", buf[pos+5]);
+ VERBOSE("InterfaceSubClass: %02X\n", buf[pos+6]);
+ VERBOSE("InterfaceProtocol: %02X\n", buf[pos+7]);
+ VERBOSE("Interface: %d\n", buf[pos+8]);
+ _If = buf[pos+2];
+ _Ifalt = buf[pos+3];
+ _IfClass = buf[pos+5];
+ _IfSubClass = buf[pos+6];
+ ENDIF
+ IF_EQ_THEN_PRINTF(DESCRIPTOR_TYPE_ENDPOINT, type)
+ VERBOSE("EndpointAddress: %02X\n", buf[pos+2]);
+ VERBOSE("Attributes: %02X\n", buf[pos+3]);
+ VERBOSE("MaxPacketSize: %d\n", LE16(buf+pos+4));
+ VERBOSE("Interval: %d\n", buf[pos+6]);
+ if (_IfClass == CC_VIDEO && _IfSubClass == SC_VIDEOSTREAMING) {
+ if (bEndpointAddress == buf[pos+2]) {
+ if (wMaxPacketSize == 0) {
+ wMaxPacketSize = LE16(buf+pos+4);
+ }
+ if (wMaxPacketSize == LE16(buf+pos+4)) {
+ bInterface = _If;
+ bAlternate = _Ifalt;
+ }
+ }
+ }
+ ENDIF
+ IF_EQ_THEN_PRINTF(DESCRIPTOR_TYPE_CS_INTERFACE, type)
+ IF_EQ_THEN_PRINTF(CC_VIDEO, _IfClass)
+ IF_EQ_THEN_PRINTF(SC_VIDEOCONTROL, _IfSubClass)
+ _parserVideoControl(buf+pos, buf[pos]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(SC_VIDEOSTREAMING, _IfSubClass)
+ _parserVideoStream(buf+pos, buf[pos]);
+ ENDIF
+ ENDIF
+ IF_EQ_THEN_PRINTF(CC_AUDIO, _IfClass)
+ IF_EQ_THEN_PRINTF(SC_AUDIOCONTROL, _IfSubClass)
+ _parserAudioControl(buf+pos, buf[pos]);
+ ENDIF
+ IF_EQ_THEN_PRINTF(SC_AUDIOSTREAMING, _IfSubClass)
+ _parserAudioStream(buf+pos, buf[pos]);
+ ENDIF
+ ENDIF
+ ENDIF
+ IF_EQ_THEN_PRINTF(DESCRIPTOR_TYPE_HUB, type)
+ ENDIF
+ pos += buf[pos];
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UvcCam/UvcCam.h Tue Feb 19 15:50:06 2013 +0000
@@ -0,0 +1,58 @@
+// UvcCam.h 2013/2/11
+#ifndef UVC_CAM_H
+#define UVC_CAM_H
+
+#define UVC_160x120 2
+#define UVC_176x144 3
+#define UVC_320x176 4
+#define UVC_320x240 5
+#define UVC_352x288 6
+#define UVC_432x240 7
+#define UVC_640x480 1
+#define UVC_544x288 8
+#define UVC_640x360 9
+#define UVC_752x416 10
+#define UVC_800x448 11
+#define UVC_800x600 12
+
+#define UVC_MJPEG 2
+#define UVC_YUY2 1
+
+#define VERBOSE(...) do{printf(__VA_ARGS__);} while(0);
+
+class UvcCam : public BaseUvc {
+public:
+ UvcCam(int formatIndex = UVC_MJPEG, int frameIndex = UVC_160x120, uint32_t interval = _5FPS, ControlEp* ctlEp = NULL);
+ static bool check(ControlEp* ctlEp);
+ uint16_t vid;
+ uint16_t pid;
+};
+
+class UvcCfg {
+public:
+ UvcCfg(int formatIndex, int frameIndex, ControlEp* ctlEp);
+ uint8_t bEndpointAddress;
+ uint16_t wMaxPacketSize;
+ uint8_t FormatIndex;
+ uint8_t FrameIndex;
+ uint32_t dwFrameInterval;
+ uint8_t bInterface;
+ uint8_t bAlternate;
+ uint16_t bcdUVC;
+private:
+ void _parserAudioControl(uint8_t* buf, int len);
+ void _parserAudioStream(uint8_t* buf, int len);
+ void _parserVideoControl(uint8_t* buf, int len);
+ void _parserVideoStream(uint8_t* buf, int len);
+ void _parserConfigurationDescriptor(uint8_t* buf, int len);
+ uint16_t _width;
+ uint16_t _height;
+ uint8_t _payload;
+ int _If;
+ int _Ifalt;
+ int _IfClass;
+ int _IfSubClass;
+ ControlEp* _ctlEp;
+};
+
+#endif //UVC_CAM_H
