WinUSB can not read RAW data from Generic USB Composite Cap Card device

Hello,

I hope I don't get roasted too badly here by real programmers...

Anyhow, I am new to Windows drivers, kernels, and USB standards and am learning everything from scratch to the best of my abilities.

To remove the big question of "why" I am doing this crazy task, it's because I am designing a high-speed object detection system that uses YOLOv8 inference. I found out that there is great latency and overhead in the camera system.

I want to implement a custom driver for an HDMI-USB 3.0 capture card such that it directly loads the camera frames into a pinned-memory location in the system host RAM. Then, the GPU will directly grab those MJPEG frames, decode them, and perform inference on them. This is something in the nature of DMA (Direct Memory Access).

I know I'll need to do some USB header parsing, but right now, I am not able to get data when using WinUSB. When I use UVC drivers, I am able to receive camera data by DirectShow. However, DirectShow is not able to provide the fine-grain control I need.

The camera is connected to a generic HDMI-to-USB 3.0 capture card (I have 5 of them), all are identical, they have the same vendor, hardware, and serial numbers. The capture card is seen as a composite device with usbccgp.sys drivers loaded automatically by Windows.

Each generic HDMI-to-USB 3.0 capture card has two child nodes: one is for video, and the other is for audio and microphone.

I installed WinUSB driver into the child node of the video interface, I used Zadig to install it.

Here is the WinUSB .INF file:

; USB3.0_HD_Video_Capture_(Test_CAM).inf
; Copyright (c) 2010-2023 Pete Batard <pete@akeo.ie> (GNU LGPL)
[Strings]
DeviceName = "USB3.0 HD Video Capture (Test_CAM)"
VendorName = "(Undefined Vendor)"
SourceName = "USB3.0 HD Video Capture (Test_CAM) Install Disk"
DeviceID   = "VID_EBA4&PID_7588&MI_02"
DeviceGUID = "{99DA2DEC-C919-4F9D-9384-DAA7EBBD40EC}"

[Version]
Signature   = "$Windows NT$"
Class       = "USBDevice"
ClassGuid   = {88bae032-5a81-49f0-bc3d-a4ff138216d6}
Provider    = "libwdi"
CatalogFile = USB3.0_HD_Video_Capture_(Test_CAM).cat
DriverVer   = 06/02/2012, 6.1.7600.16385

[ClassInstall32]
Addreg = WinUSBDeviceClassReg

[WinUSBDeviceClassReg]
HKR,,,0,"Universal Serial Bus devices"
HKR,,Icon,,-20

[Manufacturer]
%VendorName% = libusbDevice_WinUSB,NTx86,NTamd64,NTarm64

[libusbDevice_WinUSB.NTx86]
%DeviceName% = USB_Install, USB\%DeviceID%

[libusbDevice_WinUSB.NTamd64]
%DeviceName% = USB_Install, USB\%DeviceID%

[libusbDevice_WinUSB.NTarm64]
%DeviceName% = USB_Install, USB\%DeviceID%

[USB_Install]
Include = winusb.inf
Needs   = WINUSB.NT

[USB_Install.Services]
Include    = winusb.inf
AddService = WinUSB,0x00000002,WinUSB_ServiceInstall

[WinUSB_ServiceInstall]
DisplayName   = "WinUSB - Kernel Driver 06/02/2012 6.1.7600.16385"
ServiceType   = 1
StartType     = 3
ErrorControl  = 1
ServiceBinary = %12%\WinUSB.sys

[USB_Install.Wdf]
KmdfService = WINUSB, WinUsb_Install

[WinUSB_Install]
KmdfLibraryVersion = 1.11

[USB_Install.HW]
AddReg = AddDeviceInterfaceGUID

;[NoDeviceInterfaceGUID]
; Avoids adding a DeviceInterfaceGUID for generic driver

[AddDeviceInterfaceGUID]
HKR,,DeviceInterfaceGUIDs,0x10000,%DeviceGUID%

[USB_Install.NTx86.CoInstallers]
AddReg    = CoInstallers_AddReg
CopyFiles = CoInstallers_CopyFiles

[USB_Install.NTamd64.CoInstallers]
AddReg    = CoInstallers_AddReg
CopyFiles = CoInstallers_CopyFiles

[USB_Install.NTarm64.CoInstallers]
;

[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01011.dll,WdfCoInstaller","WinUSBCoInstaller2.dll"

[CoInstallers_CopyFiles]
WinUSBCoInstaller2.dll
WdfCoInstaller01011.dll

[DestinationDirs]
CoInstallers_CopyFiles = 11

[SourceDisksNames]
1 = %SourceName%

[SourceDisksFiles.x86]
WinUSBCoInstaller2.dll = 1,x86
WdfCoInstaller01011.dll = 1,x86

[SourceDisksFiles.amd64]
WinUSBCoInstaller2.dll = 1,amd64
WdfCoInstaller01011.dll = 1,amd64

[SourceDisksFiles.arm64]
;

Here is the USB Descriptor obtained by usbview.exe:

[Port2]  :  USB Composite Device


Is Port User Connectable:         yes
Is Port Debug Capable:            no
Companion Port Number:            0
Companion Hub Symbolic Link Name: 
Protocols Supported:
 USB 1.1:                         yes
 USB 2.0:                         yes
 USB 3.0:                         no

Device Power State:               PowerDeviceD0

       ---===>Device Information<===---
English product name: "USB3.0 HD Video Capture"

ConnectionStatus:                  
Current Config Value:              0x01  -> Device Bus Speed: High (is not SuperSpeed or higher capable)
Device Address:                    0x01
Open Pipes:                           0
*!*ERROR:  No open pipes!

          ===>Device Descriptor<===
bLength:                           0x12
bDescriptorType:                   0x01
bcdUSB:                          0x0200
bDeviceClass:                      0xEF  -> This is a Multi-interface Function Code Device
bDeviceSubClass:                   0x02  -> This is the Common Class Sub Class
bDeviceProtocol:                   0x01  -> This is the Interface Association Descriptor protocol
bMaxPacketSize0:                   0x40 = (64) Bytes
idVendor:                        0xEBA4 = Vendor ID not listed with USB.org
idProduct:                       0x7588
bcdDevice:                       0x0328
iManufacturer:                     0x01
     English (United States)  "USB3.0 HD Audio Capture"
iProduct:                          0x02
     English (United States)  "USB3.0 HD Video Capture"
iSerialNumber:                     0x06
*!*ERROR:  No String Descriptor for index 6!
bNumConfigurations:                0x01

       ---===>Full Configuration Descriptor<===---

          ===>Configuration Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x02
wTotalLength:                    0x01B9  -> Validated
bNumInterfaces:                    0x04
bConfigurationValue:               0x01
iConfiguration:                    0x00
bmAttributes:                      0x80  -> Bus Powered
MaxPower:                          0x64 = 200 mA

          ===>IAD Descriptor<===
bLength:                           0x08
bDescriptorType:                   0x0B
bFirstInterface:                   0x00
bInterfaceCount:                   0x02
bFunctionClass:                    0x01  -> Audio Interface Class
bFunctionSubClass:                 0x00
*!*CAUTION:    This appears to be an invalid bFunctionSubClass
bFunctionProtocol:                 0x00
iFunction:                         0x04
     English (United States)  "USB3.0 HD Audio Capture"

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x00
bAlternateSetting:                 0x00
bNumEndpoints:                     0x00
bInterfaceClass:                   0x01  -> Audio Interface Class
bInterfaceSubClass:                0x01  -> Audio Control Interface SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x04
     English (United States)  "USB3.0 HD Audio Capture"

          ===>Audio Control Interface Header Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x01 (HEADER)
bcdADC:                          0x0100
wTotalLength:                    0x0029
bInCollection:                     0x01
baInterfaceNr[1]:                  0x01

          ===>Audio Control Input Terminal Descriptor<===
bLength:                           0x0C
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x02 (INPUT_TERMINAL)
bTerminalID:                       0x01
wTerminalType:                   0x0201 (Microphone)
bAssocTerminal:                    0x00
bNrChannels:                       0x02
wChannelConfig:                  0x0000
iChannelNames:                     0x00
iTerminal:                         0x00

          ===>Audio Control Feature Unit Descriptor<===
bLength:                           0x0B
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x06 (FEATURE_UNIT)
bUnitID:                           0x02
bSourceID:                         0x01
bControlSize:                      0x02
bmaControls[master]:               03 00 
bmaControls[channel 0]:            03 00 
iFeature:                          0x00

          ===>Audio Control Output Terminal Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x03 (OUTPUT_TERMINAL)
bTerminalID:                       0x03
wTerminalType:                   0x0101 (USB streaming)
bAssocTerminal:                    0x00
bSourceID:                         0x02
iTerminal:                         0x00

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x01
bAlternateSetting:                 0x00
bNumEndpoints:                     0x00
bInterfaceClass:                   0x01  -> Audio Interface Class
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x01
     English (United States)  "USB3.0 HD Audio Capture"

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x01
bAlternateSetting:                 0x01
bNumEndpoints:                     0x01
bInterfaceClass:                   0x01  -> Audio Interface Class
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Audio Streaming Class Specific Interface Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x01 (AS_GENERAL)
bTerminalLink:                     0x03
bDelay:                            0x01
wFormatTag:                      0x0001 (PCM)

          ===>Audio Streaming Format Type Descriptor<===
bLength:                           0x0B
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x02 (FORMAT_TYPE)
bFormatType:                       0x01 (FORMAT_TYPE_I)
bNrChannels:                       0x02
bSubframeSize:                     0x02
bBitResolution:                    0x10 (16)
bSamFreqType:                      0x01 (Discrete)
tSamFreq[1]:                   0x00BB80 (48000 Hz)

          ===>Endpoint Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x05
bEndpointAddress:                  0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                      0x01  -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Data Endpoint
wMaxPacketSize:                  0x01B0 = 1 transactions per microframe, 0x1B0 max bytes
wInterval:                       0x0004
bSyncAddress:                      0x00

          ===>Audio Streaming Class Specific Audio Data Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x25 (CS_ENDPOINT)
bDescriptorSubtype:                0x01 (EP_GENERAL)
bmAttributes:                      0x00
bLockDelayUnits:                   0x00 (Undefined)
wLockDelay:                      0x0001

          ===>IAD Descriptor<===
bLength:                           0x08
bDescriptorType:                   0x0B
bFirstInterface:                   0x02
bInterfaceCount:                   0x02
bFunctionClass:                    0x0E  -> Video Interface Class
bFunctionSubClass:                 0x03  -> Video Interface Collection
bFunctionProtocol:                 0x00  -> PC_PROTOCOL_UNDEFINED protocol
iFunction:                         0x05
*!*ERROR:  No String Descriptor for index 5!

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x02
bAlternateSetting:                 0x00
bNumEndpoints:                     0x00
bInterfaceClass:                   0x0E  -> Video Interface Class
bInterfaceSubClass:                0x01  -> Video Control Interface SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x05
*!*ERROR:  No String Descriptor for index 5!

          ===>Class-Specific Video Control Interface Header Descriptor<===
bLength:                           0x0D
bDescriptorType:                   0x24
bDescriptorSubtype:                0x01
bcdVDC:                          0x0100
wTotalLength:                    0x004F  -> Validated
dwClockFreq:                 0x017D7840 = (25000000) Hz
bInCollection:                     0x01
baInterfaceNr[1]:                  0x01
USB Video Class device: spec version 1.0

          ===>Video Control Input Terminal Descriptor<===
bLength:                           0x11
bDescriptorType:                   0x24
bDescriptorSubtype:                0x02
bTerminalID:                       0x01
wTerminalType:                   0x0201 = (ITT_CAMERA)
bAssocTerminal:                    0x00
iTerminal:                         0x00
===>Camera Input Terminal Data
wObjectiveFocalLengthMin:        0x0000
wObjectiveFocalLengthMax:        0x0000
wOcularFocalLength:              0x0000
bControlSize:                      0x02
bmControls : 0x00 0x00 
     D00 = 0   no -  Scanning Mode
     D01 = 0   no -  Auto-Exposure Mode
     D02 = 0   no -  Auto-Exposure Priority
     D03 = 0   no -  Exposure Time (Absolute)
     D04 = 0   no -  Exposure Time (Relative)
     D05 = 0   no -  Focus (Absolute)
     D06 = 0   no -  Focus (Relative)
     D07 = 0   no -  Iris (Absolute)
     D08 = 0   no -  Iris (Relative)
     D09 = 0   no -  Zoom (Absolute)
     D10 = 0   no -  Zoom (Relative)
     D11 = 0   no -  PanTilt (Absolute)
     D12 = 0   no -  PanTilt (Relative)
     D13 = 0   no -  Roll (Absolute)
     D14 = 0   no -  Roll (Relative)
     D15 = 0   no -  Reserved

          ===>Video Control Extension Unit Descriptor<===
bLength:                           0x1D
bDescriptorType:                   0x24
bDescriptorSubtype:                0x06
bUnitID:                           0x02
guidExtensionCode:                 {46394292-0CD0-4AE3-8783-3133F9EAAA3B}
bNumControls:                      0x0A
bNrInPins:                         0x01
===>List of Connected Units and Terminal ID's
baSourceID[1]:                     0x01
bControlSize:                      0x04
bmControls : 0xFF 0x03 0x00 0x00 
     D00 = 1  yes -  Vendor-Specific (Optional)
     D01 = 1  yes -  Vendor-Specific (Optional)
     D02 = 1  yes -  Vendor-Specific (Optional)
     D03 = 1  yes -  Vendor-Specific (Optional)
     D04 = 1  yes -  Vendor-Specific (Optional)
     D05 = 1  yes -  Vendor-Specific (Optional)
     D06 = 1  yes -  Vendor-Specific (Optional)
     D07 = 1  yes -  Vendor-Specific (Optional)
     D08 = 1  yes -  Vendor-Specific (Optional)
     D09 = 1  yes -  Vendor-Specific (Optional)
     D10 = 0   no -  Vendor-Specific (Optional)
     D11 = 0   no -  Vendor-Specific (Optional)
     D12 = 0   no -  Vendor-Specific (Optional)
     D13 = 0   no -  Vendor-Specific (Optional)
     D14 = 0   no -  Vendor-Specific (Optional)
     D15 = 0   no -  Vendor-Specific (Optional)
     D16 = 0   no -  Vendor-Specific (Optional)
     D17 = 0   no -  Vendor-Specific (Optional)
     D18 = 0   no -  Vendor-Specific (Optional)
     D19 = 0   no -  Vendor-Specific (Optional)
     D20 = 0   no -  Vendor-Specific (Optional)
     D21 = 0   no -  Vendor-Specific (Optional)
     D22 = 0   no -  Vendor-Specific (Optional)
     D23 = 0   no -  Vendor-Specific (Optional)
     D24 = 0   no -  Vendor-Specific (Optional)
     D25 = 0   no -  Vendor-Specific (Optional)
     D26 = 0   no -  Vendor-Specific (Optional)
     D27 = 0   no -  Vendor-Specific (Optional)
     D28 = 0   no -  Vendor-Specific (Optional)
     D29 = 0   no -  Vendor-Specific (Optional)
     D30 = 0   no -  Vendor-Specific (Optional)
     D31 = 0   no -  Vendor-Specific (Optional)
iExtension:                        0x00

          ===>Video Control Processing Unit Descriptor<===
bLength:                           0x0B
bDescriptorType:                   0x24
bDescriptorSubtype:                0x05
bUnitID:                           0x03
bSourceID:                         0x02
wMaxMultiplier:                  0x0000
bControlSize:                      0x02
bmControls : 0x00 0x00 
     D00 = 0   no -  Brightness
     D01 = 0   no -  Contrast
     D02 = 0   no -  Hue
     D03 = 0   no -  Saturation
     D04 = 0   no -  Sharpness
     D05 = 0   no -  Gamma
     D06 = 0   no -  White Balance Temperature
     D07 = 0   no -  White Balance Component
     D08 = 0   no -  Backlight Compensation
     D09 = 0   no -  Gain
     D10 = 0   no -  Power Line Frequency
     D11 = 0   no -  Hue, Auto
     D12 = 0   no -  White Balance Temperature, Auto
     D13 = 0   no -  White Balance Component, Auto
     D14 = 0   no -  Digital Multiplier
     D15 = 0   no -  Digital Multiplier Limit
iProcessing :                      0x00

          ===>Video Control Output Terminal Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x24
bDescriptorSubtype:                0x03
bTerminalID:                       0x04
wTerminalType:                   0x0101 = (TT_STREAMING)
bAssocTerminal:                    0x00
bSourceID:                         0x03
iTerminal:                         0x00

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x03
bAlternateSetting:                 0x00
bNumEndpoints:                     0x00
bInterfaceClass:                   0x0E  -> Video Interface Class
bInterfaceSubClass:                0x02  -> Video Streaming Interface SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Video Class-Specific VS Video Input Header Descriptor<===
bLength:                           0x0E
bDescriptorType:                   0x24
bDescriptorSubtype:                0x01
bNumFormats:                       0x01
wTotalLength:                    0x00C9  -> Validated
bEndpointAddress:                  0x82  -> Direction: IN - EndpointID: 2
bmInfo:                            0x00  -> Dynamic Format Change not Supported
bTerminalLink:                     0x04
bStillCaptureMethod:               0x01  -> Still Capture Method 1
bTriggerSupport:                   0x01  -> Hardware Triggering Support
bTriggerUsage:                     0x01  -> Host will notify client application of button event
bControlSize:                      0x01
Video Payload Format 1             0x00 
     D00 = 0   no -  Key Frame Rate
     D01 = 0   no -  P Frame Rate
     D02 = 0   no -  Compression Quality
     D03 = 0   no -  Compression Window Size
     D04 = 0   no -  Generate Key Frame
     D05 = 0   no -  Update Frame Segment
     D06 = 0   no -  Reserved
     D07 = 0   no -  Reserved

          ===>Video Streaming MJPEG Format Type Descriptor<===
bLength:                           0x0B
bDescriptorType:                   0x24
bDescriptorSubtype:                0x06
bFormatIndex:                      0x01
bNumFrameDescriptors:              0x05
bmFlags:                           0x01  -> Sample Size is Fixed
bDefaultFrameIndex:                0x01
bAspectRatioX:                     0x00
bAspectRatioY:                     0x00
bmInterlaceFlags:                  0x02
     D00   = 0  non-Interlaced stream or variable
     D01   = 1  1 field per frame
     D02   = 0  Field 1 not first
     D03   = 0  Reserved
     D4..5 = 0  Field patterns  -> Field 1 only
     D6..7 = 0  Display Mode  -> Bob only
bCopyProtect:                      0x00  -> Duplication Unrestricted

          ===>Video Streaming MJPEG Frame Type Descriptor<===
          --->This is the Default (optimum) Frame index
bLength:                           0x22
bDescriptorType:                   0x24
bDescriptorSubtype:                0x07
bFrameIndex:                       0x01
bmCapabilities:                    0x02
wWidth:                          0x0780 = 1920
wHeight:                         0x0438 = 1080
dwMinBitRate:                0x201DEC00
dwMaxBitRate:                0x201DEC00
dwMaxVideoFrameBufferSize:   0x001FA400
dwDefaultFrameInterval:      0x00028B0A = 16.666600 mSec (60.00 Hz)
bFrameIntervalType:                0x02
===>Additional Discrete Frame TypeData
dwFrameInterval[1]:          0x00028B0A = 16.666600 mSec (60.00 Hz)
dwFrameInterval[2]:          0x00051615 = 33.333300 mSec (30.00 Hz)

          ===>Video Streaming MJPEG Frame Type Descriptor<===
bLength:                           0x22
bDescriptorType:                   0x24
bDescriptorSubtype:                0x07
bFrameIndex:                       0x02
bmCapabilities:                    0x02
wWidth:                          0x0500 = 1280
wHeight:                         0x02D0 = 720
dwMinBitRate:                0x201DEC00
dwMaxBitRate:                0x201DEC00
dwMaxVideoFrameBufferSize:   0x000E1000
dwDefaultFrameInterval:      0x00028B0A = 16.666600 mSec (60.00 Hz)
bFrameIntervalType:                0x02
===>Additional Discrete Frame TypeData
dwFrameInterval[1]:          0x00028B0A = 16.666600 mSec (60.00 Hz)
dwFrameInterval[2]:          0x00051615 = 33.333300 mSec (30.00 Hz)

          ===>Video Streaming MJPEG Frame Type Descriptor<===
bLength:                           0x22
bDescriptorType:                   0x24
bDescriptorSubtype:                0x07
bFrameIndex:                       0x03
bmCapabilities:                    0x02
wWidth:                          0x03C0 = 960
wHeight:                         0x021C = 540
dwMinBitRate:                0x201DEC00
dwMaxBitRate:                0x201DEC00
dwMaxVideoFrameBufferSize:   0x0007E900
dwDefaultFrameInterval:      0x00028B0A = 16.666600 mSec (60.00 Hz)
bFrameIntervalType:                0x02
===>Additional Discrete Frame TypeData
dwFrameInterval[1]:          0x00028B0A = 16.666600 mSec (60.00 Hz)
dwFrameInterval[2]:          0x00051615 = 33.333300 mSec (30.00 Hz)

          ===>Video Streaming MJPEG Frame Type Descriptor<===
bLength:                           0x22
bDescriptorType:                   0x24
bDescriptorSubtype:                0x07
bFrameIndex:                       0x04
bmCapabilities:                    0x02
wWidth:                          0x0320 = 800
wHeight:                         0x01C2 = 450
dwMinBitRate:                0x201DEC00
dwMaxBitRate:                0x201DEC00
dwMaxVideoFrameBufferSize:   0x00057E40
dwDefaultFrameInterval:      0x00028B0A = 16.666600 mSec (60.00 Hz)
bFrameIntervalType:                0x02
===>Additional Discrete Frame TypeData
dwFrameInterval[1]:          0x00028B0A = 16.666600 mSec (60.00 Hz)
dwFrameInterval[2]:          0x00051615 = 33.333300 mSec (30.00 Hz)

          ===>Video Streaming MJPEG Frame Type Descriptor<===
bLength:                           0x22
bDescriptorType:                   0x24
bDescriptorSubtype:                0x07
bFrameIndex:                       0x05
bmCapabilities:                    0x02
wWidth:                          0x0280 = 640
wHeight:                         0x0168 = 360
dwMinBitRate:                0x201DEC00
dwMaxBitRate:                0x201DEC00
dwMaxVideoFrameBufferSize:   0x00038400
dwDefaultFrameInterval:      0x00028B0A = 16.666600 mSec (60.00 Hz)
bFrameIntervalType:                0x02
===>Additional Discrete Frame TypeData
dwFrameInterval[1]:          0x00028B0A = 16.666600 mSec (60.00 Hz)
dwFrameInterval[2]:          0x00051615 = 33.333300 mSec (30.00 Hz)

          ===>Color Matching Descriptor<===
bLength:                           0x06
bDescriptorType:                   0x24
bDescriptorSubtype:                0x0D
bColorPrimaries:                   0x01
bTransferCharacteristics:          0x01
bMatrixCoefficients:               0x04

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x03
bAlternateSetting:                 0x01
bNumEndpoints:                     0x01
bInterfaceClass:                   0x0E  -> Video Interface Class
bInterfaceSubClass:                0x02  -> Video Streaming Interface SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x82  -> Direction: IN - EndpointID: 2
bmAttributes:                      0x01  -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Data Endpoint
wMaxPacketSize:                  0x1400 = 3 transactions per microframe, 0x400 max bytes
bInterval:                         0x01

          ===>Additional Error Checking<===
PASS: number of MJPEG frame descriptors (5) == number of frame descriptors (5) specified in MJPEG format descriptor(s)

Here is the issue: I used the USB Visual Studio template and modified it to access the video interface, but the WinUSB C++ API is unable to access the IN-endpoint.

It can never find the alternate settings that lead to the video interface IN-endpoint. It can only find the first alternate video settings at index 0, which shows it has no endpoints. I tried everything I could, but I am not able to make WinUSB access the alternate settings index of the video interface, which contains the endpoint information.

I am unable to provide complete C++ code since the forum says I have reached max character count for the post.

I tried different index values and it only finds index values at 0:

    UCHAR buffer[1024];
if (!QueryDeviceEndpointsDirect(deviceData.WinusbHandle, &deviceData.IsochronousInPipe, /*desiredAltSetting*/ 0, /*endpointIndex*/ 0)) {
    wprintf(L"Failed to query device endpoints\n");
    CloseDevice(&deviceData);
    return 0;
}

What am I doing wrong?

Also, I just found out that WinUSB is not able to provide fine control to allocate memory to a specific location, such as a pinned memory location, and dump camera frames there. However, I just want WinUSB to work for what it was made for, just for the sake of it, since I spent a whole week on it.

I hope some guidance can be given.

Thanks.

Here is the C++ code if needed:

#include <windows.h>
#include <winusb.h>
#include <cfgmgr32.h>
#include <tchar.h>
#include <strsafe.h>
#include <stdio.h>

// Define the correct device interface GUID
DEFINE_GUID(GUID_DEVINTERFACE_WinUSBApplication,
    0x99DA2DEC, 0xC919, 0x4F9D, 0x93, 0x84, 0xDA, 0xA7, 0xEB, 0xBD, 0x40, 0xEC);

typedef struct _DEVICE_DATA {
    WINUSB_INTERFACE_HANDLE WinusbHandle;
    HANDLE                  DeviceHandle;
    TCHAR                   DevicePath[MAX_PATH];
    UCHAR                   IsochronousInPipe;
} DEVICE_DATA, *PDEVICE_DATA;

HRESULT OpenDevice(PDEVICE_DATA DeviceData, PBOOL FailureDeviceNotFound);
VOID CloseDevice(PDEVICE_DATA DeviceData);
BOOL ReadFromIsochronousEndpoint(PDEVICE_DATA DeviceData, UCHAR* buffer, ULONG bufferLength);
HRESULT RetrieveDevicePath(LPTSTR DevicePath, ULONG BufLen, PBOOL FailureDeviceNotFound);
BOOL QueryDeviceEndpoints(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* IsochronousInPipe);

HRESULT OpenDevice(PDEVICE_DATA DeviceData, PBOOL FailureDeviceNotFound)
{
    HRESULT hr = RetrieveDevicePath(DeviceData->DevicePath, sizeof(DeviceData->DevicePath), FailureDeviceNotFound);
    if (FAILED(hr)) {
        _tprintf(_T("Failed to retrieve device path. HRESULT: 0x%x\n"), hr);
        return hr;
    }

    _tprintf(_T("Device Path: %s\n"), DeviceData->DevicePath);

    DeviceData->DeviceHandle = CreateFile(DeviceData->DevicePath, GENERIC_WRITE | GENERIC_READ,
        FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);

    if (INVALID_HANDLE_VALUE == DeviceData->DeviceHandle) {
        _tprintf(_T("Failed to open device. Error: %d\n"), GetLastError());
        return HRESULT_FROM_WIN32(GetLastError());
    }

    if (!WinUsb_Initialize(DeviceData->DeviceHandle, &DeviceData->WinusbHandle)) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        CloseHandle(DeviceData->DeviceHandle);
        _tprintf(_T("WinUsb_Initialize failed. Error: %d\n"), GetLastError());
        return hr;
    }

    return S_OK;
}

