君正UVC驱动框架概述

内容纲要

一、君正UVC驱动框架概述

君正UVC驱动框架是一套完整的USB视频类(UVC)和音频类(UAC)驱动解决方案,包含内核模块、用户态库和配置文件解析模块,支持摄像头和音频设备的硬件操作、流数据处理及配置管理。以下为框架的详细说明和模块划分。
file

二、框架模块说明

1. usbcamera.ko
作为内核模块,负责向Linux内核注册驱动程序,提供video0gaudio0设备节点,支持UVC/UAC标准协议。其功能包括:

  • 设备枚举与初始化。
  • 提供V4L2接口(如/dev/video0)和ALSA接口(如/dev/gaudio0)。
  • 响应硬件中断并管理数据传输。

2. libusbcamera.a
用户态操作库,封装对内核驱动的控制接口,分为三类功能:

  • CT(Control):硬件控制(光圈、变焦、对焦等)。
  • PU(Processing Unit):图像质量调整(亮度、对比度、饱和度等)。
  • EU(Extension Unit):扩展功能(AI检测、特效等)。
    依赖子库libconfig.alibmodule.alibucamera.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音频数据,支持重采样和格式转换。

三、数据流与模块交互

  1. 初始化阶段
    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;
    }
    }
  2. 运行时控制
    用户调用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;
      }
  3. 流数据处理
    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)|
                                  +-------------------+

初始化流程及步骤如下

  1. 加载usbcamera.ko驱动,注册设备节点
  2. 应用程序加载libusbcamera.a库
  3. 解析uvc.config配置文件
  4. 初始化CT、PU、EU各功能模块
  5. 配置编解码参数和流媒体设置**

视频采集流程如下:
file

应用控制控制流程如下:
file

四、关键特性

  1. 模块化设计
    各功能解耦,如编码库libimp.a独立于驱动,便于替换编码算法。

  2. 灵活配置
    通过uvc.config动态调整设备参数,无需重新编译驱动。

  3. 扩展性
    EU模块支持二次开发,可集成AI或图像处理算法。

  4. 多格式支持
    视频支持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]”;对于有明确作者署名的文章,还需完整保留作者姓名。在引用本网站内容时,请确保内容准确无误,并遵循学术及行业的引用规范。

Like (0)
Donate 微信扫一扫打赏 微信扫一扫打赏 支付宝扫一扫打赏 支付宝扫一扫打赏
tomgzhe的头像tomgzhe
Previous 2025年9月12日 15:20
Next 2025年10月16日

相关推荐

发表回复

Please Login to Comment
联系我们

联系我们

400-800-6666

在线咨询: QQ交谈 邮件:tomgzhe@qq.com 工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
关注微信
SHARE
TOP
蛇年新气象!从2025年2月起,本博客将在保留科技板块基础上,新增生活美学、个人成长等多元内容,希望能为大家带来更丰富的阅读体验,敬请期待!