android_usbaudio
基于libusb,实现无驱动获取USBAudio
期望实现的功能:
- 通过libusb获取USBAudio数据,无需SELinux声卡权限
部分摄像头无法获取音频问题解决思路:
- 无法获取音频的原因:当前传过去的采样率在设备当前选择的Audio Interface中不存在!
- 描述:我们打开的设备声卡通道它当前自己有一个固定的采样率,主机这边传过去的采样率和设备当前的端点的采样率不匹配,就会导致拿不到音频数据
- 如:设备当前采样率为32000,主机传过去的采样率为48000,就会导致拿去不到音频数据
- 罗技4K无声音:当前选择bAlternateSetting的Audio Interface中采样率为48000HZ,然而主机传递传递过去想要设置的采样率在当前Audio Interface下不存在;
- RAPOO 4K无声音是因为在当前的Audio Interface下,不存在输入的采样率
获取设备采样率、通道数、bit
- 获取当前libusb_interface_descriptor下的extra
- 解析extra,可获取到当前interface的通道数,采样率等;
- c++ 设置usbAudio采样率,代码如下:
int USBAudio::scan_audio_interface(libusb_device *usbDev) {
int r = 0;
r = libusb_get_config_descriptor(usbDev, 0, &uac_config);
LOGD("scan_audio_interface");
for (int interface_idx = 0; interface_idx < uac_config->bNumInterfaces; interface_idx++) {
i_face = &uac_config->interface[interface_idx];
if (i_face->altsetting->bInterfaceClass != LIBUSB_CLASS_AUDIO/*1*/) {// Audio, Control
continue;
}
LOGD("scan_audio_interface :%d", i_face->num_altsetting);
for (int i = 0; i < i_face->num_altsetting; ++i) {
if_desc = &i_face->altsetting[i];
switch (if_desc->bInterfaceSubClass) {
case 1:
_controlInterface = if_desc->bInterfaceNumber;
break;
case 2:
if (if_desc->bNumEndpoints) {
auto endpoint = if_desc->endpoint;
if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) ==
LIBUSB_ENDPOINT_IN) {
LOGD("set _speakerInterface ");
//获取 FORMAT_TYPE_I 相关数据
set_audio_stream_desc(if_desc);
//赋值
_speakerInterface = if_desc->bInterfaceNumber;
_alternateSetting = if_desc->bAlternateSetting;
_speakerEndpoint = endpoint->bEndpointAddress;
mMaxPacketSize = endpoint->wMaxPacketSize;
LOGD(" _speakerInterface %d _controlInterface %d mMaxPacketSize %d _alternateSetting %d\n",
_speakerInterface,
_controlInterface,
mMaxPacketSize,
_alternateSetting);
}
}
break;
}
}
}
libusb_free_config_descriptor(uac_config);
return r;
}
- 设置usbAudio采样率,代码如下:
int USBAudio::set_sample_rate_v1(int rate) {
unsigned char data[3];
int ret, crate;
data[0] = (rate & 0xff);
data[1] = (rate >> 8);
data[2] = (rate >> 16);
ret = libusb_control_transfer(uac_devh,
USB_REQ_CS_ENDPOINT_SET,
UAC_SET_CUR,
0x0100,
_speakerEndpoint,
data, sizeof(data), 500);
if (ret < 0) {
LOGD("%d:%d: cannot set freq %d to ep %#x\n",
_speakerInterface, _alternateSetting, rate, _speakerEndpoint);
return ret;
}
ret = libusb_control_transfer(uac_devh,
USB_REQ_CS_ENDPOINT_GET,
UAC_GET_CUR,
0x0100,
_speakerEndpoint,
data, sizeof(data), 500);
if (ret < 0) {
LOGD("%d:%d: cannot get freq at ep %#x\n",
_speakerInterface, _alternateSetting, _speakerEndpoint);
/* some devices don't support reading */
}
crate = data[0] | (data[1] << 8) | (data[2] << 16);
LOGD("host rate is %d ,device rate is %d\n", rate, crate);
if (!crate) {
LOGD("failed to read current rate; disabling the check\n");
return 0;
}
if (crate != rate) {
LOGD("current rate %d is different from the runtime rate %d\n", crate, rate);
// runtime->rate = crate;
}
return 0;
}
问题记录
记录在开发这个项目时,遇到的问题
- 使用libusb_control_transfer,报错LIBUSB_ERROR_PIPE()
- 原因是当前传过去的参数错误,如:bmRequestType:(LIBUSB_ENDPOINT_OUT |LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_ENDPOINT),wIndex:(EndpointAddress);并且部分摄像头必须要求设置采样率
- 如何设置采样率
- UAC协议中,采样率数据长度为3,给libusb_control_transfer长度为3的data中设置
//rate 采样率
data[0] = (rate & 0xff);
data[1] = (rate >> 8);
data[2] = (rate >> 16);
感谢以下相关资料,对我的帮助
- Universal Serial Bus Device Class Definition for Audio Devices 1.0
- UAC规范-USB音频 这是对UAC相关协议部分中文资料
- Android无驱usb音频实现
- Android从USB声卡录制高质量音频-----使用libusb读取USB声卡数据