VOID CloseDevice(PDEVICE_DATA DeviceData)
{
    if (DeviceData->WinusbHandle) {
        WinUsb_Free(DeviceData->WinusbHandle);
        DeviceData->WinusbHandle = NULL;
    }
    if (DeviceData->DeviceHandle) {
        CloseHandle(DeviceData->DeviceHandle);
        DeviceData->DeviceHandle = NULL;
    }
}

BOOL ReadFromIsochronousEndpoint(PDEVICE_DATA DeviceData, UCHAR* buffer, ULONG bufferLength)
{
    ULONG frameNumber = 0;
    ULONG numberOfPackets = 128; // Adjust size if needed
    USBD_ISO_PACKET_DESCRIPTOR isoPacketDescriptors[128] = { 0 }; // Adjust size if needed
    OVERLAPPED overlapped = { 0 };
    overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (!overlapped.hEvent) {
        printf("Failed to create event for overlapped I/O.\n");
        return FALSE;
    }

    WINUSB_ISOCH_BUFFER_HANDLE isochBuffer;
    BOOL result = WinUsb_RegisterIsochBuffer(DeviceData->WinusbHandle, DeviceData->IsochronousInPipe,
                                             buffer, bufferLength, &isochBuffer);
    if (!result) {
        printf("Error registering isochronous buffer: %d.\n", GetLastError());
        CloseHandle(overlapped.hEvent);
        return FALSE;
    }

    result = WinUsb_ReadIsochPipe(isochBuffer, 0, bufferLength, &frameNumber,
                                  numberOfPackets, isoPacketDescriptors, &overlapped);
    if (!result && GetLastError() != ERROR_IO_PENDING) {
        printf("Error reading from isochronous endpoint: %d.\n", GetLastError());
        WinUsb_UnregisterIsochBuffer(isochBuffer);
        CloseHandle(overlapped.hEvent);
        return FALSE;
    }

    // Wait for the read operation to complete
    result = GetOverlappedResult(DeviceData->DeviceHandle, &overlapped, &frameNumber, TRUE);
    if (!result) {
        printf("Error in overlapped result: %d.\n", GetLastError());
        WinUsb_UnregisterIsochBuffer(isochBuffer);
        CloseHandle(overlapped.hEvent);
        return FALSE;
    }

    WinUsb_UnregisterIsochBuffer(isochBuffer);
    CloseHandle(overlapped.hEvent);

    // Print the data read from the isochronous endpoint
    for (ULONG i = 0; i < frameNumber; i++) {
        printf("Packet %lu: Status = %d, Length = %lu\n", i, isoPacketDescriptors[i].Status, isoPacketDescriptors[i].Length);
    }

    return TRUE;
}

