一、君正UVC驱动框架概述
君正UVC驱动框架是一套完整的USB视频类(UVC)和音频类(UAC)驱动解决方案,包含内核模块、用户态库和配置文件解析模块,支持摄像头和音频设备的硬件操作、流数据处理及配置管理。以下为框架的详细说明和模块划分。
二、框架模块说明
1. usbcamera.ko
作为内核模块,负责向Linux内核注册驱动程序,提供video0和gaudio0设备节点,支持UVC/UAC标准协议。其功能包括:
- 设备枚举与初始化。
- 提供V4L2接口(如/dev/video0)和ALSA接口(如/dev/gaudio0)。
- 响应硬件中断并管理数据传输。
2. libusbcamera.a
用户态操作库,封装对内核驱动的控制接口,分为三类功能:
- CT(Control):硬件控制(光圈、变焦、对焦等)。
- PU(Processing Unit):图像质量调整(亮度、对比度、饱和度等)。
- EU(Extension Unit):扩展功能(AI检测、特效等)。
依赖子库libconfig.a、libmodule.a和libucamera.a实现UVC/UAC的配置管理。
3. libconfig.a
解析"uvc.config"配置文件,动态配置设备参数:
- UVC编码格式(MJPG/H264/H265/YUV)、分辨率、码率。
- UAC采样率、声道数、数据格式。
-
模块开关状态(如是否启用AI模块)。
uvc.config文件信息如下:#设备信息 vendor_id :0xA108 product_id :0x2240 device_bcd :0090 serial_lab :Ucamera001 product_lab :SmartBar USB Camera manufact_lab :SmartBar Semiconductor Co.,Ltd adb_en :1 #isp sensor 编码等参数设置 sensor_name :sc301iot i2c_addr :0x30 default_boot :0 sensor_fps :30 sensor_width :2048 sensor_height :1536 hvflip :0 rcmode :1 ##码率 2Mbps bitrate :3000 qp_value :60 qp_reform :0 gop :30 bcsh_en :0 bcsh_low :15 bcsh_high :235 ivdc_mode :0 ivdc_jpegv :0 #uvc vc 设置(CT/PU/EU) ct_controls :0x2a,0x02,0x02 pu_controls :0x1f,0x05 eu_en :1 eu_num :1 eu_guid :0x38,0xAB,0xE4,0x62,0x6E,0x76,0x54,0x4B,0xA8,0x45,0xBA,0x59,0x3A,0xB3,0xC0,0xA5 eu_controls :0x00,0x10,0x0 #音频设置(amic dmic spk) audio_name :SmartBar USB Audio amic_en :1 dmic_en :0 amic_samplerate_num :1 amic_samplerate_list :8000 amic_bitwidth :16 amic_soundmode :1 #单位为db(spk同理) amic_volume :95 spk_en :1 spk_samplerate :8000 spk_bitwidth :16 spk_soundmode :1 spk_volume :0 #Facezoom参数设置 facezoom_en :0 facezoom_mode :2 #视频设置 video_name :SmartBar USB Camera led_en :1 led_gpio :0 led_level :1 #(mc + 1) * mps 代表最大带宽,mps_num:支持的带宽个数,最大设置为11,mps_list:支持的带宽,必须从小到大排列 mc :7 mps :1024 mps_num :11 mps_list :192/384/512/640/800/944/1280/1600/2048/3000/3072 stillcap_en :1 h264_en :3 h265_en :2 mjpeg_en :1 nv12_en :0 yuyv_en :0 winhello_en :0 #fps_num:支持的帧率个数,fps_list:支持的帧率,必须为整数,必须从大到小排列 fps_num :7 fps_list :30/25/20/15/10/7/5 nframes :5 {2048, 1536} {1920, 1080} {1280, 720} {640, 480} {640, 360}
4. libimp.a
提供流数据处理接口(君正编码库):
- UVC:获取YUV420数据,支持编码为MJPG/H264/H265。
- UAC:获取PCM音频数据,支持重采样和格式转换。
三、数据流与模块交互
-
初始化阶段
usbcamera.ko加载后创建设备节点,libusbcamera.a通过ioctl与节点交互,读取uvc.config配置(由libconfig.a解析,以下是读取文件信息,并通过module_config_get_hex接口解析相应的配置信息(设备信息、camera帧率配置等),详细参考以下代码:int device_param_init(device_info_t* dev_info) { int ret; int temp_int; // 使用这个变量去获取int型的数据,防止非int型变量引入地址时发生强转int型导致内存数据出错 ret = module_config_get_hex("vendor_id", &temp_int); if (ret < 0) { dev_info->vendor_id = VENDOR_ID; } else { dev_info->vendor_id = (uint32_t)temp_int; printf("INFO[%s]: vendor id 0x%x\n", MODULE_TAG, dev_info->vendor_id); } ret = module_config_get_hex("device_bcd", &temp_int); if (ret < 0) { dev_info->dev_bcd = DEVICE_BCD; } else { dev_info->dev_bcd = (uint32_t)temp_int; } ret = module_config_get_hex("product_id", &temp_int); if (ret < 0) { dev_info->product_id = PRODUCT_ID; } else { dev_info->product_id = (uint32_t)temp_int; } ret = module_config_get_string("serial_lab", (char*)dev_info->serial_number); if (ret < 0) { strcpy((char*)dev_info->serial_number, SERIAL_LABEL); } else { /*printf("INFO[%s]: serial_label %s\n", MODULE_TAG, dev_info->serial_number);*/ } ret = module_config_get_string("product_lab", (char*)dev_info->product_label); if (ret < 0) { strcpy((char*)dev_info->product_label, PRODUCT_LABEL); } /*printf("INFO[%s]: product_label %s\n", MODULE_TAG, dev_info->product_label);*/ ret = module_config_get_string("manufact_lab", (char*)dev_info->manufact_label); if (ret < 0) { strcpy((char*)dev_info->manufact_label, MANUFACT_LABEL); } ret = module_config_get_int("adb_en", &temp_int); if (ret < 0) { dev_info->adb_en = !ADB_ENABLE; } else { dev_info->adb_en = (uint8_t)temp_int; } /*printf("INFO[%s]: adb en %d\n", MODULE_TAG, dev_info->adb_en);*/ return 0; } int video_param_init(video_info_t* video_info) { int ret; int temp_int; // 使用这个变量去获取int型的数据,防止非int型变量引入地址时发生强转int型导致内存数据出错 ret = module_config_get_string("video_name", (char*)video_info->video_name); if (ret < 0) { strcpy((char*)video_info->video_name, VIDEO_DEVICE_NAME); } ret = module_config_get_int("mc", &temp_int); if (ret < 0) { video_info->mult = 2; } else { video_info->mult = (uint8_t)temp_int; } ret = module_config_get_int("mps", &temp_int); if (ret < 0) { video_info->mps = 1024; } else { video_info->mps = temp_int; } /* get the number of supported maxpacketsize */ ret = module_config_get_int("mps_num", &temp_int); if (ret < 0) { video_info->mps_num = 11; } else { if (temp_int > 11) { printf("ERROR:mps_num > 11,please check uvc.config"); return 0; } video_info->mps_num = temp_int; } /* get mps_list ,such as 3072, 2048, 1024 etc. */ video_info->mps_list = (uint32_t*)malloc(video_info->mps_num * sizeof(uint32_t)); if (!video_info->mps_list) { printf("ERROR(%s): mps_list malloc failed!\n", MODULE_TAG); return -1; } } -
运行时控制
用户调用libusbcamera.a的CT/PU/EU接口,通过内核驱动操作硬件;配置变更时,libconfig.a 同步更新到驱动层。
以下是音视频控制代码:-
UAC控制register 函数:
static void register_audio_func(void) { #ifdef T23 a_func.get_AudioPcm = smartbar_audio_amic_pcm_get; #else if (uac_ctx.dmic_en) a_func.get_AudioPcm = smartbar_audio_dmic_pcm_get; else a_func.get_AudioPcm = smartbar_audio_amic_pcm_get; #endif a_func.set_Mic_Volume = smartbar_set_mic_volume; a_func.set_Spk_Volume = smartbar_set_spk_volume; a_func.set_Mic_Mute = smartbar_set_mic_mute; a_func.set_Spk_Mute = smartbar_set_spk_mute; a_func.set_Mic_Samplerate = smartbar_set_mic_samplerate; a_func.get_record_on = smartbar_get_record_on; a_func.get_record_off = smartbar_get_record_off; a_func.get_speak_on = smartbar_get_speak_on; a_func.get_speak_off = smartbar_get_speak_off; IF_Ucam_Audio_Func(&a_func); } int uac_control_init(void *param) { int ret = 0; config_func_param_t *cfg_param = (config_func_param_t*)param; /* uac context initialize */ memcpy(&uac_ctx, &cfg_param->audio_info, sizeof(audio_info_t)); if(uac_ctx.amic_en || uac_ctx.dmic_en || uac_ctx.spk_en){ imp_audio_init(); ret = IF_Ucam_Audio_Init(AUDIO_DEVICE_NAME, &cfg_param->audio_info); if (ret < 0) { printf("ERROR(%s): IF_UCAM_AUDIO_Init failed!\n", MODULE_TAG); return -1; } register_audio_func(); ret = IF_Ucam_Audio_Start(&cfg_param->audio_info); if (ret < 0) { printf("ERROR(%s): IF_UCAM_AUDIO_Start failed!\n", MODULE_TAG); return -1; } } /* speak play */ if(uac_ctx.spk_en) { smartbar_audio_play_start(); printf("INFO[%s]: audio play start...!\n", MODULE_TAG); } return 0; } - UVC控制及回调函数:
int uvc_control_init(void *param) { int ret; if (param == NULL) { printf("ERROR(%s,%s): Pointer is null pointer!\n", MODULE_TAG, __func__); return -1; } config_func_param_t *cfg_param = (config_func_param_t*)param; imp_attr_t *imp_attr = &cfg_param->imp_attr; /* uvc context initialize */ uvc_ctx[0].stream_on = 0; uvc_ctx[0].hvflip = imp_attr->hvflip; uvc_ctx[0].sensor_fps = imp_attr->sensor_info[0].sensor_fps; uvc_ctx[0].sensor_width = imp_attr->sensor_info[0].sensor_width; uvc_ctx[0].sensor_height = imp_attr->sensor_info[0].sensor_height; /* uvc init */ /* Step first */ ret = IF_Ucam_Video_Init(VIDEO_DEVICE_NAME); if (ret < 0) { printf("ERROR(%s): IF_Ucam_Video_Init failed!\n", MODULE_TAG); return -1; } #if (VIDEO_NUM == 2) #if (DUAL_SENSOR == 1) uvc_ctx[1].stream_on = 0; uvc_ctx[1].hvflip = imp_attr->hvflip; uvc_ctx[1].sensor_fps = imp_attr->sensor_info[1].sensor_fps; uvc_ctx[1].sensor_width = imp_attr->sensor_info[1].sensor_width; uvc_ctx[1].sensor_height = imp_attr->sensor_info[1].sensor_height; #endif uvc_ctx[1].hvflip = imp_attr->hvflip; ret = IF_Ucam_Video_Init(VIDEO2_DEVICE_NAME); if (ret < 0) { printf("ERROR(%s): IF_Ucam_Video_Init failed!\n", MODULE_TAG); return -1; } printf("-------- video 2 init ok!\n"); #endif register_video_callback(); register_ct_callback(); register_pu_callback(); register_eu_callback(); register_eventprocess_callback(); /* Step last */ ret = IF_Ucam_Video_Start(); if (ret < 0) printf("ERROR(%s): IF_UCAM_Video_Start failed!\n", MODULE_TAG); return 0; }
-
-
流数据处理
UVC视频流:libimp.a 从驱动获取YUV数据并编码,输出到应用层。
UAC音频流:libimp.a 处理PCM数据后传输至ALSA。
以下是输出到扬声器代码:
int smartbar_audio_amic_pcm_get(short *pcm)
{
/* Step 6: get audio record frame. */
int ret = 0;
int devID = 1;
int chnID = 0;
#ifdef MERT_ENABLE
int len = 0;
int audio_ns = uac_ctx.audio_ns;
int samplerate = mic_samplerate_cur;
int soundmode = uac_ctx.amic_info.soundmode;
short save_data[320] = {0};
int nframe = 2;
#endif
if(!mic_enable)
return 0;
ret = IMP_AI_PollingFrame(devID, chnID, 1000);
if (ret != 0 ) {
IMP_LOG_ERR(MODULE_TAG, "Audio Polling Frame Data error\n");
}
IMPAudioFrame frm;
ret = IMP_AI_GetFrame(devID, chnID, &frm, BLOCK);
if(ret != 0) {
IMP_LOG_ERR(MODULE_TAG, "Audio Get Frame Data error\n");
return 0;
}
/* Step 7: Save the recording data to a file. */
memcpy((void *)pcm, frm.virAddr, frm.len);
#ifdef MERT_ENABLE
len = frm.len;
#endif
//struct timeval start;
//gettimeofday(&start,NULL);
//unsigned long long start_1 = 1000000 * start.tv_sec + start.tv_usec;
//printf("%s: timestamp = %llu star_1 = %llu.\n",__func__, frm.timeStamp, start_1);
/* Step 8: release the audio record frame. */
ret = IMP_AI_ReleaseFrame(devID, chnID, &frm);
if(ret != 0) {
IMP_LOG_ERR(MODULE_TAG, "Audio release frame data error\n");
return 0;
}
#ifdef MERT_ENABLE
if (audio_ns == 1 && samplerate == 16000 && soundmode == 1){
for(int frames = 0; frames < nframe; frames++)
{
mert_ns_process(handle_ns, pcm + frames*320, save_data, 320);
memcpy(pcm + frames*320, save_data, 640);
}
return len;
}
else
#endif
return frm.len;
}
//输出扬声器
int smartbar_audio_send_pcm_to_spk(void *pcm, int size)
{
int devID = 0;
int chnID = 0;
int ret = -1;
// 准备音频帧
IMPAudioFrame frm;
frm.virAddr = (uint32_t *)pcm;
frm.len = size;
// 发送音频帧
ret = IMP_AO_SendFrame(devID, chnID, &frm, BLOCK);
if (ret != 0) {
fprintf(stderr, "IMP_AO_SendFrame error: %d\n", ret);
return -1 ;
}
return 0;
}
static void *_ao_test_play_thread(void *argv)
{
/*int size = 0;*/
int ret = -1;
int devID = 0;
int chnID = 0;
prctl(PR_SET_NAME, "audio_speak");
struct Ucamera_Audio_Frame u_frame;
/*struct timeval time_cur;*/
/*uint32_t time_recv;*/
while (!ao_play_stop) {
//USB mic 模式
#ifndef AUDIO_AMIC_TO_SPK
ret = IF_Ucam_Audio_Get_Frame(&u_frame);
if (ret || u_frame.len <= 0)
{
usleep(10000);
continue;
}
/* Step 5: send frame data. */
IMPAudioFrame frm;
frm.virAddr = (uint32_t *)u_frame.data;
frm.len = u_frame.len;
ret = IMP_AO_SendFrame(devID, chnID, &frm, BLOCK);
//ret = IMP_AO_SendFrame(devID, chnID, &frm, BLOCK);
if (ret != 0) {
IMP_LOG_ERR(MODULE_TAG, "send Frame Data error\n");
usleep(10000);
continue;
}
IF_Ucam_Audio_Release_Frame(&u_frame);
#else // mic采集直接输出扬声器模式
#define smartbar_RATE 8000 // 采样率
#define CHANNELS 1 // 单声道
#define BITWIDTH 16 // 16位采样精度
#define FRAME_SIZE 320 // 每帧采样点数
#define BYTES_PER_FRAME (FRAME_SIZE * CHANNELS * BITWIDTH / 8)
#define IMP_AUDIO_BUF_SIZE BYTES_PER_FRAME
short pcm[IMP_AUDIO_BUF_SIZE]={0};
int size = smartbar_audio_amic_pcm_get(&pcm);
//输出扬声器
smartbar_audio_send_pcm_to_spk((void *)&pcm, size);
#endif
}
/* pthread_exit(0); */
return NULL;
}
应用调用UVC框图
+-----------------------+
| Application |
+-----------------------+
|
v
+-----------------------+
| libusbcamera.a |<---(CT/PU/EU)--->[usbcamera.ko]
| (依赖libconfig.a等) | | ^
+-----------------------+ | |
| v |
+-----------------------++ +-----------------------+
| libimp.a |<------| Video/Audio Data |
| (编码/取流接口) | | (YUV420/PCM) |
+-----------------------+ +-----------------------+
^
|
+-------------------+
| Hardware Device |
| (Camera/Microphone)|
+-------------------+
初始化流程及步骤如下
- 加载usbcamera.ko驱动,注册设备节点
- 应用程序加载libusbcamera.a库
- 解析uvc.config配置文件
- 初始化CT、PU、EU各功能模块
- 配置编解码参数和流媒体设置**
视频采集流程如下:
应用控制控制流程如下:
四、关键特性
-
模块化设计
各功能解耦,如编码库libimp.a独立于驱动,便于替换编码算法。 -
灵活配置
通过uvc.config动态调整设备参数,无需重新编译驱动。 -
扩展性
EU模块支持二次开发,可集成AI或图像处理算法。 -
多格式支持
视频支持YUV/MJPG/H264/H265,音频支持PCM多种采样率。
版权声明
内容来源及使用限制
欢迎访问 TomgZHE研习社(网址:https://blog.tomgzhe.com)。本网站部分文章内容源自网络,仅作学习交流与参考分享;若您发现有内容涉嫌侵权,请立即联系 tomgzhe@qq.com,我们将在接到通知后的 48 小时内核实并删除相关侵权内容。
软件资源相关规定
本网站为个人非盈利性质的站点,所有软件资源均来自网络。这些资源仅用于个人学习、研究和参考,严禁用于任何商业用途。您下载和使用本网站软件资源即表示您同意仅将其用于学习目的,若因违反此规定导致任何法律纠纷或损失,责任由您自行承担。
原创版权
本网站上的原创内容,包括但不限于文字作品、自行设计的图片、独家制作的音频视频等,其版权均归本网站所有。未经本网站书面授权,任何组织或个人不得擅自复制、转载、摘编、传播或以其他任何方式使用这些原创内容。如需使用,请提前与我们联系并获得书面许可,同时需在显著位置注明出处及作者信息。
转载与引用规范
若您需转载本网站文章,务必注明文章来源为 “[TomgZHE研习社],原文链接:[https://blog.tomgzhe.com/index.php/2025/09/17/jz_uvc_info]”;对于有明确作者署名的文章,还需完整保留作者姓名。在引用本网站内容时,请确保内容准确无误,并遵循学术及行业的引用规范。
微信扫一扫打赏
支付宝扫一扫打赏