一、系统架构总览
1. 硬件平台
本系统基于 Beken BK7258 双核 Wi-Fi SoC,具备以下图传相关硬件能力:
| 硬件模块 | 功能 | 备注 |
|---|---|---|
| DVP 接口 | 并行摄像头输入 | 支持 8 位数据总线 |
| JPEG 硬件编码器 | 将 YUV 编码为 MJPEG | 由 bk_jpeg_enc 驱动 |
| H.264 硬件编码器 | 将 YUV 编码为 H.264 | 由 bk_h264 驱动 |
| USB OTG | USB 视频类(UVC)主机 | 支持 H.264/JPEG/YUV 流 |
| PSRAM | 帧缓冲存储 | 独立编码/YUV 堆 |
| DMA | 编码器 FIFO → PSRAM 搬运 | 8 通道固定分配 |
| LCD RGB/I80 | 本地显示 | 最大 864×480 |
2. 软件分层
整个系统从底到顶分为五层:
┌─────────────────────────────────────────────────────┐
│ 应用层: doorbell_asr / uvc_example / dvp_example │
├─────────────────────────────────────────────────────┤
│ 服务层: media_service / wifi_transfer │
├─────────────────────────────────────────────────────┤
│ 管线层: uvc_pipeline / h264_encode_pipeline / │
│ jpeg_decode_pipeline │
├─────────────────────────────────────────────────────┤
│ 抽象层: bk_camera_ctlr (统一摄像头控制器接口) │
├─────────────────────────────────────────────────────┤
│ 驱动层: bk_dvp / bk_uvc / frame_buffer │
└─────────────────────────────────────────────────────┘
3. 数据流路径
系统支持两条独立的图传链路:
链路 A:DVP 本地采集链路(发送端)
DVP 传感器 → YUV BUF → JPEG/H.264 硬件编码器 → DMA → PSRAM 帧缓冲
→ 帧队列 → WiFi 传输 / LCD 显示
链路 B:UVC USB 接收链路(接收端)
USB 主机口 ← UVC 摄像头 → URB 包接收 → 包解析 → 帧缓冲
→ JPEG 解码管线 → YUV → H.264 编码管线 / LCD 旋转显示
二、环境准备
1. 工程目录结构
bash
cd ~/Project_Chen/SCH3000_4302644_4.3_Inch
# 核心源码目录
ap/include/components/ # 公开头文件
ap/components/bk_camera/ # 摄像头控制器抽象层
ap/components/bk_dvp/ # DVP 底层驱动
ap/components/bk_uvc/ # UVC 底层协议栈
ap/components/multimedia/ # 多媒体管线
ap/components/wifi_transfer/ # WiFi 帧传输
# 示例工程目录
projects/dvp_example/ # DVP 摄像头示例
projects/uvc_example/ # UVC 单流示例
projects/uvc_dualstream_example/ # UVC 双流示例
projects/video_pipeline_example/ # 视频管线示例
projects/doorbell_asr/ # 可视门铃综合应用
2. 关键依赖关系
doorbell_asr
├── media_service (服务框架)
├── wifi_transfer (WiFi 传输)
├── multimedia/pipeline (视频管线)
│ ├── uvc_pipeline_act
│ ├── h264_encode_pipeline
│ └── jpeg_decode_pipeline
├── bk_camera (摄像头控制器)
│ ├── bk_camera_dvp_ctlr
│ └── bk_camera_uvc_ctlr
├── bk_dvp (DVP 驱动)
├── bk_uvc (UVC 协议栈)
└── frame_buffer (帧缓冲管理)
三、帧缓冲管理
1. 内存分配原理
帧缓冲是所有图像数据的载体。系统在 PSRAM 中划分了两个独立堆:
| 堆类型 | 用途 | 分配函数 |
|---|---|---|
PSRAM_HEAP_YUV |
YUV 原始帧 | frame_buffer_display_malloc() |
PSRAM_HEAP_ENCODE |
编码后帧(JPEG/H.264) | frame_buffer_encode_malloc() |
核心实现位于 ap/components/multimedia/src/frame_buffer.c:
c
frame_buffer_t *frame_buffer_encode_malloc(uint32_t size)
{
// 分配 frame_buffer_t 头 + 数据区 + 32 字节对齐余量
frame_buffer_t *frame = bk_psram_frame_buffer_malloc(
PSRAM_HEAP_ENCODE,
size + sizeof(frame_buffer_t) + (1 << ALIGN_BITS)
);
if (frame == NULL) return NULL;
os_memset(frame, 0, sizeof(frame_buffer_t));
// 数据指针 32 字节对齐
frame->frame = (uint8_t *)((((uint32_t)(frame + 1) >> ALIGN_BITS) + 1) << ALIGN_BITS);
frame->size = size;
frame->flag = FB_ALLOCATED_PATTERN; // 0x8338 已分配标记
frame->frame_crc = hnd_crc8((uint8_t *)frame, 6, 0xFF); // CRC 防重复释放
return frame;
}
原理说明:
frame_buffer_t结构体头部紧挨数据区,一次性从 PSRAM 分配flag字段标记分配状态,frame_crc校验头部完整性,防止 double-free- 数据指针强制 32 字节对齐,满足 DMA 要求
2. 帧队列管理
DVP 示例中的 dvp_frame_list.c 实现了生产者-消费者模型:
c
typedef struct {
uint8_t count;
image_format_t format;
beken_queue_t free_que; // 空闲帧队列
beken_queue_t ready_que; // 就绪帧队列
} frame_queue_t;
工作流程:
① 摄像头中断 → dvp_frame_malloc() → 从 free_que 取消息 → 分配 frame_buffer
② 编码完成 → dvp_frame_complete() → 将 frame 推入 ready_que
③ 消费任务 → dvp_frame_queue_get_frame() → 从 ready_que 取 frame
④ 消费完成 → dvp_frame_queue_free() → 释放 frame → 消息回 free_que
每种格式预分配帧数:
| 格式 | 帧数 |
|---|---|
| MJPEG | 4 |
| H.264 | 6 |
| YUV | 3 |
四、摄像头控制器抽象层
1. 统一接口设计
bk_camera_ctlr.h 定义了所有摄像头的统一操作接口,采用函数指针表实现多态:
c
typedef struct bk_camera_ctlr *bk_camera_ctlr_handle_t;
struct bk_camera_ctlr {
avdk_err_t (*open)(bk_camera_ctlr_t *controller);
avdk_err_t (*close)(bk_camera_ctlr_t *controller);
avdk_err_t (*suspend)(bk_camera_ctlr_t *controller);
avdk_err_t (*resume)(bk_camera_ctlr_t *controller);
avdk_err_t (*del)(bk_camera_ctlr_t *controller);
avdk_err_t (*ioctlr)(bk_camera_ctlr_t *controller, uint32_t cmd, void *arg);
};
上层应用只依赖这六个接口,不关心底层是 DVP 还是 UVC。
2. DVP 控制器实现
bk_camera_dvp_ctlr_new() 在创建时绑定 DVP 专用的操作函数:
c
avdk_err_t bk_camera_dvp_ctlr_new(bk_camera_ctlr_handle_t *handle,
bk_dvp_ctlr_config_t *config)
{
private_camera_dvp_ctlr_t *controller = os_malloc(sizeof(...));
// 根据输出格式预分配编码缓冲区
if (config->config.img_format & IMAGE_H264)
controller->encode_buffer = os_malloc(config->config.width * 32 * 2);
else if (config->config.img_format & IMAGE_MJPEG)
controller->encode_buffer = os_malloc(config->config.width * 16 * 2);
// 绑定操作函数
controller->ops.open = dvp_camera_ctlr_open;
controller->ops.close = dvp_camera_ctlr_close;
controller->ops.del = dvp_camera_ctlr_delete;
controller->ops.suspend = dvp_camera_ctlr_suspend;
controller->ops.resume = dvp_camera_ctlr_resume;
controller->ops.ioctlr = dvp_camera_ctlr_ioctlr;
*handle = &(controller->ops); // 返回接口指针
controller->state = CAMERA_STATE_INIT;
return AVDK_ERR_OK;
}
3. UVC 控制器实现
bk_camera_uvc_ctlr_new() 同样绑定 UVC 操作函数,open 内部调用 bk_uvc_power_on() + bk_uvc_open():
c
static avdk_err_t uvc_camera_ctlr_open(bk_camera_ctlr_t *controller)
{
private_camera_uvc_ctlr_t *uvc_controller =
__containerof(controller, private_camera_uvc_ctlr_t, ops);
// 先上电等待设备连接
avdk_err_t ret = bk_uvc_power_on(uvc_controller->config.config.img_format, 40000);
if (ret == AVDK_ERR_OK) {
// 再打开流
ret = bk_uvc_open(&uvc_controller->handle,
&uvc_controller->config.config,
uvc_controller->config.cbs);
if (ret == AVDK_ERR_OK)
uvc_controller->state = CAMERA_STATE_OPENED;
}
return ret;
}
五、DVP 摄像头配置
1. 配置结构体
bk_dvp_config_t 包含摄像头完整的硬件配置:
c
typedef struct {
bk_dvp_i2c_config_t i2c_config; // I2C 配置(传感器寄存器读写)
uint8_t reset_pin; // 复位引脚,0xFF 表示无效
uint8_t pwdn_pin; // 电源控制引脚,0xFF 表示无效
bk_dvp_io_config_t io_config; // 数据/同步 IO 引脚
mclk_freq_t clk_source; // 主时钟频率
uint16_t img_format; // 输出格式
uint16_t width; // 输出宽度
uint16_t height; // 输出高度
uint32_t fps; // 帧率
void *user_data; // 用户数据
} bk_dvp_config_t;
2. 预定义配置
dvp_camera_types.h 提供了默认配置宏:
c
#define BK_DVP_864X480_30FPS_MJPEG_CONFIG() \
{ \
.i2c_config = { \
.id = 2, /* I2C 总线 2 */ \
.scl_pin = 0, /* SCL: GPIO0 */ \
.sda_pin = 1, /* SDA: GPIO1 */ \
.baud_rate = 100000, /* 100KHz */ \
.flags = 0, \
}, \
.reset_pin = 0xFF, /* 无复位引脚 */ \
.pwdn_pin = 0xFF, /* 无电源引脚 */ \
.io_config = { \
.data_width = SENSOR_BITS_WIDTH_8BIT, /* 8 位数据总线 */ \
.data_pin = {32, 33, 34, 35, 36, 37, 38, 39}, /* D0-D7 */ \
.vsync_pin = 31, /* VSYNC: GPIO31 */ \
.hsync_pin = 30, /* HSYNC: GPIO30 */ \
.xclk_pin = 27, /* XCLK: GPIO27 */ \
.pclk_pin = 29, /* PCLK: GPIO29 */ \
}, \
.clk_source = MCLK_24M, /* 24MHz 主时钟 */ \
.width = 864, \
.height = 480, \
.fps = 30, \
.img_format = IMAGE_MJPEG, \
.user_data = NULL, \
}
3. 回调函数
摄像头每采集完成一帧,通过回调通知上层:
c
typedef struct {
frame_buffer_t *(*malloc)(image_format_t format, uint32_t size);
void (*complete)(image_format_t format, frame_buffer_t *frame, int result);
} bk_dvp_callback_t;
注意:回调函数运行在中断上下文中,不允许阻塞操作。源码中通过独立工作线程将中断回调转发到任务上下文:
c
static void dvp_work_thread(void *param)
{
// 从消息队列接收 EOF 事件
// 调用 handle->callback->complete() 在任务上下文通知上层
}
4. 三种工作模式
bk_dvp.c 中根据 img_format 初始化不同硬件:
| 模式 | 硬件初始化 | 中断注册 | 说明 |
|---|---|---|---|
IMAGE_YUV |
dvp_camera_yuv_mode() |
YUV_BUF_YUV_ARV |
直接输出 YUV422 |
IMAGE_MJPEG |
dvp_camera_jpeg_mode() |
JPEG_EOF |
JPEG 硬件编码 |
IMAGE_H264 |
dvp_camera_h264_mode() |
H264_FINAL_OUT |
H.264 硬件编码 |
组合模式:IMAGE_MJPEG | IMAGE_YUV 同时输出 JPEG 和 YUV。
六、UVC 摄像头配置
1. 配置结构体
c
typedef struct {
uvc_stream_type_t type; // 单流/双流
uint8_t port; // 端口号(1 开始)
uint8_t drop_num; // 丢弃帧数
image_format_t img_format; // 输出格式
uint16_t width;
uint16_t height;
uint32_t fps;
void *user_data;
} bk_cam_uvc_config_t;
2. 预定义配置
c
// MJPEG 单流
#define BK_UVC_864X480_30FPS_MJPEG_CONFIG() \
{ \
.type = UVC_SINGLE_STREAM, \
.port = 1, \
.drop_num = 0, \
.img_format = IMAGE_MJPEG, \
.width = 864, .height = 480, .fps = 30, \
.user_data = NULL, \
}
// H.264 单流
#define BK_UVC_1920X1080_30FPS_H26X_CONFIG() \
{ \
.type = UVC_SINGLE_STREAM, \
.port = 1, .drop_num = 0, \
.img_format = IMAGE_H264, \
.width = 1920, .height = 1080, .fps = 15,\
.user_data = NULL, \
}
3. UVC 协议栈核心
bk_uvc.c 实现了完整的 UVC 视频类主机栈:
(1) 设备枚举 :通过 bk_usbh_hub_port_register_connect_callback() 注册连接回调,设备插入时自动触发。
(2) 格式协商 :uvc_camera_stream_check_config() 遍历设备支持的格式/分辨率/帧率,与用户配置匹配。
(3) URB 管理 :使用 uvc_camera_urb_malloc() / uvc_camera_urb_push() 管理 USB 请求块。
(4) 包解析 :uvc_camera_stream_packet_process() 处理 UVC 载荷头(12 字节头),提取 FID/EOF 标志,重组帧数据。
(5) EOF 处理 :uvc_camera_stream_eof_handle() 校验帧完整性(JPEG SOF/EOF 标记、H.264 起始码),通过 complete 回调通知上层。
4. 回调函数
c
typedef struct {
frame_buffer_t *(*malloc)(image_format_t format, uint32_t size);
void (*complete)(uint8_t port, image_format_t format,
frame_buffer_t *frame, int result);
void (*uvc_event_callback)(bk_usb_hub_port_info *port_info,
void *arg, uvc_error_code_t code);
} bk_uvc_callback_t;
七、视频管线配置
1. 管线初始化
uvc_pipeline_act.c 中的 uvc_pipeline_init() 按顺序初始化各子管线:
c
bk_err_t uvc_pipeline_init(void)
{
bk_jdec_pipeline_init(); // ① JPEG 解码管线
#if SUPPORTED_IMAGE_MAX_720P
bk_scale_pipeline_init(); // ② 缩放管线(可选)
#endif
bk_rotate_pipeline_init(); // ③ 旋转管线
#ifdef CONFIG_H264
bk_h264_pipeline_init(); // ④ H.264 编码管线
#endif
pipeline_init = true;
}
2. H.264 编码管线
h264_encode_pipeline.c 实现了从 JPEG 解码输出到 H.264 编码的完整链路:
(1) 任务模型 :独立任务 h264_encode_main(),通过消息队列处理编码请求。
(2) 消息类型:
| 消息 | 功能 |
|---|---|
H264_ENCODE_START |
开始编码一帧 |
H264_ENCODE_LINE_DONE |
一行编码完成 |
H264_ENCODE_LINE_CONTINUE |
继续编码下一行 |
H264_ENCODE_FINISH |
一帧编码完成 |
H264_ENCODE_REGENERATE_IDR |
强制生成 IDR 帧 |
H264_ENCODE_RESET |
硬件复位 |
H264_ENCODE_STOP |
停止编码 |
(3) 双缓冲机制:
编码器在 YUV_BUF 中采用 ping-pang 缓冲,每 16 行触发一次行完成中断,将解码后的 YUV 数据送入编码器。
(4) DMA 搬运:
c
static bk_err_t h264_encode_dma_config(void)
{
// 从 H.264 编码器 FIFO 到 PSRAM 帧缓冲
dma_config.src.dev = DMA_DEV_H264;
dma_config.src.start_addr = encode_fifo_addr;
dma_config.dst.start_addr = (uint32_t)h264_encode_config->h264_frame->frame;
dma_config.dst.end_addr = h264_encode_config->h264_frame->frame + size;
// 重复模式,10KB 传输长度
dma_config.mode = DMA_WORK_MODE_REPEAT;
bk_dma_set_transfer_len(channel, H264_DMA_LEN); // 10240 字节
}
(5) 帧完成回调:
c
static void h264_encode_finish_handle(void)
{
// 校验 DMA 长度与实际编码长度一致
// 校验 H.264 起始码 00 00 00 01
// 设置 I/P 帧类型标志
// 调用 cb->complete() 通知上层
}
3. JPEG 解码管线
jpeg_decode_pipeline.c 实现 JPEG → YUV 的解码,作为 H.264 编码的数据源:
(1) 解码模式自动切换:
| 条件 | 模式 |
|---|---|
| JPEG 内部为 YUV422 | 硬件解码(按行) |
| JPEG 内部为 YUV420 | 软件解码(按帧) |
| 分辨率 ≥ 1280×720 | 软件解码不支持,报错 |
(2) 多路复用缓冲:
解码输出可以同时供 H.264 编码、旋转、缩放三个模块使用,每个模块通过 MUX_BUFFER_PRESET/SHAREED/IDLE 状态管理。
(3) 模块注册机制:
c
void bk_jdec_buffer_request_register(pipeline_module_t module,
mux_request_callback_t cb,
mux_reset_callback_t reset_cb)
{
jdec_config->module[module].enable = true;
jdec_config->cb[module] = cb;
jdec_config->reset_cb[module] = reset_cb;
}
八、WiFi 传输配置
1. 传输层接口
c
typedef struct {
media_transfer_send_cb send; // 发送回调
media_transfer_prepare_cb prepare; // 预处理回调
media_transfer_drop_check_cb drop_check; // 丢帧检查
media_transfer_get_tx_buf_cb get_tx_buf; // 获取发送缓冲
media_transfer_get_tx_size_cb get_tx_size; // 获取缓冲大小
frame_buffer_t *(*read)(image_format_t format, uint32_t timeout); // 读取帧
void (*free)(image_format_t format, frame_buffer_t *frame); // 释放帧
} media_transfer_cb_t;
2. 分包发送
wifi_transfer.c 将一帧数据按 pkt_size 分包,添加 transfer_data_t 头部:
c
typedef struct {
uint8_t id; // 帧 ID(sequence 低 8 位)
uint8_t eof; // 帧结束标志
uint8_t cnt; // 包序号
uint8_t size; // 总包数
uint8_t data[]; // 载荷
} transfer_data_t;
发送流程:
① 读取帧 → wifi_transfer_send_frame()
② 计算分包数: count = length / pkt_size, tail = length % pkt_size
③ 逐包填充 transfer_data_t 头 + 数据
④ 调用 cfg->cb->send() 发送
⑤ 最后一包设置 eof = 1
⑥ 释放帧
3. 传输任务
c
static void wifi_transfer_task_entry(beken_thread_arg_t data)
{
while (cfg->enable) {
// 从帧队列读取帧(50ms 超时)
frame = cfg->cb->read(cfg->img_format, 50);
if (frame == NULL) continue;
// 分包发送
wifi_transfer_send_frame(cfg, frame);
// 释放帧
cfg->cb->free(cfg->img_format, frame);
}
}
4. 打开/关闭
c
bk_err_t bk_wifi_transfer_frame_open(const media_transfer_cb_t *cb,
uint16_t img_format)
{
// 初始化发送缓冲
wifi_transfer_buffer_init(cfg);
// 创建传输任务
rtos_create_thread(&cfg->transfer_thread, ..., "trs_task", ...);
// 设置 WiFi 媒体模式
bk_wifi_set_wifi_media_mode(true);
bk_wifi_set_video_quality(WIFI_VIDEO_QUALITY_SD);
}
bk_err_t bk_wifi_transfer_frame_close(void)
{
cfg->enable = 0;
// 等待任务退出
rtos_get_semaphore(&cfg->sem, BEKEN_NEVER_TIMEOUT);
// 恢复 WiFi 模式
bk_wifi_set_wifi_media_mode(false);
bk_wifi_set_video_quality(WIFI_VIDEO_QUALITY_HD);
}
九、应用层集成
1. 门铃应用入口
doorbell_asr/ap/ap_main.c:
c
int main(void)
{
bk_init(); // ① 系统初始化
media_service_init(); // ② 媒体服务初始化
#if CONFIG_INTEGRATION_DOORBELL
bk_smart_config_init(); // ③ WiFi 配网
doorbell_core_init(); // ④ 门铃核心服务
#endif
return 0;
}
2. 摄像头开关流程
doorbell_devices.c 中的 doorbell_camera_turn_on() 展示了完整的调用链:
c
int doorbell_camera_turn_on(camera_parameters_t *parameters)
{
// ① 根据设备 ID 判断摄像头类型
if (parameters->id == UVC_DEVICE_ID) // 0xFDF6
curr_cam_type = UVC_CAMERA;
else
curr_cam_type = DVP_CAMERA;
// ② 根据传输格式配置编码
if (parameters->format == 0) { // WiFi 传输 MJPEG
uvc_config.img_format = IMAGE_MJPEG;
dvp_config.img_format = IMAGE_YUV | IMAGE_MJPEG;
} else { // WiFi 传输 H.264
dvp_config.img_format = IMAGE_YUV | IMAGE_H264;
uvc_config.img_format = IMAGE_MJPEG; // UVC 输出 MJPEG
db_device_info->pipeline_enable = true; // 启用 JPEG→H264 管线
}
// ③ 组装摄像头配置
bk_camera_config_t camera_config = {0};
camera_config.camera_type = curr_cam_type;
if (curr_cam_type == DVP_CAMERA)
camera_config.dvp_cfg.dvp_config = &dvp_config;
else
camera_config.uvc_cfg.uvc_config = &uvc_config;
// ④ 打开摄像头
ret = media_app_camera_open(&db_device_info->video_handle, &camera_config);
// ⑤ 如果启用管线,打开 H.264 编码
if (db_device_info->pipeline_enable)
media_app_pipeline_h264_encode_open(NULL, NULL, NULL, NULL);
// ⑥ 如果 LCD 已打开,打开 JPEG 解码
if (check_lcd_task_is_open())
media_app_jpeg_decode_open(&jpeg_dec_config, NULL, NULL);
}
3. WiFi 传输开关
c
int doorbell_video_transfer_turn_on(void)
{
if (db_device_info->h264_transfer)
bk_wifi_transfer_frame_open(db_device_info->camera_transfer_cb, IMAGE_H264);
else
bk_wifi_transfer_frame_open(db_device_info->camera_transfer_cb, IMAGE_MJPEG);
}
int doorbell_video_transfer_turn_off(void)
{
bk_wifi_transfer_frame_close();
}
十、实际操作步骤
1. DVP 摄像头示例
bash
cd projects/dvp_example
# 编译
make bk7258
# 烧录后通过串口 CLI 操作
# 打开 MJPEG 摄像头
dvp open 864 480 mjpeg
# 打开 H.264 摄像头
dvp open 864 480 h264
# 打开 YUV 输出
dvp open 864 480 yuv
# 转储帧数据
dvp dump
# 关闭摄像头
dvp close
2. UVC 单流示例
bash
cd projects/uvc_example
make bk7258
# 打开 UVC MJPEG 流
uvc open 1 864 480 mjpeg
# 打开 UVC H.264 流
uvc open 1 1920 1080 h264
# 关闭
uvc close 1
3. UVC 双流示例
bash
cd projects/uvc_dualstream_example
make bk7258
# 打开双流:H.264 主码流 + MJPEG 子码流
uvc open_dual 1 H264 1280 720 MJPEG 640 480
# 关闭双流
uvc close 1 all
4. 视频管线示例
bash
cd projects/video_pipeline_example
make bk7258
# 创建管线
video_pipeline new
# 打开 H.264 编码管线(JPEG 解码 → H.264 编码)
video_pipeline open_h264e
# 打开旋转管线(JPEG 解码 → 旋转 → LCD)
video_pipeline open_rotate
# 关闭管线
video_pipeline close_h264e
video_pipeline delete
5. 门铃应用
bash
cd projects/doorbell_asr
make bk7258
# 烧录后通过 APP 或 CLI 控制摄像头和传输
十一、调试与验证
1. 关键日志
源码中留有关键调试日志:
| 日志 TAG | 位置 | 内容 |
|---|---|---|
dvp_drv |
bk_dvp.c |
打开成功:dvp open success 864 X 480, 30, 0x4 |
uvc_stream |
bk_uvc.c |
连接:device_index, port |
h264_pipline |
h264_encode_pipeline.c |
编码:I/p frame type |
jdec_pip |
jpeg_decode_pipeline.c |
解码模式切换 |
wifi_trs |
wifi_transfer.c |
分包发送 |
2. 性能统计
media_debug_t 结构提供实时统计:
c
typedef struct {
uint16_t isr_h264; // H.264 帧计数
uint16_t isr_decoder; // JPEG 解码帧计数
uint16_t fps_wifi; // WiFi 传输帧率
uint32_t h264_kbps; // H.264 码率
uint32_t wifi_kbps; // WiFi 传输码率
} media_debug_t;
3. 常见问题排查
| 现象 | 原因 | 排查方法 |
|---|---|---|
| DVP 打不开 | 传感器未检测到 | 检查 I2C 地址、GPIO 引脚、传感器供电 |
| UVC 连接超时 | USB 设备未枚举 | 检查 USB 线缆、设备供电、VID/PID |
| 帧数据错误 | 分辨率不匹配 | 检查 uvc_camera_stream_check_config() 日志 |
| H.264 编码失败 | 管线未正确初始化 | 检查 uvc_pipeline_init() 返回值 |
| WiFi 丢帧 | 发送缓冲不足 | 检查 drop_check 回调逻辑 |
| 帧缓冲泄漏 | double-free 或忘记释放 | 检查 frame_crc 校验日志 |
- 统一抽象 :
bk_camera_ctlr将 DVP 和 UVC 统一为六个接口,上层无感知 - 回调分离:中断回调通过工作线程转发,避免中断上下文阻塞
- 双队列管理:free/ready 队列实现零拷贝帧传递
- 管线可组合:JPEG 解码 → H.264 编码 / 旋转 / 缩放按需注册
- WiFi 分包:固定头部 + 载荷,支持接收端重组
掌握这些要点后,即可灵活配置出 DVP 直出、UVC 接收、JPEG→H.264 转码等多种图传方案。