HRESULT RetrieveDevicePath(LPTSTR DevicePath, ULONG BufLen, PBOOL FailureDeviceNotFound)
{
    if (FailureDeviceNotFound) *FailureDeviceNotFound = FALSE;

    ULONG length = 0;
    CONFIGRET cr = CM_Get_Device_Interface_List_Size(&length, (LPGUID)&GUID_DEVINTERFACE_WinUSBApplication,
                                                     NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    if (cr != CR_SUCCESS) return HRESULT_FROM_WIN32(CM_MapCrToWin32Err(cr, ERROR_INVALID_DATA));

    PTSTR DeviceInterfaceList = (PTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, length * sizeof(TCHAR));
    if (!DeviceInterfaceList) return E_OUTOFMEMORY;

    cr = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_WinUSBApplication, NULL, DeviceInterfaceList,
                                      length, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    if (cr != CR_SUCCESS) {
        HeapFree(GetProcessHeap(), 0, DeviceInterfaceList);
        return HRESULT_FROM_WIN32(CM_MapCrToWin32Err(cr, ERROR_INVALID_DATA));
    }

    if (*DeviceInterfaceList == TEXT('\0')) {
        if (FailureDeviceNotFound) *FailureDeviceNotFound = TRUE;
        HeapFree(GetProcessHeap(), 0, DeviceInterfaceList);
        return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    }

    HRESULT hr = StringCbCopy(DevicePath, BufLen, DeviceInterfaceList);
    HeapFree(GetProcessHeap(), 0, DeviceInterfaceList);
    return hr;
}


BOOL QueryDeviceEndpointsDirect(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* IsochronousInPipe, UCHAR desiredAltSetting, UCHAR endpointIndex)
{
    USB_INTERFACE_DESCRIPTOR InterfaceDescriptor;

    // Set the specified alternate setting
    if (!WinUsb_SetCurrentAlternateSetting(hDeviceHandle, desiredAltSetting)) {
        printf("Failed to set alternate setting %d: %d\n", desiredAltSetting, GetLastError());
        return FALSE;
    }

    // Query the interface descriptor for the specified alternate setting
    if (!WinUsb_QueryInterfaceSettings(hDeviceHandle, desiredAltSetting, &InterfaceDescriptor)) {
        printf("Failed to query interface settings for alternate setting %d: %d\n", desiredAltSetting, GetLastError());
        return FALSE;
    }

    printf("Alternate Setting %d:\n", desiredAltSetting);
    printf("  Interface Number: %d\n", InterfaceDescriptor.bInterfaceNumber);
    printf("  Number of Endpoints: %d\n", InterfaceDescriptor.bNumEndpoints);
    printf("  Interface Class: %02X\n", InterfaceDescriptor.bInterfaceClass);
    printf("  Interface SubClass: %02X\n", InterfaceDescriptor.bInterfaceSubClass);
    printf("  Interface Protocol: %02X\n", InterfaceDescriptor.bInterfaceProtocol);

    WINUSB_PIPE_INFORMATION Pipe;

    // Query the pipe for the known endpoint index
    if (!WinUsb_QueryPipe(hDeviceHandle, desiredAltSetting, endpointIndex, &Pipe)) {
        printf("    Failed to query pipe %d: %d\n", endpointIndex, GetLastError());
        return FALSE;
    }

    printf("    Endpoint %d: PipeType = %d, PipeId = %d\n", endpointIndex, Pipe.PipeType, Pipe.PipeId);

    // Ensure the endpoint is Isochronous and IN direction
    if (Pipe.PipeType == UsbdPipeTypeIsochronous && USB_ENDPOINT_DIRECTION_IN(Pipe.PipeId)) {
        *IsochronousInPipe = Pipe.PipeId;
        return TRUE;
    }

    printf("No suitable isochronous IN endpoint found.\n");
    return FALSE;
}





int _tmain()
{
    DEVICE_DATA deviceData = {};
    BOOL noDevice = FALSE;

    HRESULT hr = OpenDevice(&deviceData, &noDevice);
    if (FAILED(hr)) {
        if (noDevice) wprintf(L"Device not connected or driver not installed\n");
        else wprintf(L"Failed to open device. HRESULT: 0x%x\n", hr);
        return 0;
    }

    UCHAR buffer[1024];
if (!QueryDeviceEndpointsDirect(deviceData.WinusbHandle, &deviceData.IsochronousInPipe, /*desiredAltSetting*/ 1, /*endpointIndex*/ 1)) {
    wprintf(L"Failed to query device endpoints\n");
    CloseDevice(&deviceData);
    return 0;
}

    if (ReadFromIsochronousEndpoint(&deviceData, buffer, sizeof(buffer))) {
        printf("Data successfully read from isochronous endpoint\n");
    }

    CloseDevice(&deviceData);
    return 0;
}

Alright so I seem to have been able to now get the endpoint queries, however I am not able to create isochronous endpoints:

I get the following error:

F:\AI_Componets\WinUSB\main_test>main.exe

Endpoint Descriptor:
bLength: 7
bDescriptorType: 5
bEndpointAddress: 130
bmAttributes: 1
wMaxPacketSize: 5120
bInterval: 1

Error reading from isochronous endpoint: 87

Here is the C++ code:

#include <windows.h>
#include <winusb.h>
#include <setupapi.h>
#include <usb.h>
#include <iostream>

#define BUFFER_SIZE 9000

void PrintEndpointDescriptor(PUSB_ENDPOINT_DESCRIPTOR endpointDesc) {
    std::cout << "Endpoint Descriptor:" << std::endl;
    std::cout << "bLength: " << (int)endpointDesc->bLength << std::endl;
    std::cout << "bDescriptorType: " << (int)endpointDesc->bDescriptorType << std::endl;
    std::cout << "bEndpointAddress: " << (int)endpointDesc->bEndpointAddress << std::endl;
    std::cout << "bmAttributes: " << (int)endpointDesc->bmAttributes << std::endl;
    std::cout << "wMaxPacketSize: " << endpointDesc->wMaxPacketSize << std::endl;
    std::cout << "bInterval: " << (int)endpointDesc->bInterval << std::endl;
}

void ParseDescriptors(UCHAR* buffer, ULONG length) {
    UCHAR* ptr = buffer;
    UCHAR* end = buffer + length;

    while (ptr < end) {
        PUSB_COMMON_DESCRIPTOR commonDesc = (PUSB_COMMON_DESCRIPTOR)ptr;

        if (commonDesc->bLength < 2) {
            std::cerr << "Invalid descriptor length" << std::endl;
            break;
        }

        switch (commonDesc->bDescriptorType) {
            case USB_ENDPOINT_DESCRIPTOR_TYPE:
                PrintEndpointDescriptor((PUSB_ENDPOINT_DESCRIPTOR)ptr);
                break;
            default:
                // std::cout << "Unknown Descriptor Type: " << (int)commonDesc->bDescriptorType << std::endl;
                break;
        }

        ptr += commonDesc->bLength;
    }
}

int main() {
    // Replace this with the actual device path of your USB device
    const wchar_t* devicePath = L"\\\\.\\USB#VID_EBA4&PID_7588&MI_02#6&63DE0D9&0&0002#{99DA2DEC-C919-4F9D-9384-DAA7EBBD40EC}";

    HANDLE hDevice = CreateFileW(
        devicePath,
        GENERIC_WRITE | GENERIC_READ,
        FILE_SHARE_WRITE | FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL);

    if (hDevice == INVALID_HANDLE_VALUE) {
        std::cerr << "Error opening device: " << GetLastError() << std::endl;
        return 1;
    }

    WINUSB_INTERFACE_HANDLE hWinUSB;
    if (!WinUsb_Initialize(hDevice, &hWinUSB)) {
        std::cerr << "Error initializing WinUSB: " << GetLastError() << std::endl;
        CloseHandle(hDevice);
        return 1;
    }

    // Get Device Descriptor
    UCHAR buffer[BUFFER_SIZE];
    ULONG lengthTransferred;
    USB_DEVICE_DESCRIPTOR deviceDesc;
    if (WinUsb_GetDescriptor(hWinUSB, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, buffer, sizeof(buffer), &lengthTransferred)) {
        memcpy(&deviceDesc, buffer, sizeof(USB_DEVICE_DESCRIPTOR));
        // PrintDeviceDescriptor(&deviceDesc);
    } else {
        std::cerr << "Error getting device descriptor: " << GetLastError() << std::endl;
    }

    // Get Configuration Descriptor
    USB_CONFIGURATION_DESCRIPTOR configDesc;
    if (WinUsb_GetDescriptor(hWinUSB, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, buffer, sizeof(buffer), &lengthTransferred)) {
        memcpy(&configDesc, buffer, sizeof(USB_CONFIGURATION_DESCRIPTOR));
        // PrintConfigurationDescriptor(&configDesc);
    } else {
        std::cerr << "Error getting configuration descriptor: " << GetLastError() << std::endl;
    }

    // Dynamically allocate buffer for the full configuration descriptor
    UCHAR* configBuffer = new UCHAR[configDesc.wTotalLength];
    if (WinUsb_GetDescriptor(hWinUSB, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, configBuffer, configDesc.wTotalLength, &lengthTransferred)) {
        ParseDescriptors(configBuffer, configDesc.wTotalLength);
    } else {
        std::cerr << "Error getting interface descriptors: " << GetLastError() << std::endl;
    }

    // Free dynamically allocated buffer
    delete[] configBuffer;

    // Register the isochronous buffer
    UCHAR pipeID = 0x82; // Replace with the actual pipe ID (endpoint address & 0x0F)
    UCHAR isochBuffer[BUFFER_SIZE];
    WINUSB_ISOCH_BUFFER_HANDLE isochBufferHandle;

    if (!WinUsb_RegisterIsochBuffer(hWinUSB, pipeID, isochBuffer, BUFFER_SIZE, &isochBufferHandle)) {
        std::cerr << "Error registering isochronous buffer: " << GetLastError() << std::endl;
        WinUsb_Free(hWinUSB);
        CloseHandle(hDevice);
        return 1;
    }

    // Read data from the isochronous endpoint
    ULONG offset = 0;
    ULONG length = BUFFER_SIZE;
    ULONG frameNumber = 0; // Starting frame number
    BOOL continueStream = TRUE;
    ULONG numberOfPackets = 1; // Adjust based on your requirements
    USBD_ISO_PACKET_DESCRIPTOR* isoPacketDescriptors = new USBD_ISO_PACKET_DESCRIPTOR[numberOfPackets];
    OVERLAPPED overlapped = {0};

    // Initialize the isoPacketDescriptors array
    for (ULONG i = 0; i < numberOfPackets; i++) {
        isoPacketDescriptors[i].Offset = i * (BUFFER_SIZE / numberOfPackets);
        isoPacketDescriptors[i].Length = BUFFER_SIZE / numberOfPackets;
        isoPacketDescriptors[i].Status = 0;
    }

    if (WinUsb_ReadIsochPipe(isochBufferHandle, offset, length, &frameNumber, numberOfPackets, isoPacketDescriptors, &overlapped)) {
        std::cout << "Read request submitted successfully" << std::endl;
        // Wait for the read operation to complete
        DWORD bytesTransferred;
        if (GetOverlappedResult(hDevice, &overlapped, &bytesTransferred, TRUE)) {
            std::cout << "Read " << bytesTransferred << " bytes from the isochronous endpoint" << std::endl;
            // Process the data in isochBuffer
            for (ULONG i = 0; i < numberOfPackets; i++) {
                std::cout << "Packet " << i << " Status: " << isoPacketDescriptors[i].Status << std::endl;
                std::cout << "Packet " << i << " Length: " << isoPacketDescriptors[i].Length << std::endl;
            }
        } else {
            std::cerr << "Error waiting for read operation: " << GetLastError() << std::endl;
        }
    } else {
        std::cerr << "Error reading from isochronous endpoint: " << GetLastError() << std::endl;
    }

    // Free dynamically allocated isoPacketDescriptors array
    delete[] isoPacketDescriptors;

    // Unregister the isochronous buffer
    WinUsb_UnregisterIsochBuffer(isochBufferHandle);

    // Clean up
    WinUsb_Free(hWinUSB);
    CloseHandle(hDevice);

    return 0;
}

So the error is:
Error reading from isochronous endpoint: 87

Which means the error code 87 corresponds to ERROR_INVALID_PARAMETER, in the context of WinUsb_ReadIsochPipeAsap function.

Hope someone can point some issues with the supplied parameters for WinUsb_ReadIsochPipeAsap I'm doing wrong here.

Thanks.

It is nothing but folly for you to try to replace the WIndows UVC driver. You cannot do a better job. Full stop.

The USB Host Controller does DMA for ALL USB devices. That is the fundamental operating model for USB. USB requests all flow into the host controller driver, which sets up a transfer schedule that gets passed to the hardware. The hardware executes those transfers by DMAing directly to the request memory.

Tell me what you need to do that you can't do with DirectShow, because to be honest, I don't believe you. I've done USB video devices for more than 25 years.

BTW, the string descriptor says "USB3.0 HD Video Capture", but it's not USB 3.0 -- it is a USB 2 device, running high speed. Because of that, when the wMaxPacketSize says 0x1400, that doesn't mean "5120 bytes", as you have. There are bit fields in there. It means 3 transactions of 1024 bytes per microframe, so the packet size is actually 3072 bytes. That is the maximum for a USB 2 isochronous pipe -- 24MB/s.

1 Like

Hello @Tim_Roberts,

Thank you for your response; I am much obliged.

The USB Host Controller does DMA for all USB devices. That is the fundamental operating model for USB. USB requests all flow into the host controller driver, which sets up a transfer schedule that gets passed to the hardware. The hardware executes those transfers by DMAing directly to the request memory.

So I assume this means I won't be able to tell the program to directly dump the camera frames into a specific pinned-memory location. Thus, I need to increase latency by telling the CPU to copy the camera frames from where they were dumped by DMA in one region of system RAM, only to be dumped again in another region in system RAM (this time the pinned memory region) so that the GPU can have access to it?

Tell me what you need to do that you can't do with DirectShow, because to be honest, I don't believe you. I've done USB video devices for more than 25 years.

Here is what I would like to do: directly load camera data straight into the beefy Nvidia high-performance GPU. Literally. Basically, bypass the CPU and want to offload as much as 99% of CPU usage to the GPU.

For now, I think the CPU might be useful and efficient only for one task: 1% of CPU resources will be used for parsing USB packet headers. Until I or someone else spends time to create extremely parallel CUDA GPU algorithms to parse USB packet headers in the GPU rather than in the CPU.

I have planned a logic flow which seems to be the most practical since my GPU does not support DMAing to its own GPU memory (USB device --> GPU directly). I have no choice but to rely on using system RAM for DMA (USB device --> System RAM --> GPU):

Step 1: I need to somehow directly get my hands on the full control of the raw data fresh out from the HDMI-USB cap card.

Step 2: I then need to assign these fresh raw data to be placed straight into a pinned memory location which was created by Nvidia's CUDA function.

Step 3: I then have the beefy Nvidia GPU perform its MJPEG decoding.

Step 4: I then have the beefy Nvidia GPU perform YOLOv8 inference (this is a whole chapter on its own, but I have already figured out these easy parts).

Right now, I'm using FFMPEG with DirectShow. It is filled with bugs. Such as buffer overflow happens if I use 30FPS input instead of the native 60FPS. (You may ask, why do I need to use 30FPS input instead of 60FPS native input? It's because that 30 extra FPS in a 60FPS input is not required for some applications. Thus, why have the hardware unnecessarily waste useful time, money, battery, and computation for 60FPS when 30FPS is more than enough and sufficient?)

I assume that DirectShow won't be able to give me the ability to directly place the camera frames (directly from the USB device before it ever reaches system RAM) into a pinned memory location so that the GPU can perform tons of computation for it.

I assume that DirectShow will do its own in-house work, such as grabbing camera raw frames. This alone will have a lot of CPU involvement. Who knows how many times the camera frames (directly out from the USB device) have been copied and transferred to how many different memory regions in system RAM until it finally got into the hands of DirectShow.

This is the issue: it is latency and overhead, raw camera frames getting into the hands of DirectShow. Every single millisecond matters. If I can improve latency by only 1 millisecond, that would also give me a high increase in FPS, useful when using high frame rate cameras.

But it doesn't stop there. I now need to tell DirectShow to dump these raw camera frames into the GPU because I do not want the CPU to perform MJPEG decoding. It is terrible at it and slow when compared to GPU MJPEG decoding. Huffman decoding is commonly done in the CPU. However, in the year 2025, it is finally possible to do Huffman decoding in Nvidia GPUs. I can get as much as 1000 to 500 FPS decoding speeds. There is no way to do Huffman decoding by the GPU via FFMPEG or DirectShow. That is another issue.

Since you do not believe me, here is some proof for the GPU doing 100% JPEG decoding; it's really impressive: 117.31 ms for 852 FPS, which is ~7 frames per millisecond:

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.8.ppm -o wild.jpg -repeat 100 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.09 GB
PCI-Express bandwidth test (host to device): 4620 MByte/s

Surface format: RGB8
Sampling format: 444
JPEG quality: 75%
Restart interval: 10
Requested GPU memory space: 56.77 MB

Input image: .\Images\wild.8.ppm
Image size: 1920x1080 pixels, 8 bits

Input sampling format: 444

Input file size: 6075.04 KB
Output file size: -0.00 KB
Compression ratio: -6220840.00

Processing time on GPU for 100 images excluding host-to-device transfer = 117.31 ms; 5057 MB/s;  852 FPS

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG encoder (Multi thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.8.ppm -o wild.mt.jpg -repeat 200 -thread 2 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.09 GB
PCI-Express bandwidth test (host to device): 4910 MByte/s

Surface format: RGB8
Sampling format: 444
JPEG quality: 75%
Restart interval: 10
Requested GPU memory space: 113.54 MB
Total encode time including all transfers for 200 images per 2 threads = 328.8 ms; 608.2 FPS;

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG encoder (Single thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.4k.8.ppm -o wild.4k.jpg -repeat 100 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.09 GB
PCI-Express bandwidth test (host to device): 3977 MByte/s

Surface format: RGB8
Sampling format: 444
JPEG quality: 75%
Restart interval: 10
Requested GPU memory space: 227.08 MB

Input image: .\Images\wild.4k.8.ppm
Image size: 3840x2160 pixels, 8 bits

Input sampling format: 444

Input file size: 24300.04 KB
Output file size: -0.00 KB
Compression ratio: -24883240.00

Processing time on GPU for 100 images excluding host-to-device transfer = 174.76 ms; 13579 MB/s;  572 FPS

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG encoder (Multi thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.4k.8.ppm -o wild.4k.mt.jpg -repeat 200 -thread 2 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.07 GB
PCI-Express bandwidth test (host to device): 5040 MByte/s

Surface format: RGB8
Sampling format: 444
JPEG quality: 75%
Restart interval: 10
Requested GPU memory size: 454.15 MB
Total encode time including all transfers for 200 images per 2 threads = 1031.4 ms; 193.9 FPS;

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Single thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.2k.ri1.jpg -o wild.2k.ppm -repeat 100 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.07 GB
PCI-Express bandwidth test (host to device): 4493 MByte/s

Requested GPU memory space: 62.82 MB

Input image: .\Images\wild.2k.ri1.jpg
Input image size: 1920x1080 pixels
Input sampling format: 444
Input restart interval: 1

Process time for 100 images without HDD I/O and excluding device-to-host transfer = 155.38 ms; 644 FPS

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Multi thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.2k.ri1.jpg -o wild.2k.mt.ppm -repeat 200 -thread 2 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.07 GB
PCI-Express bandwidth test (host to device): 4094 MByte/s

Requested GPU memory size: 125.65 MB
Total decode time including all transfers for 200 images per 2 threads = 502.3 ms; 398.2 FPS;

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Single thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.4k.ri1.jpg -o wild.4k.ppm -repeat 100 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.07 GB
PCI-Express bandwidth test (host to device): 4774 MByte/s

Requested GPU memory space: 251.20 MB

Input image: .\Images\wild.4k.ri1.jpg
Input image size: 3840x2160 pixels
Input sampling format: 444
Input restart interval: 1

Process time for 100 images without HDD I/O and excluding device-to-host transfer = 220.58 ms; 453 FPS

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Multi thread)

F:\AI_Componets\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.4k.ri1.jpg -o wild.4k.mt.ppm -repeat 200 -thread 2 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expired in 325 days. Expired date: 2025-11-06

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 8.07 GB
PCI-Express bandwidth test (host to device): 4663 MByte/s

Requested GPU memory size: 502.41 MB
Total decode time including all transfers for 200 images per 2 threads = 1208.5 ms; 165.5 FPS;

You can try their demo app obtained from their website. I cannot post links in the forum, which prevents me.

Nvidia also provides their own Huffman decoding API, but I haven't had the time to test it yet.

Another problem is that when DirectShow has the camera frames, it then again needs to copy these frames from one buffer into a pinned buffer for the GPU to have access to it. So, the camera frames are again traveling all over the world in system RAM from one place to another place to do a very simple task.

As you can see, I want to keep things extremely simple: grab camera frames, prevent having them travel all over the world in system RAM only to get into the hands of the GPU. In theory, if I can grab camera frames, have them travel only "once" in system RAM, and have the GPU directly copy and perform computation, I will greatly improve latency and performance. Every single millisecond matters. You might say go use a different OS, but if Microsoft provides "abilities" for improving latency, I might as well use those abilities since all of the world's cutting-edge engineering software are implemented in Windows, which I use very often and doesn't work in different OS.

WinUSB is the most complicated API I've ever encountered in my life. I have to do tons and tons of API calls just to do simple tasks which have no great value. I once read a book by Charles Petzold (Microsoft Professional software engineer) and he mentioned that low-level programming was intentionally made to be complicated. I suspect WinUSB and all of Microsoft low-level APIs are made complicated so that people won't be able to hack around with it? Even AI can't even figure these things out, yet they say "AGI" is here :rofl:.

BTW, the string descriptor says "USB3.0 HD Video Capture", but it's not USB 3.0 -- it is a USB 2 device, running high speed. Because of that, when the wMaxPacketSize says 0x1400, that doesn't mean "5120 bytes", as you have. There are bit fields in there. It means 3 transactions of 1024 bytes per microframe, so the packet size is actually 3072 bytes. That is the maximum for a USB 2 isochronous pipe -- 24MB/s.

Thank you for this clarification. I was extremely confused about 0x1400 and things made much more sense with the math regarding the bit fields. The USB devices are Chinese and cheap, so I'm not surprised it is USB 2.0 when it was falsely advertised as USB 3.0 on Amazon. They work really great for the given application, however.

I also finally made WinUSB obtain data using isochronous endpoints. I will share results and the code later. I'll also compare it to DirectShow in terms of decoding latency from USB device to a complete decoded MJPEG frame.

The processing times you are showing are for JPEG encoding, not JPEG decoding. Decoding is pretty quick, even in the CPU.

Such as buffer overflow happens if I use 30FPS input instead of the native 60FPS.

How are you setting 30FPS? If you are using DirectShow directly, you can use IAMStreamConfig::SetFormat to select the frame rate you want, assuming the camera has the ability to adjust the rate. Not all of them do.

Who knows how many times the camera frames (directly out from the USB device) have been copied and transferred to how many different memory regions in system RAM until it finally got into the hands of DirectShow.

Well, WE do. This is not a mystery -- it is all well-studied and well-understood. When the DirectShow graph is created, the graph creates an "allocator". That allocator creates a set of empty frame buffers, which get queued up to the UVC driver. The UVC driver then creates USB read requests for those buffers and submits them to the host controller driver. When the hardware signals that a buffer has been filled, the UVC driver signals to the graph that a new frame is available. That buffer is passed (as a pointer) down the graph for further processing.

Up to that point, there have been zero copies. The host controller hardware did DMA directly into the graph's memory buffer. Video capture in Windows is not slapped together. DirectShow and kernel streaming were created clear back in 1994, when CPUs and memories were not very fast. The path is highly optimized to avoid copies, although CPUs these days do copies very, very, very fast. Personally, I think you have committed the sin premature optimization.

You CANNOT have a single pinned buffer. It simply won't work. USB works in real time. When one buffer gets filled, the next empty buffer already has to be in the hands of the hardware in order to start collecting the next frame.

It IS possible to create your own allocator, so you can supply a set of your own buffers to the graph, but there will always be a "set" of buffers. Also, I'm not sure that the USB host controller hardware has the ability to DMA to another device.

Are you 100% sure that Nvidia does not already have a DirectShow filter that does MJPEG decompression in the GPU? All of the plumbing for that is built-in to DirectShow.

I need to somehow directly get my hands on the full control of the raw data fresh out from the HDMI-USB cap card.

That's just not how it works, nor do I believe it is necessary in this case. What's the point of worrying about 500 FPS decoding if your camera can only deliver 60 FPS? If you really have a USB 2 device, then you're never going to achieve high frame rates. You're limited to 24MB/s. If your MJPEG compression ratio is 10:1, a full HD image will be 600kB, giving you a maximum of 40 FPS.

Thanks for you reply.

The processing times you are showing are for JPEG encoding, not JPEG decoding. Decoding is pretty quick, even on the CPU.

Here are the decoding times: 178.31 ms; 561 FPS, which is ~3 JPEG Frames/ms. I don't think my AMD FX-3850 CPU or any common CPU can do that fast:

Microsoft Windows [Version 10.0.19045.5247]
(c) Microsoft Corporation. All rights reserved.

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>"F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13\run_fast_jpeg.performance.x64 - Decode.cmd"

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Single thread)

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.2k.ri1.jpg -o wild.2k.ppm -repeat 100 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expires in 324 days. Expiry date: 2025-11-05

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 7.87 GB
PCI-Express bandwidth test (host to device): 4125 MByte/s

Requested GPU memory space: 62.82 MB

Input image: .\Images\wild.2k.ri1.jpg
Input image size: 1920x1080 pixels
Input sampling format: 444
Input restart interval: 1

Process time for 100 images without HDD I/O and excluding device-to-host transfer = 178.31 ms; 561 FPS

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Multi thread)

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.2k.ri1.jpg -o wild.2k.mt.ppm -repeat 200 -thread 2 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expires in 324 days. Expiry date: 2025-11-05

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 7.87 GB
PCI-Express bandwidth test (host to device): 4370 MByte/s

Requested GPU memory size: 125.65 MB
Total decode time including all transfers for 200 images per 2 threads = 473.1 ms; 422.7 FPS;

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Single thread)

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.4k.ri1.jpg -o wild.4k.ppm -repeat 100 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expires in 324 days. Expiry date: 2025-11-05

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 7.87 GB
PCI-Express bandwidth test (host to device): 4963 MByte/s

Requested GPU memory space: 251.20 MB

Input image: .\Images\wild.4k.ri1.jpg
Input image size: 3840x2160 pixels
Input sampling format: 444
Input restart interval: 1

Process time for 100 images without HDD I/O and excluding device-to-host transfer = 228.27 ms; 438 FPS

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>REM JPEG decoder (Multi thread)

F:\AI_Components\JPEG_CODEC\fast_jpeg\fast_jpeg.x64.0.19.4.2.exp-2025-12-13>fast_jpeg.exe -i .\Images\wild.4k.ri1.jpg -o wild.4k.mt.ppm -repeat 200 -thread 2 -discard
SDK version: 0.19.4.2 (build date: 2024-12-16)
License type: Trial. Expires in 324 days. Expiry date: 2025-11-05

Processing unit: NVIDIA GeForce RTX 3080 Ti (device id = 0)
Available GPU memory size: 7.87 GB
PCI-Express bandwidth test (host to device): 4672 MByte/s

Requested GPU memory size: 502.41 MB
Total decode time including all transfers for 200 images per 2 threads = 1425.4 ms; 140.3 FPS;

How are you setting 30 FPS? If you are using DirectShow directly, you can use IAMStreamConfig::SetFormat to select the frame rate you want, assuming the camera has the ability to adjust the rate. Not all of them do.

I haven't used DirectShow directly, since I assumed it would be a waste of time. I used DirectShow via the FFMPEG API. The camera descriptor shows multiple resolution outputs and frame rates, yet I get buffer overflow if the input frame rate is not set to 60 FPS.

When the DirectShow graph is created, the graph creates an "allocator". That allocator creates a set of empty frame buffers, which get queued up to the UVC driver. The UVC driver then creates USB read requests for those buffers and submits them to the host controller driver. When the hardware signals that a buffer has been filled, the UVC driver signals to the graph that a new frame is available. That buffer is passed (as a pointer) down the graph for further processing.

Up to that point, there have been zero copies. The host controller hardware did DMA directly into the graph's memory buffer. Video capture in Windows is not slapped together. DirectShow and kernel streaming were created back in 1994, when CPUs and memories were not very fast. The path is highly optimized to avoid copies, although CPUs these days do copies very, very, very fast. Personally, I think you have committed the sin of premature optimization.

Now this seems interesting. Is it possible to have the DirectShow graph buffer in a pinned memory location? I'll create a custom allocator such that JPEG frames are written and overwritten for the next frame or whatnot. I want the GPU to grab these JPEG frames directly.

It's good to know that the logic path was carefully thought of and optimized. However, I know with 99% certainty that it cannot decode the Huffman component in JPEGs, which itself makes DirectShow a sin of premature optimization attempts in using only CPUs. Thus, folks like me need to make it more mature to use the GPUs. As I've shown previously, Nvidia's modern GPUs can do a whopping 561 FPS 100% (Huffman included) decoding while offloading a 13-year-old CPU.

I might consider using the DirectShow API directly if some workaround could be done. It would save me a great deal of scratch low-level programming.

You CANNOT have a single pinned buffer. It simply won't work. USB works in real-time. When one buffer gets filled, the next empty buffer already has to be in the hands of the hardware in order to start collecting the next frame.

It IS possible to create your own allocator, so you can supply a set of your own buffers to the graph, but there will always be a "set" of buffers. Also, I'm not sure that the USB host controller hardware has the ability to DMA to another device.

I already spoke to a few Nvidia devs, they said it is possible, not sure what makes you think it is not. In fact, today, there are many enterprises and professionals who need these specific requirements of DMA directly into the GPU. You have PCIe fabrics of network cards and NVMe drives which do RDMA directly into Nvidia GPUs. Not sure what makes the USB host controller not a viable candidate... it is possible in theory to do this, since the USB host controller hardware has access to system RAM. However, my particular GPU does not support RDMA and only supports DMA.

Are you 100% sure that Nvidia does not already have a DirectShow filter that does MJPEG decompression in the GPU? All of the plumbing for that is built into DirectShow.

Yes, I am sure. Huffman decoding had the unfortunate invention of being serial rather than parallel. Only maybe 2 or 3 computer scientists out of 8 billion people in this universe have actually contributed to making Huffman parallel so that it can be decoded in the GPU. You should know this with your decades of experience in media encoding/decoding. Actually, many programmers don't know because lots of misinformation is spread. They say MJPEG can be decoded in the GPU, while "technically" this is true, what they don't tell you is that the decoding is hybrid. Huffman is done in the CPU and the inverse DCT is done in the GPU.

However, Nvidia's latest CUDA toolkits allow 100% MJPEG decoding in the GPU, including the Huffman component. There are also third-party decoders which can do this too, which I've posted much above.

That's just not how it works, nor do I believe it is necessary in this case. What's the point of worrying about 500 FPS decoding if your camera can only deliver 60 FPS? If you really have a USB 2 device, then you're never going to achieve high frame rates. You're limited to 24 MB/s. If your MJPEG compression ratio is 10:1, a full HD image will be 600 kB, giving you a maximum of 40 FPS.

It's not about my specific setup or hardware, it's about future setups. Although the current setup has latency issues since MJPEG decoding is done in CPU.
As of now, I have a prototype setup, which is at its minimum, using low-cost, cheap camera hardware. It's for high-performance security surveillance applications where latency is crucial. Every millisecond matters, since the software is designed to predict the future by the second(s). I am trying my best to make the software at its best.

You might also look at DXVA and DXVA2, which were specifically designed to integrate GPU-accelerated decoding into media graphs. A DXVA-enabled decode filter can impose its special memory on the graph.

1 Like

You might also look at DXVA and DXVA2, which were specifically designed to integrate GPU-accelerated decoding into media graphs. A DXVA-enabled decode filter can impose its special memory on the graph.

Thanks for the reply.

Seems like DXVA has Huffman decoding, however minimum support is for Windows 11 (I use Windows 10), so they just recently added this new Huffman decoding implementation:

Requirement Value
Minimum supported client Windows 11, verion 24H2
Header dxva.h

Reference:
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/dxva/ns-dxva-dxva_huffmantable_mjpeg

Maybe there are some workarounds... that might make it work in Windows 10?

Anyhow, hope you can help me with this WinUSB C++ program which I've got going.

I can't establish a WinUsb_RegisterIsochBuffer request. Keep getting an error returned:

Failed to start a `WinUsb_ReadIsochPipe` request. ---->: Error Code: [ 57 ], Error Meaning: [ The parameter is incorrect. ]

Can not figure the life of me what is causing this error.

Here is the C++ code:


#include <windows.h>
#include <winusb.h>
#include <setupapi.h>
#include <usb.h>
#include <iostream>
#include <string>
#include <stdio.h> // For printf

#define BUFFER_SIZE 3072
#define ISOCH_DATA_SIZE_MS 1 // 1 millisecond of isochronous data
#define ISOCH_TRANSFER_COUNT 1 // Number of transfers to perform

typedef struct _DEVICE_DATA {
    BOOL                    HandlesOpen;
    WINUSB_INTERFACE_HANDLE WinusbHandle;
    HANDLE                  DeviceHandle;
    TCHAR                   DevicePath[MAX_PATH];
    UCHAR                   IsochOutPipe;
    UCHAR                   IsochInPipe;
    ULONG                   IsochInTransferSize;
    ULONG                   IsochOutTransferSize;
    ULONG                   IsochInPacketCount;
} DEVICE_DATA, *PDEVICE_DATA;


void ParseDescriptors(UCHAR* buffer, ULONG length) {
    UCHAR* ptr = buffer;
    UCHAR* end = buffer + length;

    while (ptr < end) 
    {
        PUSB_COMMON_DESCRIPTOR commonDesc = (PUSB_COMMON_DESCRIPTOR)ptr;

        if (commonDesc->bLength < 2) {
            std::cerr << "Invalid descriptor length" << std::endl;
            break;
        }

        // Move to the next descriptor
        ptr += commonDesc->bLength;
    }
}

std::string GetErrorMessage(HRESULT hr) {
    LPSTR messageBuffer = nullptr;
    DWORD bufferLength = FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        nullptr,
        hr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPSTR>(&messageBuffer),
        0,
        nullptr);

    if (bufferLength == 0) {
        return "Unknown error";
    }

    std::string errorMessage(messageBuffer, bufferLength);
    LocalFree(messageBuffer);
    return errorMessage;
}

HRESULT GetIsochPipes(_Inout_ PDEVICE_DATA DeviceData) {
    BOOL result;
    USB_INTERFACE_DESCRIPTOR usbInterface;
    WINUSB_PIPE_INFORMATION_EX pipe;
    HRESULT hr = S_OK;
    UCHAR i;

    WINUSB_INTERFACE_HANDLE associatedInterfaceHandle = NULL;
    result = WinUsb_GetAssociatedInterface(DeviceData->WinusbHandle, 0, &associatedInterfaceHandle);

    if (result == FALSE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        std::cerr << "WinUsb_GetAssociatedInterface failed to get handle for interface 0." << std::endl;
        return hr;
    }

    result = WinUsb_QueryInterfaceSettings(associatedInterfaceHandle, 1, &usbInterface);

    if (result == FALSE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        std::cerr << "WinUsb_QueryInterfaceSettings failed to get USB interface for Alternate Setting 1." << std::endl;
        WinUsb_Free(associatedInterfaceHandle);
        return hr;
    }

    std::cout << "\n\nChecking Alternate Setting 1 with [" << (int)usbInterface.bNumEndpoints << "] endpoints:" << std::endl;

    for (i = 0; i < usbInterface.bNumEndpoints; i++) 
    {
        result = WinUsb_QueryPipeEx(
            associatedInterfaceHandle,
            1,
            i,
            &pipe);

        if (result == FALSE) 
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            std::cerr << "WinUsb_QueryPipeEx failed to get USB pipe for endpoint " << (int)i << " in Alternate Setting 1." << std::endl;
            CloseHandle(DeviceData->DeviceHandle);
            return hr;
        }

        std::cout << "Pipe " << (int)i << " in Alternate Setting 1: "
                  << "PipeId = " << (int)pipe.PipeId << ", PipeType = " << (int)pipe.PipeType
                  << ", MaximumBytesPerInterval = " << pipe.MaximumBytesPerInterval
                  << ", Interval = " << (int)pipe.Interval << "\n\n" << std::endl;

        if ((pipe.PipeType == UsbdPipeTypeIsochronous) && (!(pipe.PipeId == 0x82)))  // <-------- Need to make sure to hardcode the IN-Endpoint here!.
        {
            DeviceData->IsochOutPipe = pipe.PipeId;

            if ((pipe.MaximumBytesPerInterval == 0) || (pipe.Interval == 0))
            {
                hr = E_INVALIDARG;
                printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
                CloseHandle(DeviceData->DeviceHandle);
                return hr;
            }
            else
            {
                DeviceData->IsochOutTransferSize = ISOCH_DATA_SIZE_MS * pipe.MaximumBytesPerInterval * (8 / pipe.Interval);
                std::cout << "Isochronous Out Pipe Transfer Size: " << DeviceData->IsochOutTransferSize << std::endl;
            }
        }
        else if (pipe.PipeType == UsbdPipeTypeIsochronous)
        {
            DeviceData->IsochInPipe = pipe.PipeId;

            if (pipe.MaximumBytesPerInterval == 0 || (pipe.Interval == 0))
            {
                hr = E_INVALIDARG;
                printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
                CloseHandle(DeviceData->DeviceHandle);
                return hr;
            }
            else
            {
                DeviceData->IsochInTransferSize = ISOCH_DATA_SIZE_MS * pipe.MaximumBytesPerInterval * (8 / pipe.Interval);
                // Dynamically calculate the packet count
                DeviceData->IsochInPacketCount = DeviceData->IsochInTransferSize / pipe.MaximumBytesPerInterval;
                std::cout << "Isochronous In Pipe Transfer Size: " << DeviceData->IsochInTransferSize << std::endl;
                std::cout << "Isochronous In Pipe Packet Count: " << DeviceData->IsochInPacketCount << std::endl;
            }
        }
    }

    WinUsb_Free(associatedInterfaceHandle);
    return hr;
}


VOID SendIsochInTransfer(
    _Inout_ PDEVICE_DATA DeviceData,
    _In_ BOOL AsapTransfer
) {
    PUCHAR readBuffer = nullptr;
    LPOVERLAPPED overlapped = nullptr;
    ULONG numBytes = 0;
    BOOL result = FALSE;
    DWORD lastError = 0;
    WINUSB_ISOCH_BUFFER_HANDLE isochReadBufferHandle;
    PUSBD_ISO_PACKET_DESCRIPTOR isochPackets = nullptr;
    ULONG i = 0;
    ULONG j = 0;

    ULONG totalTransferSize;
    
    readBuffer = NULL;
    isochPackets = NULL;
    overlapped = NULL;
    isochReadBufferHandle = INVALID_HANDLE_VALUE;

    printf("\n\nInitiating Read transfer.\n\n");

    totalTransferSize = DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT;

    if (totalTransferSize % DeviceData->IsochInTransferSize != 0) 
    {
        printf("Error: Transfer size must end at a frame boundary.\n");
        goto Error;
    }

    // Allocate aligned buffer
    readBuffer = new UCHAR[totalTransferSize];

    if (readBuffer == nullptr) 
    {
        printf("Failed to allocate memory for aligned buffer.\n");
        goto Error;
    }

    ZeroMemory(readBuffer, totalTransferSize);

    // Print transfer parameters for debugging:
    printf("\nParameters, BEFORE calling `WinUsb_RegisterIsochBuffer`:\n");
    printf("Original Buffer Address: %p\n", readBuffer);
    printf("Buffer Size in Memory (Bytes): %zu\n", sizeof(readBuffer)); 
    printf("WinusbHandle Address: %p\n", DeviceData->WinusbHandle);
    printf("IsochReadBufferHandle: Address %p\n", isochReadBufferHandle);


    // Allocate overlapped structures
    overlapped = new OVERLAPPED[ISOCH_TRANSFER_COUNT];
    ZeroMemory(overlapped, (sizeof(OVERLAPPED) * ISOCH_TRANSFER_COUNT));

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) 
    {
        overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        
        if (overlapped[i].hEvent == NULL) 
        {
            printf("Unable to set event for overlapped operation.\n");
            goto Error;
        }
    }

    // Allocate packet descriptors using new
    isochPackets = new USBD_ISO_PACKET_DESCRIPTOR[DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT];

    if (isochPackets == nullptr) 
    {
        printf("Failed to allocate packet descriptors.\n");
        goto Error;
    }

    // Zero-initialize the allocated memory
    ZeroMemory(isochPackets, DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT);

    // Register isochronous buffer
    result = WinUsb_RegisterIsochBuffer(
        DeviceData->WinusbHandle,
        DeviceData->IsochInPipe,
        readBuffer,
        DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT,
        &isochReadBufferHandle);


    // Print transfer parameters for debugging
    printf("\nWinUsb_ReadIsochPipeAsap Parameters, AFTER calling `WinUsb_RegisterIsochBuffer`:\n");
    printf("IsochReadBufferHandle: Address %p\n", isochReadBufferHandle);
    printf("TransferSize: %lu\n", DeviceData->IsochInTransferSize);
    printf("PacketCount: %lu\n", DeviceData->IsochInPacketCount);
    printf("Buffer Address: %p\n", readBuffer);
    printf("Buffer Size in Memory (Bytes): %zu\n", sizeof(readBuffer)); 
    printf("WinusbHandle Address: %p\n", DeviceData->WinusbHandle);
    printf("BufferLength: %lu\n\n", DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT);

    if (!result) 
    {
        // Convert the DWORD error code to an HRESULT
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());

        // Get the human-readable error message
        std::string errorMessage = GetErrorMessage(hr);

        // Print the error message
        printf("Isoch buffer registration failed. Error Code: [%d], Meaning: [%s]\n", GetLastError(), errorMessage.c_str());

        goto Error;
    }


    // Send read requests
    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) {
        printf("Transfer %d: Offset = %lu\n\n", i + 1, DeviceData->IsochInTransferSize * i);

        if (AsapTransfer) {
            result = WinUsb_ReadIsochPipeAsap(
                isochReadBufferHandle,
                DeviceData->IsochInTransferSize * i, // Offset should start at 0
                DeviceData->IsochInTransferSize,
                (i == 0) ? FALSE : TRUE, // ASAP flag for subsequent transfers
                DeviceData->IsochInPacketCount,
                &isochPackets[i * DeviceData->IsochInPacketCount],
                &overlapped[i]);

            printf("Read transfer sent by using ASAP flag.\n\n");
        }

        if (!result) 
        {
            lastError = GetLastError();

            if (lastError != ERROR_IO_PENDING) 
            {
                // Convert the DWORD error code to an HRESULT
                HRESULT hr = HRESULT_FROM_WIN32(lastError);

                // Get the human-readable error message
                std::string errorMessage = GetErrorMessage(hr);

                // Trim trailing newline characters <--------------------------------- Removes the included newline in the error message output.
                errorMessage.erase(errorMessage.find_last_not_of("\r\n") + 1);

                // Print the error message:
                printf("Failed to start a `WinUsb_ReadIsochPipe` request. ---->: Error Code: [ %x ], Error Meaning: [ %s ]\n\n", lastError, errorMessage.c_str());
            }
        }
    }

    // Wait for transfers to complete
    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) 
    {
        result = WinUsb_GetOverlappedResult(
            DeviceData->WinusbHandle,
            &overlapped[i],
            &numBytes,
            TRUE);

        if (!result) 
        {
            lastError = GetLastError();
            printf("Failed to read with error %x\n", lastError);
        } else 
        {
            numBytes = 0;
            for (j = 0; j < DeviceData->IsochInPacketCount; j++) 
            {
                numBytes += isochPackets[j].Length;
            }

            printf("Requested %d bytes in %d packets per transfer.\n", DeviceData->IsochInTransferSize, DeviceData->IsochInPacketCount);
        }

        printf("Transfer %d completed. Read %d bytes. \n\n", i + 1, numBytes);
    }

Error:
    if (isochReadBufferHandle != INVALID_HANDLE_VALUE) 
    {
        result = WinUsb_UnregisterIsochBuffer(isochReadBufferHandle);
        if (!result) 
        {
            printf("Failed to unregister isoch read buffer. \n");
        }
    }

    if (readBuffer != nullptr) 
    {
        delete [] readBuffer;
    }

    if (isochPackets != nullptr) 
    {
        delete [] isochPackets;
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++) 
    {
        if (overlapped[i].hEvent != NULL) 
        {
            CloseHandle(overlapped[i].hEvent);
        }
    }

    if (overlapped != nullptr) 
    {
        delete[] overlapped;
    }
    return;
}




int main() 
{
    DEVICE_DATA deviceData = {0};
    deviceData.DeviceHandle = INVALID_HANDLE_VALUE;
    deviceData.WinusbHandle = INVALID_HANDLE_VALUE;

    const wchar_t* devicePath = L"\\\\.\\USB#VID_EBA4&PID_7588&MI_02#6&63DE0D9&0&0002#{99DA2DEC-C919-4F9D-9384-DAA7EBBD40EC}";

    deviceData.DeviceHandle = CreateFileW(
        devicePath,
        GENERIC_WRITE | GENERIC_READ,
        FILE_SHARE_WRITE | FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL);

    if (deviceData.DeviceHandle == INVALID_HANDLE_VALUE) 
    {
        std::cerr << "Error opening device: " << GetLastError() << std::endl;
        return 1;
    }

    if (!WinUsb_Initialize(deviceData.DeviceHandle, &deviceData.WinusbHandle)) 
    {
        std::cerr << "Error initializing WinUSB: " << GetLastError() << std::endl;
        CloseHandle(deviceData.DeviceHandle);
        return 1;
    }

    UCHAR buffer[BUFFER_SIZE];
    ULONG lengthTransferred;

    USB_DEVICE_DESCRIPTOR deviceDesc;
    if (WinUsb_GetDescriptor(deviceData.WinusbHandle, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, buffer, sizeof(buffer), &lengthTransferred)) {
        memcpy(&deviceDesc, buffer, sizeof(USB_DEVICE_DESCRIPTOR));
    } 
    
    else
    
    {
        std::cerr << "Error getting device descriptor: " << GetLastError() << std::endl;
    }

    USB_CONFIGURATION_DESCRIPTOR configDesc;
    if (WinUsb_GetDescriptor(deviceData.WinusbHandle, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, buffer, sizeof(buffer), &lengthTransferred)) {
        memcpy(&configDesc, buffer, sizeof(USB_CONFIGURATION_DESCRIPTOR));
    } 
    
    else 
    {
        std::cerr << "Error getting configuration descriptor: " << GetLastError() << std::endl;
    }

    UCHAR* configBuffer = new UCHAR[configDesc.wTotalLength];
    if (WinUsb_GetDescriptor(deviceData.WinusbHandle, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, configBuffer, configDesc.wTotalLength, &lengthTransferred)) {
        ParseDescriptors(configBuffer, configDesc.wTotalLength);
    } 
    else 
    {
        std::cerr << "Error getting interface descriptors: " << GetLastError() << std::endl;
    }

    delete[] configBuffer;

    HRESULT hr = GetIsochPipes(&deviceData);
    if (FAILED(hr)) 
    {
        std::cerr << "Error getting isochronous pipes: " << GetErrorMessage(hr) << std::endl;
    } 
    
    else 
    {
        // Send isochronous IN transfer
        SendIsochInTransfer(&deviceData, TRUE);  // <--- `True` if using ASAP.
    }

    WinUsb_Free(deviceData.WinusbHandle);
    CloseHandle(deviceData.DeviceHandle);

    return 0;
}

How to compile:

cl /EHsc /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\winrt" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt" main.cpp /link /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64" /LIBPATH:"F:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\lib\x64" winusb.lib SetupAPI.lib cfgmgr32.lib

Here is the program output:

F:\AI_Componets\WinUSB\main_test>main.exe


Checking Alternate Setting 1 with [1] endpoints:
Pipe 0 in Alternate Setting 1: PipeId = 130, PipeType = 1, MaximumBytesPerInterval = 3072, Interval = 1


Isochronous In Pipe Transfer Size: 24576
Isochronous In Pipe Packet Count: 8


Initiating Read transfer.


Parameters, BEFORE calling `WinUsb_RegisterIsochBuffer`:
Original Buffer Address: 000002904D8832B0
Buffer Size in Memory (Bytes): 8
WinusbHandle Address: 000002904D8819F0
IsochReadBufferHandle: Address FFFFFFFFFFFFFFFF

WinUsb_ReadIsochPipeAsap Parameters, AFTER calling `WinUsb_RegisterIsochBuffer`:
IsochReadBufferHandle: Address 000002904D8898B0
TransferSize: 24576
PacketCount: 8
Buffer Address: 000002904D8832B0
Buffer Size in Memory (Bytes): 8
WinusbHandle Address: 000002904D8819F0
BufferLength: 24576

Transfer 1: Offset = 0

Read transfer sent by using ASAP flag.

Failed to start a `WinUsb_ReadIsochPipe` request. ---->: Error Code: [ 57 ], Error Meaning: [ The parameter is incorrect. ]

Requested 24576 bytes in 8 packets per transfer.
Transfer 1 completed. Read 1369252523 bytes.

Failed to unregister isoch read buffer.

So from the USB descriptor which I've posted much earlier, you've mentioned that it can do max transaction per microframe of 3072 Bytes, since 1 frame has 8 microframes, that would mean 24,576 Bytes for 1 frame transfer and buffer would also needs to be 24,576 Bytes? Does this mean that the packet size is also 24 packets per transfer?

Thanks for advice.

Please discard the previous code.

I've now read MS Docs religiously and now have this:

/*

How to compile:
cl /EHsc /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\winrt" /I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt" main.cpp /link /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\ucrt\x64" /LIBPATH:"F:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\lib\x64" winusb.lib SetupAPI.lib cfgmgr32.lib

*/

#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <winusb.h>
#include <usb.h>
#include <cfgmgr32.h>
#include <stdio.h>  // For printf
#include <iostream>
#include <string>
#include <initguid.h>

#define ISOCH_DATA_SIZE_MS   1
#define ISOCH_TRANSFER_COUNT   1


DEFINE_GUID(GUID_DEVINTERFACE_USBApplication1, 0xD3CACD43, 0xD2AF, 0x4589, 0x8A, 0x20, 0x7B, 0x1B, 0xB2, 0x08, 0xC4, 0x75); // Device Interface GUID.

typedef struct _DEVICE_DATA {

    BOOL                    HandlesOpen;
    WINUSB_INTERFACE_HANDLE WinusbHandle;
    WINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle; // Add this
    HANDLE                  DeviceHandle;
    TCHAR                   DevicePath[MAX_PATH];
    UCHAR                   IsochOutPipe;
    UCHAR                   IsochInPipe;
    ULONG                   IsochInTransferSize;
    ULONG                   IsochOutTransferSize;
    ULONG                   IsochInPacketCount;
    ULONG                   IsochInBytesPerFrame; // Add this member

} DEVICE_DATA, *PDEVICE_DATA;



HRESULT GetIsochPipes(_Inout_ PDEVICE_DATA DeviceData)
{
       BOOL result;
       USB_INTERFACE_DESCRIPTOR usbInterface;
       WINUSB_PIPE_INFORMATION_EX pipe;
       HRESULT hr = S_OK;
       UCHAR i;

    // Use the DeviceData->AssociatedInterfaceHandle instead of a local variable
    result = WinUsb_GetAssociatedInterface(DeviceData->WinusbHandle, 0, &DeviceData->AssociatedInterfaceHandle);

    if (result == FALSE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        std::cerr << "WinUsb_GetAssociatedInterface failed to get handle for interface 0." << std::endl;
        CloseHandle(DeviceData->WinusbHandle);
        return hr;
    }

    result = WinUsb_QueryInterfaceSettings(DeviceData->AssociatedInterfaceHandle, 1, &usbInterface);

    if (result == FALSE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        std::cerr << "WinUsb_QueryInterfaceSettings failed to get USB interface for Alternate Setting 1." << std::endl;
        CloseHandle(DeviceData->AssociatedInterfaceHandle);
        return hr;
    }

       for (i = 0; i < usbInterface.bNumEndpoints; i++)
       {
              result = WinUsb_QueryPipeEx(
                     DeviceData->AssociatedInterfaceHandle,
                     1,
                     (UCHAR) i,
                     &pipe);

              if (result == FALSE)
              {
                     hr = HRESULT_FROM_WIN32(GetLastError());
                     printf(_T("WinUsb_QueryPipeEx failed to get USB pipe.\n"));
                     CloseHandle(DeviceData->DeviceHandle);
                     return hr;
              }

        if ((pipe.PipeType == UsbdPipeTypeIsochronous) && (!(pipe.PipeId == 0x82)))  // <-------- Need to make sure to hardcode the IN-Endpoint here!.
        {
            DeviceData->IsochOutPipe = pipe.PipeId;
            printf("Isoch Out Pipe ID: %d\n", DeviceData->IsochOutPipe);

            if ((pipe.MaximumBytesPerInterval == 0) || (pipe.Interval == 0))
            {
                hr = E_INVALIDARG;
                    printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
                    CloseHandle(DeviceData->DeviceHandle);
                    return hr;
            }
            else
            {
                    DeviceData->IsochOutTransferSize = ISOCH_DATA_SIZE_MS * pipe.MaximumBytesPerInterval * (8 / pipe.Interval);
                    std::cout << "Isochronous Out Pipe Transfer Size: " << DeviceData->IsochOutTransferSize << std::endl;

            }
        }
        else if (pipe.PipeType == UsbdPipeTypeIsochronous)
        {
            DeviceData->IsochInPipe = pipe.PipeId;
            printf("Isoch In Pipe ID: %d\n", DeviceData->IsochInPipe);

            if (pipe.MaximumBytesPerInterval == 0 || (pipe.Interval == 0))
            {
                hr = E_INVALIDARG;
                    printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
                    CloseHandle(DeviceData->DeviceHandle);
                    return hr;
            }
            else
            {
                    DeviceData->IsochInTransferSize = ISOCH_DATA_SIZE_MS * pipe.MaximumBytesPerInterval * (8 / pipe.Interval);
                    // Dynamically calculate the packet count
                    DeviceData->IsochInPacketCount = DeviceData->IsochInTransferSize / pipe.MaximumBytesPerInterval;
                    std::cout << "Isochronous In Pipe Transfer Size: " << DeviceData->IsochInTransferSize << std::endl;
                    std::cout << "Isochronous In Pipe Packet Count: " << DeviceData->IsochInPacketCount << std::endl;
            }
        }
        }
        return hr;
}

VOID SendIsochInTransfer(
    _Inout_ PDEVICE_DATA DeviceData,
    _In_ BOOL AsapTransfer )
{
    PUCHAR readBuffer;
    LPOVERLAPPED overlapped;
    ULONG numBytes;
    BOOL result;
    DWORD lastError;
    WINUSB_ISOCH_BUFFER_HANDLE isochReadBufferHandle;
    PUSBD_ISO_PACKET_DESCRIPTOR isochPackets;
    ULONG i;
    ULONG j;

    ULONG frameNumber;
    ULONG startFrame;
    LARGE_INTEGER timeStamp;

    ULONG totalTransferSize;

    readBuffer = NULL;
    isochPackets = NULL;
    overlapped = NULL;
    isochReadBufferHandle = INVALID_HANDLE_VALUE;

    printf(_T("\n\nRead transfer.\n"));

    totalTransferSize = DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT;

    printf("totalTransferSize: %lu\n\n", totalTransferSize);

    if (totalTransferSize % DeviceData->IsochInBytesPerFrame != 0)
    {
        printf(_T("Transfer size must end at a frame boundary.\n"));
        goto Error;
    }

    readBuffer = new UCHAR[totalTransferSize];

    if (readBuffer == NULL)
    {
        printf(_T("Unable to allocate memory.\n"));
        goto Error;
    }

    ZeroMemory(readBuffer, totalTransferSize);

    overlapped = new OVERLAPPED[ISOCH_TRANSFER_COUNT];
    ZeroMemory(overlapped, (sizeof(OVERLAPPED) * ISOCH_TRANSFER_COUNT));

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        if (overlapped[i].hEvent == NULL)
        {
            printf("Unable to set event for overlapped operation.\n");
            goto Error;
        }
    }

    isochPackets = new USBD_ISO_PACKET_DESCRIPTOR[DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT];
    ZeroMemory(isochPackets, DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT);

    result = WinUsb_RegisterIsochBuffer(
        DeviceData->WinusbHandle,
        DeviceData->IsochInPipe,
        readBuffer,
        DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT,
        &isochReadBufferHandle);

    if (!result)
    {
        printf(_T("Isoch buffer registration failed.\n"));
        goto Error;
    }

    result = WinUsb_GetCurrentFrameNumber(DeviceData->WinusbHandle, &frameNumber,&timeStamp);

    if (!result)
    {
        printf(_T("WinUsb_GetCurrentFrameNumber failed.\n"));
        goto Error;
    }

    startFrame = frameNumber + 5;

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        if (AsapTransfer)
        {
            result = WinUsb_ReadIsochPipeAsap(
                isochReadBufferHandle,
                DeviceData->IsochInTransferSize * i,
                DeviceData->IsochInTransferSize,
                (i == 0) ? FALSE : TRUE,
                DeviceData->IsochInPacketCount,
                &isochPackets[i * DeviceData->IsochInPacketCount],
                &overlapped[i]);

            printf("Read transfer sent by using ASAP flag.\n");
        }
        else
        {
            printf("Transfer starting at frame %d.\n", startFrame);

            result = WinUsb_ReadIsochPipe(
                isochReadBufferHandle,
                DeviceData->IsochInTransferSize * i,
                DeviceData->IsochInTransferSize,
                &startFrame,
                DeviceData->IsochInPacketCount,
                &isochPackets[i * DeviceData->IsochInPacketCount],
                &overlapped[i]);

            printf("Next transfer frame %d.\n", startFrame);
        }

        if (!result)
        {
            lastError = GetLastError();

            if (lastError != ERROR_IO_PENDING)
            {
                printf("Failed to start a read operation with error %x\n", lastError);
            }
        }
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        result = WinUsb_GetOverlappedResult(
            DeviceData->WinusbHandle,
            &overlapped[i],
            &numBytes,
            TRUE);

        if (!result)
        {
            lastError = GetLastError();

            printf("Failed to read with error %x\n", lastError);
        }
        else
        {
            numBytes = 0;
            for (j = 0; j < DeviceData->IsochInPacketCount; j++)
            {
                numBytes += isochPackets[j].Length;
            }

            printf("Requested %d bytes in %d packets per transfer.\n", DeviceData->IsochInTransferSize, DeviceData->IsochInPacketCount);
        }

        printf("Transfer %d completed. Read %d bytes. \n\n", i+1, numBytes);
    }

Error:
    if (isochReadBufferHandle != INVALID_HANDLE_VALUE)
    {
        result = WinUsb_UnregisterIsochBuffer(isochReadBufferHandle);
        if (!result)
        {
            printf(_T("Failed to unregister isoch read buffer. \n"));
        }
    }

    if (readBuffer != NULL)
    {
        delete [] readBuffer;
    }

    if (isochPackets != NULL)
    {
        delete [] isochPackets;
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        if (overlapped[i].hEvent != NULL)
        {
            CloseHandle(overlapped[i].hEvent);
        }
    }

    if (overlapped != NULL)
    {
        delete [] overlapped;
    }
    return;
}

HRESULT OpenDevice( 
    _Out_ PDEVICE_DATA DeviceData, 
    _Out_opt_ PBOOL FailureDeviceNotFound );

VOID CloseDevice(
    _Inout_ PDEVICE_DATA DeviceData );

HRESULT RetrieveDevicePath(
    _Out_bytecap_(BufLen) LPTSTR DevicePath,
    _In_                  ULONG  BufLen,
    _Out_opt_             PBOOL  FailureDeviceNotFound );

HRESULT OpenDevice(
    _Out_     PDEVICE_DATA DeviceData,
    _Out_opt_ PBOOL        FailureDeviceNotFound )

{
    HRESULT hr = S_OK;
    BOOL    bResult;

    DeviceData->HandlesOpen = FALSE;

    hr = RetrieveDevicePath(DeviceData->DevicePath,
                            sizeof(DeviceData->DevicePath),
                            FailureDeviceNotFound);

    if (FAILED(hr)) 
    {
        return hr;
    }

    DeviceData->DeviceHandle = CreateFile(DeviceData->DevicePath,
                                          GENERIC_WRITE | GENERIC_READ,
                                          FILE_SHARE_WRITE | FILE_SHARE_READ,
                                          NULL,
                                          OPEN_EXISTING,
                                          FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                          NULL);

    if (INVALID_HANDLE_VALUE == DeviceData->DeviceHandle) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    bResult = WinUsb_Initialize(DeviceData->DeviceHandle,
                                &DeviceData->WinusbHandle);

    if (FALSE == bResult) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        CloseHandle(DeviceData->DeviceHandle);
        return hr;
    }

    DeviceData->HandlesOpen = TRUE;
    return hr;
}

VOID CloseDevice(
    _Inout_ PDEVICE_DATA DeviceData )

{
    if (FALSE == DeviceData->HandlesOpen) {

        //
        // Called on an uninitialized DeviceData
        //
        return;
    }

    WinUsb_Free(DeviceData->WinusbHandle);
    CloseHandle(DeviceData->DeviceHandle);
    DeviceData->HandlesOpen = FALSE;

    return;
}

HRESULT RetrieveDevicePath(
    _Out_bytecap_(BufLen) LPTSTR DevicePath,
    _In_                  ULONG  BufLen,
    _Out_opt_             PBOOL  FailureDeviceNotFound )

{
    CONFIGRET cr = CR_SUCCESS;
    HRESULT   hr = S_OK;
    PTSTR     DeviceInterfaceList = NULL;
    ULONG     DeviceInterfaceListLength = 0;

    if (NULL != FailureDeviceNotFound) {

        *FailureDeviceNotFound = FALSE;
    }

    //
    // Enumerate all devices exposing the interface. Do this in a loop
    // in case a new interface is discovered while this code is executing,
    // causing CM_Get_Device_Interface_List to return CR_BUFFER_SMALL.
    //
    do {
        cr = CM_Get_Device_Interface_List_Size(&DeviceInterfaceListLength,
                                               (LPGUID)&GUID_DEVINTERFACE_USBApplication1,
                                               NULL,
                                               CM_GET_DEVICE_INTERFACE_LIST_PRESENT);

        if (cr != CR_SUCCESS) {
            hr = HRESULT_FROM_WIN32(CM_MapCrToWin32Err(cr, ERROR_INVALID_DATA));
            break;
        }

        DeviceInterfaceList = (PTSTR)HeapAlloc(GetProcessHeap(),
                                               HEAP_ZERO_MEMORY,
                                               DeviceInterfaceListLength * sizeof(TCHAR));

        if (DeviceInterfaceList == NULL) {
            hr = E_OUTOFMEMORY;
            break;
        }

        cr = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_USBApplication1,
                                          NULL,
                                          DeviceInterfaceList,
                                          DeviceInterfaceListLength,
                                          CM_GET_DEVICE_INTERFACE_LIST_PRESENT);

        if (cr != CR_SUCCESS) {
            HeapFree(GetProcessHeap(), 0, DeviceInterfaceList);

            if (cr != CR_BUFFER_SMALL) {
                hr = HRESULT_FROM_WIN32(CM_MapCrToWin32Err(cr, ERROR_INVALID_DATA));
            }
        }
    } while (cr == CR_BUFFER_SMALL);

    if (FAILED(hr)) {
        return hr;
    }

    //
    // If the interface list is empty, no devices were found.
    //
    if (*DeviceInterfaceList == TEXT('\0')) {
        if (NULL != FailureDeviceNotFound) {
            *FailureDeviceNotFound = TRUE;
        }

        hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
        HeapFree(GetProcessHeap(), 0, DeviceInterfaceList);
        return hr;
    }

    //
    // Give path of the first found device interface instance to the caller. CM_Get_Device_Interface_List ensured
    // the instance is NULL-terminated.
    //
    hr = StringCbCopy(DevicePath,
                      BufLen,
                      DeviceInterfaceList);

    HeapFree(GetProcessHeap(), 0, DeviceInterfaceList);

    return hr;
}

LONG __cdecl _tmain(
    LONG     Argc,
    LPTSTR * Argv
    )
/*++

Routine description:

    Sample program that communicates with a USB device using WinUSB

--*/
{
    DEVICE_DATA           deviceData;
    HRESULT               hr;
    USB_DEVICE_DESCRIPTOR deviceDesc;
    BOOL                  bResult;
    BOOL                  noDevice;
    ULONG                 lengthReceived;

    UNREFERENCED_PARAMETER(Argc);
    UNREFERENCED_PARAMETER(Argv);

    //
    // Find a device connected to the system that has WinUSB installed using our
    // INF
    //
    hr = OpenDevice(&deviceData, &noDevice);

    if (FAILED(hr)) {

        if (noDevice) {

            wprintf(L"Device not connected or driver not installed\n");

        } else {

            wprintf(L"Failed looking for device, HRESULT 0x%x\n", hr);
        }

        return 0;
    }

    //
    // Get device descriptor
    //
    bResult = WinUsb_GetDescriptor(deviceData.WinusbHandle,
                                   USB_DEVICE_DESCRIPTOR_TYPE,
                                   0,
                                   0,
                                   (PBYTE) &deviceDesc,
                                   sizeof(deviceDesc),
                                   &lengthReceived);

    if (FALSE == bResult || lengthReceived != sizeof(deviceDesc)) {

        wprintf(L"Error among LastError %d or lengthReceived %d\n",
                FALSE == bResult ? GetLastError() : 0,
                lengthReceived);
        CloseDevice(&deviceData);
        return 0;
    }

    //
    // Print a few parts of the device descriptor
    //
    wprintf(L"Device found: VID_%04X&PID_%04X; bcdUsb %04X\n",
            deviceDesc.idVendor,
            deviceDesc.idProduct,
            deviceDesc.bcdUSB);

        // Call the GetIsochPipes function
    hr = GetIsochPipes(&deviceData);

    if (SUCCEEDED(hr)) 
    {
        // printf("Isoch Out Pipe ID: %d\n", deviceData.IsochOutPipe);
        // printf("Isoch In Pipe ID: %d\n", deviceData.IsochInPipe);
        SendIsochInTransfer(&deviceData, FALSE);  // <--- `True` if using ASAP.
    } 
    else 
    {
        printf("GetIsochPipes failed with HRESULT: 0x%08X\n", hr);
    }

    CloseDevice(&deviceData);
    return 0;
}

This is the output I get:

F:\AI_Componets\WinUSB\main_test_legit>main.exe
Device found: VID_EBA4&PID_7588; bcdUsb 0200
Isoch In Pipe ID: 130
Isochronous In Pipe Transfer Size: 24576
Isochronous In Pipe Packet Count: 8


Read transfer.
totalTransferSize: 49152

Transfer size must end at a frame boundary.

Seems like the device path is wrong since I use this device path for FFMPEG:
usb#vid_eba4&pid_7588&mi_02#6&63de0d9&0&0002#{D3CACD43-D2AF-4589-8A20-7B1BB208C475}

Your code can't actually look like that. Your third line shows that IsochInTransferSize is 24576, then you compute totalTransferSize to be that value times 1, but it prints totalTransferSize: 49152.

Then, you're checking IsochInBytesPerFrame, which you have not initialized. And you don't need to initialize it, because that's what isochInTransferSize tells you.

There's really no point in tracking the frame number. Just use ASAP. Also, you will need to have several requests outstanding at once. By the time your completed request gets back to you, you've already missed the next frame.

Seriously, you should let the stock usbvideo driver handle this. You're not going to a better job. UVC cameras require a capabilities negotiation ("probe/commit") that you don't have here.

1 Like

Hello @Tim_Roberts,

Thanks for the reply, much obliged.

This is not my code, if it was it surely wouldn't look like that.
98% of the code and it's structure flow was obtained from examples and describings from MS docs.

I only contributed of 2% in the code, which fixed many bugs and also giving more detailed error outputs. Who ever is composing these examples for Microsoft are clearly not doing a great job on it and possibly the cause of WinUSB driver bugs:

I know you don't believe but here are the links for references (Need to google it since the forum does not allow me to post web links):

  1. Write a Windows desktop app based on the WinUSB template:

  2. Send USB isochronous transfers from a WinUSB desktop app:

Sorry, I gave the wrong program output for my previous post, it doesn't match to the supplied source code I gave.

Here is the correct output, this matches to the source code of my previous post, as you expect, the IsochInTransferSize is consistently the same:

F:\AI_Componets\WinUSB\main_test_legit>main.exe

Device found: VID_EBA4&PID_7588; bcdUsb 0200

Isoch In Pipe ID: 130
Isochronous In Pipe Transfer Size: 24576
Isochronous In Pipe Packet Count: 8

Read transfer.
totalTransferSize: 24576

Transfer size must end at a frame boundary.

Thanks for this feedback. IsochInBytesPerFrame code implementation was provided by MS Docs example, I will remove it.

Thanks for this feedback. Both WinUsb_ReadIsochPipeAsap and WinUsb_ReadIsochPipe was included in the MS docs. I will remove WinUsb_ReadIsochPipe which uses the frame tracking.

Thanks for this feedback. I am going to assume here what this means, I remember reading the MS docs for WinUsb_ReadIsochPipeAsap function usage, it mentioned something confusing about "Continue Stream", I think you are referring this:?

If the caller sets ContinueStream to TRUE, The transfer fails if Winusb.sys is unable to schedule the transfer to continue the stream without dropping one or more frames.

Are there any examples of implementing several requests outstanding at once? I will work on this and see what I come up with my own.

I have no choice and need to have the path as efficient and directly accessible by the GPU. Is it possible I can write code using UVC API and are there any examples for only reading isochronous packets?

Alright so I need to implement negotiation ("probe/commit"), it won't be too difficult, if there are examples it would speed things up in getting this project accomplished.

The output I now get after removing IsochInBytesPerFrame:

F:\AI_Componets\WinUSB\main_test_legit>main.exe

Device found: VID_EBA4&PID_7588; bcdUsb 0200
Isoch In Pipe ID: 130
Isochronous In Pipe Transfer Size: 24576
Isochronous In Pipe Packet Count: 8


Read transfer.
totalTransferSize: 24576

Read transfer sent by using ASAP flag.
Failed to start a read operation with error 57
Requested 24576 bytes in 8 packets per transfer.
Transfer 1 completed. Read 1571397510 bytes.

Failed to unregister isoch read buffer.

This error code 57 means The parameter is incorrect for WinUsb_ReadIsochPipeAsap. I'm assuming it has to do something with the Continue Stream requiring several requests outstanding at once could need to implement negotiation ("probe/commit")?.

Also, forgot to mention in my previous post, I needed to implement two WINUSB_INTERFACE_HANDLE in the struct _DEVICE_DATA in order to access the Isochronous IN-Endpoint alternate interface settings:

WINUSB_INTERFACE_HANDLE WinusbHandle;
WINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle; 

I would need to supply the AssociatedInterfaceHandle interface handle to the WinUsb_RegisterIsochBuffer() function right? It also gives error 57.

Also shouldn't the totalTransferSize be an "EVEN" multiple of the max packets per frame? In your previous post, you've mentioned:

> packet size is actually 3072 bytes. That is the maximum for a USB 2 isochronous pipe -- 24MB/s.

So this means the max packet size is 24,576 and the totalTransferSize has to be an "EVEN" multiple to it? I got this info from one of your posts in OSR of a thread called "WinUSB: WinUsb_UnregisterIsochBuffer(): “Element not found” [0x490]"

Thanks for advice.

packet size is actually 3072 bytes. That is the maximum for a USB 2 isochronous pipe -- 24MB/s.***

So this means the max packet size is 24,576 and the totalTransferSize has to be an "EVEN" multiple to it?

The max packet size is 3,072 bytes. Your device will get one timeslot in each microframe, and 3,072 is the most you can do in that microframe. That's the hardware level. At a software level, all isoch requests have to span an entire frame, which is 8 microframes. so your minimum TRANSFER is 24,576, which will consist of 8 packets of 3,072. The packets are all treated separately.

I have no choice and need to have the path as efficient and directly accessible by the GPU.

Don't give me that nonsense. Of COURSE you have a choice! You are committing the sin of premature optimization here. You are embarking on an utterly senseless course of action, without having a SINGLE CLUE whether the preferred, simple, well-tested and well-understood mechanism will solve your problem, because you haven't even tried it. Can you see why I might be frustrated?

Is it possible I can write code using UVC API and are there any examples for only reading isochronous packets?

UVCVIDEO is a Kernel Streaming driver, which understands the Kernel Streaming ioctls. There are COM interfaces to access those. But, as with all kernel interfaces, the interface is abstracted. You don't work with USB or packets at all. You feed in a stream of buffers. UVCVIDEO creates a series of USB requests of the appropriate size and submits them to the hardware.

In your case, you can't avoid a copy. This is just a fundamental fact that you need to embed in your psyche. You're obsessed with a genie that cannot be defeated. Remember that a typical processor today can copy a 100kB image in a dozen microseconds. This copy is not going to be a stumbling block.

Hello @Tim_Roberts Thanks for the reply.

Thanks for this clarification.

"The 'sin of premature optimization' (SPO) is why DeepSeek has caused the entire tech space to lose trillions in stock funds in a matter of days , because they specifically attempted SPO—something which folks like Tim strangely demise for some reason.
They redefined the norm and realm of primitive programming backend from CUDA down to PTX (assembly language), which startled the 'magnificent 7 ' (Honestly, I find it phenomenally laughable how the 'magnificent 7' are 'shocked' by DeepSeek's 'breakthroughs'; it's too obvious). Then you have our western friends not admiring their marvelous accomplishment in embarking on the SPO route using PTX (assembly language) to program cutting-edge, insanely complicated state-of-the-art AI models, but rather disavowing it.
Honestly, it's been over 6 years since I've been telling reluctant programmers about the crucial importance of low-level programming and optimizations to be integrated into AI, but no one listens (except for DeepSeek), and they keep using slow, primitive programming methods until massive stocks go down in parallel . You have to seriously wait and think for a minute; SPOs can turn something that costs $600 million (your common go-to ChatBots, which use Python and CUDA) down to 99%, costing only $6 million (DeepSeek, which uses C and C++ and PTX, aka the 'SPO')."

I just hope we are clear on this: I never said I wanted to avoid making a copy (I already knew that this is an impossible mission to accomplish with primitive APIs such as WinUSB, UVC, and KMDF). What I wanted to avoid is making multiple copies.

If there is only 1 copy (USB to Buffer (DMA) and then Buffer to GPU), this is absolutely sufficient for now... If you have ideas, please share; I'm willing to try them. You mentioned previously that DirectShow uses UVC such that data is DMAed into the buffer and DirectShow directly accesses the buffer data. I can't find much information on this.

However, if there are 2 or more copies (USB to Buffer [DMA], then Buffer to another Buffer, and then Buffer to GPU finally), this clearly requires me to embark on a journey of SPO.

I was hoping that WinUSB would be able to DMA into the buffer and then I could do 1 copy from the buffer to the GPU; this is acceptable. However, it seems you repudiate WinUSB and would prefer me to use UVC. I'm open to all options if they can ensure only 1 copy.

I would like to say thanks again for your reply.

For the time being, I am debugging WinUSB, it seems that the memory address for DeviceHandle seems to be incorrect, right?:

HandlesOpen	1	int
WinusbHandle	0x00000191aed71150	void *
AssociatedInterfaceHandle	0x00000191aed72580	void *
DeviceHandle	0x00000000000000ec	void *

Also pipe object seems to contain the wrong value for MaximumPacketSize it knows that there are bit fields in there and why does it not have the ASCII termination ?:

PipeType	UsbdPipeTypeIsochronous (1)	_USBD_PIPE_TYPE
PipeId	130 '‚'	unsigned char
MaximumPacketSize	5120	unsigned short
Interval	1 '\x1'	unsigned char
MaximumBytesPerInterval	3072	unsigned long

A DeviceHandle of 0xec is fine. Operating system file handles have small integer values.

The MaximumPacketSize field is copied directly from wMaxPacketSize in the ENDPOINT_DESCRIPTOR. The "interpreted" value is provided by MaximumBytesPerInterval.

I don't know what you mean by "ASCII termination". Nothing there is in ASCII.

1 Like

At least comparing anything that is being done by any so called AI software to system programming of any kind is an apples to oranges comparison. The objectives and consequences of failure are entirely different.

And the choice of language and programming environment is not by itself an optimization. Certainly not a premature one, since these choices define the paradigm of an entire project

I don't think you will get any arguments from anyone on this forum about the importance of 'low-level' optimizations. System programming frequently depends on such optimizations to produce effective results - including everything from bitwise operations, to the use of linear address, lock elision & memory barriers, or branch prediction compatible code generation; there probably isn't something that you can suggest that hasn't been discussed here at least once.

The premature part comes in when when you have yet to make something work at all. And until you do, it is very hard to even estimate which parts are expensive. And deciding that you do know which parts need to be done a certain way because that way must be better, before you have a way to measure or at least estimate what the performance of different options will be is the definition of premature optimization. Because that kind of work does not make better / faster / cheaper things, but it does consume a lot of effort

None of this pertains to your direct question, but I hope it helps you to understand Tim's comments and concerns

Hello Tim, thanks for the reply.

Well I used WinDbg and have pinpointed to where exactly the error is being thrown at:

Here is the WinUSB's disassembled source code (most likely from WinUSB.dll?) for WINUSB!WinUsb_ReadIsochPipeInternal function logic:

WINUSB!WinUsb_ReadIsochPipeInternal:
00007fff`a9d43dcc 4053             push    rbx
00007fff`a9d43dce 56               push    rsi
00007fff`a9d43dcf 57               push    rdi
00007fff`a9d43dd0 4154             push    r12
00007fff`a9d43dd2 4155             push    r13
00007fff`a9d43dd4 4156             push    r14
00007fff`a9d43dd6 4881ecc8000000   sub     rsp, 0C8h
00007fff`a9d43ddd 488b0524420000   mov     rax, qword ptr [WINUSB!__security_cookie (7fffa9d48008)]
00007fff`a9d43de4 4833c4           xor     rax, rsp
00007fff`a9d43de7 48898424b0000000 mov     qword ptr [rsp+0B0h], rax
00007fff`a9d43def 44894c244c       mov     dword ptr [rsp+4Ch], r9d
00007fff`a9d43df4 458be8           mov     r13d, r8d
00007fff`a9d43df7 89542448         mov     dword ptr [rsp+48h], edx
00007fff`a9d43dfb 488bf9           mov     rdi, rcx
00007fff`a9d43dfe 4c8bb42428010000 mov     r14, qword ptr [rsp+128h]
00007fff`a9d43e06 488b842438010000 mov     rax, qword ptr [rsp+138h]
00007fff`a9d43e0e 4889442458       mov     qword ptr [rsp+58h], rax
00007fff`a9d43e13 4c8ba42440010000 mov     r12, qword ptr [rsp+140h]
00007fff`a9d43e1b 0f57c0           xorps   xmm0, xmm0
00007fff`a9d43e1e 0f11442468       movups  xmmword ptr [rsp+68h], xmm0
00007fff`a9d43e23 0f11442478       movups  xmmword ptr [rsp+78h], xmm0
00007fff`a9d43e28 4585c0           test    r8d, r8d
00007fff`a9d43e2b 0f8499020000     je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (7fffa9d440ca)
00007fff`a9d43e31 4885c9           test    rcx, rcx
00007fff`a9d43e34 0f8490020000     je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (7fffa9d440ca)
00007fff`a9d43e3a 8bb42430010000   mov     esi, dword ptr [rsp+130h]
00007fff`a9d43e41 85f6             test    esi, esi
00007fff`a9d43e43 0f8481020000     je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (7fffa9d440ca)
00007fff`a9d43e49 83bc242001000000 cmp     dword ptr [rsp+120h], 0
00007fff`a9d43e51 7509             jne     WINUSB!WinUsb_ReadIsochPipeInternal+0x90 (7fffa9d43e5c)
00007fff`a9d43e53 4d85f6           test    r14, r14
00007fff`a9d43e56 0f846e020000     je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (7fffa9d440ca)
00007fff`a9d43e5c ba50000000       mov     edx, 50h
00007fff`a9d43e61 e872f7ffff       call    WINUSB!WinUsb_ValidateBuffer (7fffa9d435d8)
00007fff`a9d43e66 8bd8             mov     ebx, eax
00007fff`a9d43e68 89442440         mov     dword ptr [rsp+40h], eax
00007fff`a9d43e6c 85c0             test    eax, eax
00007fff`a9d43e6e 7516             jne     WINUSB!WinUsb_ReadIsochPipeInternal+0xba (7fffa9d43e86)
00007fff`a9d43e70 b957000000       mov     ecx, 57h
00007fff`a9d43e75 48ff15b4220000   call    qword ptr [WINUSB!__imp_SetLastError (7fffa9d46130)]
00007fff`a9d43e7c 0f1f440000       nop     dword ptr [rax+rax]
00007fff`a9d43e81 e95b020000       jmp     WINUSB!WinUsb_ReadIsochPipeInternal+0x315 (7fffa9d440e1)
00007fff`a9d43e86 48397f10         cmp     qword ptr [rdi+10h], rdi
00007fff`a9d43e8a 7408             je      WINUSB!WinUsb_ReadIsochPipeInternal+0xc8 (7fffa9d43e94)
00007fff`a9d43e8c 33db             xor     ebx, ebx
00007fff`a9d43e8e 895c2440         mov     dword ptr [rsp+40h], ebx
00007fff`a9d43e92 ebdc             jmp     WINUSB!WinUsb_ReadIsochPipeInternal+0xa4 (7fffa9d43e70)
00007fff`a9d43e94 81fe55555515     cmp     esi, 15555555h
00007fff`a9d43e9a 77f0             ja      WINUSB!WinUsb_ReadIsochPipeInternal+0xc0 (7fffa9d43e8c)
00007fff`a9d43e9c 8d0476           lea     eax, [rsi+rsi*2]
00007fff`a9d43e9f c1e002           shl     eax, 2
00007fff`a9d43ea2 89442454         mov     dword ptr [rsp+54h], eax
00007fff`a9d43ea6 8bd0             mov     edx, eax
00007fff`a9d43ea8 488b4c2458       mov     rcx, qword ptr [rsp+58h]
00007fff`a9d43ead e826f7ffff       call    WINUSB!WinUsb_ValidateBuffer (7fffa9d435d8)
00007fff`a9d43eb2 8bd8             mov     ebx, eax
00007fff`a9d43eb4 89442440         mov     dword ptr [rsp+40h], eax
00007fff`a9d43eb8 85c0             test    eax, eax
00007fff`a9d43eba 74b4             je      WINUSB!WinUsb_ReadIsochPipeInternal+0xa4 (7fffa9d43e70)
00007fff`a9d43ebc 4c8b4708         mov     r8, qword ptr [rdi+8]
00007fff`a9d43ec0 4889bc2490000000 mov     qword ptr [rsp+90h], rdi
00007fff`a9d43ec8 448b542448       mov     r10d, dword ptr [rsp+48h]
00007fff`a9d43ecd 4489942498000000 mov     dword ptr [rsp+98h], r10d
00007fff`a9d43ed5 4489ac249c000000 mov     dword ptr [rsp+9Ch], r13d
00007fff`a9d43edd 8a4718           mov     al, byte ptr [rdi+18h]
00007fff`a9d43ee0 88842488000000   mov     byte ptr [rsp+88h], al
00007fff`a9d43ee7 0fb65719         movzx   edx, byte ptr [rdi+19h]
00007fff`a9d43eeb 88942489000000   mov     byte ptr [rsp+89h], dl
00007fff`a9d43ef2 837c244c00       cmp     dword ptr [rsp+4Ch], 0
00007fff`a9d43ef7 0f958424a1000000 setne   byte ptr [rsp+0A1h]
00007fff`a9d43eff 83bc242001000000 cmp     dword ptr [rsp+120h], 0
00007fff`a9d43f07 0f958424a0000000 setne   byte ptr [rsp+0A0h]
00007fff`a9d43f0f 89b424a8000000   mov     dword ptr [rsp+0A8h], esi
00007fff`a9d43f16 8bca             mov     ecx, edx
00007fff`a9d43f18 83e10f           and     ecx, 0Fh
00007fff`a9d43f1b 488d4110         lea     rax, [rcx+10h]
00007fff`a9d43f1f 84d2             test    dl, dl
00007fff`a9d43f21 480f48c8         cmovs   rcx, rax
00007fff`a9d43f25 488d1489         lea     rdx, [rcx+rcx*4]
00007fff`a9d43f29 41807c901400     cmp     byte ptr [r8+rdx*4+14h], 0
00007fff`a9d43f2f 0f8495010000     je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (7fffa9d440ca)

From the above disassembled code, the program eventually goes all the way down to here at the compare operation which causes a jump for error to be thrown:

00007fff`a9d43f29 41807c901400     cmp     byte ptr [r8+rdx*4+14h], 0

The program jumps here for throwing the infamous 0x57 (87) error code:

00007fff`a9d440ca b957000000       mov     ecx, 57h
00007fff`a9d440cf 48ff155a200000   call    qword ptr [WINUSB!__imp_SetLastError (7fffa9d46130)]
00007fff`a9d440d6 0f1f440000       nop     dword ptr [rax+rax]

Here is the computed operation steps the program took once it entered into the WinUsb_ReadIsochPipeAsap function:

WINUSB!WinUsb_ReadIsochPipeAsap+0x37:
00007fff`a9d44167 e860fcffff      call    WINUSB!WinUsb_ReadIsochPipeInternal (00007fff`a9d43dcc)
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal:
00007fff`a9d43dcc 4053            push    rbx
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x2:
00007fff`a9d43dce 56              push    rsi
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x3:
00007fff`a9d43dcf 57              push    rdi
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x4:
00007fff`a9d43dd0 4154            push    r12
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x6:
00007fff`a9d43dd2 4155            push    r13
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x8:
00007fff`a9d43dd4 4156            push    r14
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xa:
00007fff`a9d43dd6 4881ecc8000000  sub     rsp,0C8h
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x11:
00007fff`a9d43ddd 488b0524420000  mov     rax,qword ptr [WINUSB!_security_cookie (00007fff`a9d48008)] ds:00007fff`a9d48008=0000795ef7bf52ea
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x18:
00007fff`a9d43de4 4833c4          xor     rax,rsp
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x1b:
00007fff`a9d43de7 48898424b0000000 mov     qword ptr [rsp+0B0h],rax ss:00000032`142ff1b0=0000000000000024
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x23:
00007fff`a9d43def 44894c244c      mov     dword ptr [rsp+4Ch],r9d ss:00000032`142ff14c=00000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x28:
00007fff`a9d43df4 458be8          mov     r13d,r8d
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x2b:
00007fff`a9d43df7 89542448        mov     dword ptr [rsp+48h],edx ss:00000032`142ff148=00000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x2f:
00007fff`a9d43dfb 488bf9          mov     rdi,rcx
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x32:
00007fff`a9d43dfe 4c8bb42428010000 mov     r14,qword ptr [rsp+128h] ss:00000032`142ff228=0000000000000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x3a:
00007fff`a9d43e06 488b842438010000 mov     rax,qword ptr [rsp+138h] ss:00000032`142ff238=000001a93f35f370
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x42:
00007fff`a9d43e0e 4889442458      mov     qword ptr [rsp+58h],rax ss:00000032`142ff158=000001a93f35f370
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x47:
00007fff`a9d43e13 4c8ba42440010000 mov     r12,qword ptr [rsp+140h] ss:00000032`142ff240=0000000000000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x4f:
00007fff`a9d43e1b 0f57c0          xorps   xmm0,xmm0
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x52:
00007fff`a9d43e1e 0f11442468      movups  xmmword ptr [rsp+68h],xmm0 ss:00000032`142ff168=00000000000000000000000000000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x57:
00007fff`a9d43e23 0f11442478      movups  xmmword ptr [rsp+78h],xmm0 ss:00000032`142ff178=000001a93f3554d000007ffef18e90c0
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x5c:
00007fff`a9d43e28 4585c0          test    r8d,r8d
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x5f:
00007fff`a9d43e2b 0f8499020000    je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (00007fff`a9d440ca) [br=0]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x65:
00007fff`a9d43e31 4885c9          test    rcx,rcx
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x68:
00007fff`a9d43e34 0f8490020000    je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (00007fff`a9d440ca) [br=0]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x6e:
00007fff`a9d43e3a 8bb42430010000  mov     esi,dword ptr [rsp+130h] ss:00000032`142ff230=00000008
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x75:
00007fff`a9d43e41 85f6            test    esi,esi
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x77:
00007fff`a9d43e43 0f8481020000    je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (00007fff`a9d440ca) [br=0]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x7d:
00007fff`a9d43e49 83bc242001000000 cmp     dword ptr [rsp+120h],0 ss:00000032`142ff220=00000001
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x85:
00007fff`a9d43e51 7509            jne     WINUSB!WinUsb_ReadIsochPipeInternal+0x90 (00007fff`a9d43e5c) [br=1]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x90:
00007fff`a9d43e5c ba50000000      mov     edx,50h
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x95:
00007fff`a9d43e61 e872f7ffff      call    WINUSB!WinUsb_ValidateBuffer (00007fff`a9d435d8)
0:000> t
WINUSB!WinUsb_ValidateBuffer:
00007fff`a9d435d8 8a01            mov     al,byte ptr [rcx] ds:000001a9`3f35b280=50
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x2:
00007fff`a9d435da 88442408        mov     byte ptr [rsp+8],al ss:00000032`142ff100=30
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x6:
00007fff`a9d435de ffca            dec     edx
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x8:
00007fff`a9d435e0 4803d1          add     rdx,rcx
0:000> t
WINUSB!WinUsb_ValidateBuffer+0xb:
00007fff`a9d435e3 4889542418      mov     qword ptr [rsp+18h],rdx ss:00000032`142ff110=0000000000000000
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x10:
00007fff`a9d435e8 8a02            mov     al,byte ptr [rdx] ds:000001a9`3f35b2cf=00
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x12:
00007fff`a9d435ea 88442408        mov     byte ptr [rsp+8],al ss:00000032`142ff100=50
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x16:
00007fff`a9d435ee b801000000      mov     eax,1
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x1b:
00007fff`a9d435f3 c3              ret
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x9a:
00007fff`a9d43e66 8bd8            mov     ebx,eax
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x9c:
00007fff`a9d43e68 89442440        mov     dword ptr [rsp+40h],eax ss:00000032`142ff140=142ff268
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xa0:
00007fff`a9d43e6c 85c0            test    eax,eax
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xa2:
00007fff`a9d43e6e 7516            jne     WINUSB!WinUsb_ReadIsochPipeInternal+0xba (00007fff`a9d43e86) [br=1]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xba:
00007fff`a9d43e86 48397f10        cmp     qword ptr [rdi+10h],rdi ds:000001a9`3f35b290=000001a93f35b280
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xbe:
00007fff`a9d43e8a 7408            je      WINUSB!WinUsb_ReadIsochPipeInternal+0xc8 (00007fff`a9d43e94) [br=1]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xc8:
00007fff`a9d43e94 81fe55555515    cmp     esi,15555555h
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xce:
00007fff`a9d43e9a 77f0            ja      WINUSB!WinUsb_ReadIsochPipeInternal+0xc0 (00007fff`a9d43e8c) [br=0]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xd0:
00007fff`a9d43e9c 8d0476          lea     eax,[rsi+rsi*2]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xd3:
00007fff`a9d43e9f c1e002          shl     eax,2
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xd6:
00007fff`a9d43ea2 89442454        mov     dword ptr [rsp+54h],eax ss:00000032`142ff154=00000060
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xda:
00007fff`a9d43ea6 8bd0            mov     edx,eax
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xdc:
00007fff`a9d43ea8 488b4c2458      mov     rcx,qword ptr [rsp+58h] ss:00000032`142ff158=000001a93f35f370
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xe1:
00007fff`a9d43ead e826f7ffff      call    WINUSB!WinUsb_ValidateBuffer (00007fff`a9d435d8)
0:000> t
WINUSB!WinUsb_ValidateBuffer:
00007fff`a9d435d8 8a01            mov     al,byte ptr [rcx] ds:000001a9`3f35f370=00
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x2:
00007fff`a9d435da 88442408        mov     byte ptr [rsp+8],al ss:00000032`142ff100=00
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x6:
00007fff`a9d435de ffca            dec     edx
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x8:
00007fff`a9d435e0 4803d1          add     rdx,rcx
0:000> t
WINUSB!WinUsb_ValidateBuffer+0xb:
00007fff`a9d435e3 4889542418      mov     qword ptr [rsp+18h],rdx ss:00000032`142ff110=000001a93f35b2cf
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x10:
00007fff`a9d435e8 8a02            mov     al,byte ptr [rdx] ds:000001a9`3f35f3cf=00
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x12:
00007fff`a9d435ea 88442408        mov     byte ptr [rsp+8],al ss:00000032`142ff100=00
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x16:
00007fff`a9d435ee b801000000      mov     eax,1
0:000> t
WINUSB!WinUsb_ValidateBuffer+0x1b:
00007fff`a9d435f3 c3              ret
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xe6:
00007fff`a9d43eb2 8bd8            mov     ebx,eax
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xe8:
00007fff`a9d43eb4 89442440        mov     dword ptr [rsp+40h],eax ss:00000032`142ff140=00000001
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xec:
00007fff`a9d43eb8 85c0            test    eax,eax
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xee:
00007fff`a9d43eba 74b4            je      WINUSB!WinUsb_ReadIsochPipeInternal+0xa4 (00007fff`a9d43e70) [br=0]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xf0:
00007fff`a9d43ebc 4c8b4708        mov     r8,qword ptr [rdi+8] ds:000001a9`3f35b288=000001a93f35ef70
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xf4:
00007fff`a9d43ec0 4889bc2490000000 mov     qword ptr [rsp+90h],rdi ss:00000032`142ff190={Project1!__6F6472C1_limits (00007ff6`faa87000)}
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0xfc:
00007fff`a9d43ec8 448b542448      mov     r10d,dword ptr [rsp+48h] ss:00000032`142ff148=00000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x101:
00007fff`a9d43ecd 4489942498000000 mov     dword ptr [rsp+98h],r10d ss:00000032`142ff198=00000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x109:
00007fff`a9d43ed5 4489ac249c000000 mov     dword ptr [rsp+9Ch],r13d ss:00000032`142ff19c=00006000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x111:
00007fff`a9d43edd 8a4718          mov     al,byte ptr [rdi+18h] ds:000001a9`3f35b298=01
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x114:
00007fff`a9d43ee0 88842488000000  mov     byte ptr [rsp+88h],al ss:00000032`142ff188=01
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x11b:
00007fff`a9d43ee7 0fb65719        movzx   edx,byte ptr [rdi+19h] ds:000001a9`3f35b299=82
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x11f:
00007fff`a9d43eeb 88942489000000  mov     byte ptr [rsp+89h],dl ss:00000032`142ff189=82
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x126:
00007fff`a9d43ef2 837c244c00      cmp     dword ptr [rsp+4Ch],0 ss:00000032`142ff14c=00000000
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x12b:
00007fff`a9d43ef7 0f958424a1000000 setne   byte ptr [rsp+0A1h] ss:00000032`142ff1a1=f2
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x133:
00007fff`a9d43eff 83bc242001000000 cmp     dword ptr [rsp+120h],0 ss:00000032`142ff220=00000001
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x13b:
00007fff`a9d43f07 0f958424a0000000 setne   byte ptr [rsp+0A0h] ss:00000032`142ff1a0=50
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x143:
00007fff`a9d43f0f 89b424a8000000  mov     dword ptr [rsp+0A8h],esi ss:00000032`142ff1a8=faa73f0c
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x14a:
00007fff`a9d43f16 8bca            mov     ecx,edx
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x14c:
00007fff`a9d43f18 83e10f          and     ecx,0Fh
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x14f:
00007fff`a9d43f1b 488d4110        lea     rax,[rcx+10h]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x153:
00007fff`a9d43f1f 84d2            test    dl,dl
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x155:
00007fff`a9d43f21 480f48c8        cmovs   rcx,rax
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x159:
00007fff`a9d43f25 488d1489        lea     rdx,[rcx+rcx*4]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x15d:
00007fff`a9d43f29 41807c901400    cmp     byte ptr [r8+rdx*4+14h],0 ds:000001a9`3f35f0ec=00
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x163:
00007fff`a9d43f2f 0f8495010000    je      WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe (00007fff`a9d440ca) [br=1]
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x2fe:
00007fff`a9d440ca b957000000      mov     ecx,57h
0:000> t
WINUSB!WinUsb_ReadIsochPipeInternal+0x303:
00007fff`a9d440cf 48ff155a200000  call    qword ptr [WINUSB!_imp_SetLastError (00007fff`a9d46130)] ds:00007fff`a9d46130={ntdll!RtlSetLastWin32Error (00007fff`d3a618d0)}
0:000> t
ntdll!RtlSetLastWin32Error:
00007fff`d3a618d0 894c2408        mov     dword ptr [rsp+8],ecx ss:00000032`142ff100=142ff100

Seems like it could be a validation buffer issue, but it passed two buffer validation tests. I could provide the register results but didn't want to make the post seem too complicated. Most likely an Invalid Pipe or Endpoint Configuration or Invalid Flags or Options. So I guess I might need to enable Kernel mode debugging to possibly get more human readable clues, right now all the machine code is not making clear sense to me what is causing the error. I hope there is a way to obtain the Source Code of the WinUSB.dll file and it would be an easy understanding what is causing the 0x57 error.

Thanks again.

I'm pretty sure I see the issue. Note that it is indexing into a table based on ecx, which is only the low order 4 bits of something. That is almost certainly the endpoint number (2 from your 0x82).

You've had a lot of code here, and maybe I missed it, but I don't see the call to WinUsb_SetCurrentAlternateSetting, which is what turns on the isochronous data. I see you QUERYING the interface, which tells you what would be available in that setting, but if you don't select it, those endpoints aren't live. By default, you'll get setting 0, whiich has no endpoints. So, it tries to look up endpoint 2, and there's nothing there.

So, add WinUsb_SetCurrentAlternateSetting(associatedInterfaceHandle, 1);.

1 Like

Hello @Tim_Roberts, thanks for the reply.

Had no clue I had to utilize WinUsb_SetCurrentAlternateSetting didn't see it in the MS examples or in the MS docs or maybe it was mentioned only once and never caught it.

Anyhow, it took me 2 weeks until now to finally get this simple 0x57 error finally resolved thanks to your expertise:

F:\AI_Componets\WinUSB\main_test_legit>main.exe

Device found: VID_EBA4&PID_7588; bcdUsb 0200

Initiating: `WinUsb_GetAssociatedInterface`
`WinUsb_GetAssociatedInterface` has been Initiated.

Initiating: `WinUsb_QueryInterfaceSettings`
`WinUsb_QueryInterfaceSettings` has been Initiated.

Initiating: `WinUsb_SetCurrentAlternateSetting`
`WinUsb_SetCurrentAlternateSetting` has been Initiated.

Isochronous `IN` Pipe ID: [ 130 ]
Isochronous `IN` Pipe Transfer Size: 24576
Isochronous `IN` Pipe Packet Count: 8


Read transfer.
totalTransferSize: 24576

Initiating: `WinUsb_RegisterIsochBuffer`
`WinUsb_SetCurrentAlternateSetting` has been Initiated.

Isoch Read Buffer Handle: 000001F44C604790

Initiating: `WinUsb_ReadIsochPipeAsap`
`WinUsb_ReadIsochPipeAsap` has been Initiated.

Thanks again you're a real MVP. Now I'll get busy and see if I can "speed" things up here.