Doorbell项目开发指南
1 项目概述
本项目是一个基于BK7258芯片的智能门铃门锁解决方案,实现了通过WiFi传输图像数据,并在LCD屏幕上显示的功能,实时双向音频的功能。项目集成了丰富的多媒体处理能力、网络通信功能和用户界面显示,适用于智能门铃门锁设备的开发。区别于doorbell工程,支持的是camera的MJPEG图传与LCD显示,
2 功能特性
2.1 WiFi通信
支持STA模式连接到现有WiFi网络
支持AP模式创建WiFi热点供BK7258设备连接
支持TCP/UDP协议传输图像数据
支持CS2实时网络传输
2.2 多媒体处理
支持UVC摄像头控制与图像采集,默认格式为MJPEG,分辨率为864x480,帧率为30fps
支持软件或硬件解码,系统内部自动选择
支持H.264编码功能,编码时使用硬编码
支持默认三种帧的buffer池管理
支持多种LCD屏幕显示,RGB屏或MCU屏,默认使用RBGB屏(st7701sn)
支持多种音频编解码算法,默认使用G711编码
支持多种传输协议,实时传输音视频数据
2.3 显示功能
LCD屏幕显示
LVGL图形库支持(可选)
AVI视频播放(可选)
2.4 蓝牙功能
蓝牙基础功能
A2DP音频接收
HFP免提通话
BLE功能
WiFi配网功能
3 快速开始
3.1 硬件准备
BK7258开发板
LCD屏幕
UVC/DVP摄像头模块
板载speaker/mic,或UAC
电源和连接线
3.2 编译和烧录
编译流程参考 Doorbell 解决方案
烧录流程参考 烧录代码
编译生成的烧录bin文件路径:projects/doorbell/build/bk7258/doorbell/package/all-app.bin
3.3 基本操作流程
设备上电启动
测试机(Android)下载IOT应用到设备,下载地址: <https://dl.bekencorp.com/apk/BekenIot.apk>
自行创建账号,并完成登录
测试机打开IOT应用,添加设备,选择:
可视门铃,DL设备存在01-18,和DEBUG,建议先选择BK7258_DL_01进行尝试使用。点进去后里面详细介绍了使用的外设,包括UVC摄像头、H.264编码器、SD卡存储、LCD屏幕、语音功能等。开始添加,选择非5G的WiFi,连接成功后,点击下一步,开始通过蓝牙进行配网检查扫描到的设备蓝牙广播,点击IP地址匹配的进行连接,会自动完成100%的配网
配网完成之后会自动打开UVC摄像头,且打开网络图传,传输的格式是H.264,图像的分辨率为864X480
打开其他外设,可以在IOT应用上进行控制。
4 doorbell视频流方案
本项目支持两种摄像头方案,均采用多消费者帧队列架构,实现高效的视频流处理和分发。
4.1 方案一:UVC摄像头 + MJPEG + LCD显示 + H264网络传输
┌─────────────────────────────────────────────────────────────────────────────┐
│ UVC Camera (MJPEG输出) │
└──────────────────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ MJPEG Frame Queue (V2) │
│ - 支持多消费者并发访问 │
│ - 引用计数自动管理 │
│ - consumer_mask: 0x02 │
└──────────────┬───────────────┘
│
▼
┌─────────────────────┐
│ Decoder Consumer │
│ (CONSUMER_DECODER) │
│ ID: 0x02 │
└──────────┬──────────┘
│
│ MJPEG解码
▼
┌─────────────────────┐
│ YUV Buffer │
│ (直接内存申请) │
│ 非队列管理 │
└──────────┬──────────┘
│
┌──────────────┴──────────────┐
│ │
▼ ▼
┌─────────────────────┐ ┌────────────────────┐
│ Video Pipeline │ │ H264 Encoder │
│ 视频处理: │ │ Pipeline │
│ 1. 旋转(可选) │ │ │
│ 2. 缩放(可选) │ │ 输入: YUV原始数据 │
│ 3. 格式转换 │ │ 输出: H264编码流 │
└──────────┬──────────┘ └────────┬───────────┘
│ │
│ 处理后图像 │ H264编码
│ (直接内存) │
▼ ▼
┌─────────────────────┐ ┌────────────────────┐
│ LCD Display │ │ H264 Frame │
│ Driver │ │ Queue (V2) │
│ │ │ consumer_mask:0x01 │
└─────────────────────┘ └────────┬───────────┘
│
▼
┌────────────────────┐
│ WiFi Transfer │
│ Consumer (0x01) │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ 网络传输 │
│ (CS2/TCP/UDP) │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ 对端设备 │
│ 解码显示 │
└────────────────────┘
流程说明:
MJPEG采集与分发 - UVC摄像头直接输出MJPEG压缩流(默认864x480@30fps) - MJPEG帧通过
frame_queue_v2_complete()放入MJPEG帧队列 - 解码器注册为消费者(CONSUMER_DECODER),通过frame_queue_v2_get_frame()获取MJPEG帧MJPEG解码 - 解码器将MJPEG解码为YUV格式 - YUV数据直接使用内存申请,不经过YUV Frame Queue - 自动选择硬件或软件解码器 - 解码后的YUV buffer直接传递给后续模块
本地显示路径 - YUV buffer通过视频处理管道进行:
软件/硬件旋转(支持0°/90°/180°/270°)
图像缩放(适配LCD分辨率)
像素格式转换(RGB565/RGB888等)
处理后的图像数据也是直接内存申请
最终通过LCD驱动显示到屏幕
网络传输路径 - 解码后的YUV buffer直接送入H264编码器 - H264编码器进行硬件编码 - H264帧通过
frame_queue_v2_complete()放入H264帧队列 - WiFi传输模块注册为H264消费者(CONSUMER_TRANSMISSION) - 通过frame_queue_v2_get_frame()获取H264帧 - 通过CS2/TCP/UDP协议传输到对端设备内存管理说明 - MJPEG Frame Queue:使用V2队列管理,支持多消费者 - YUV Buffer:解码模块直接申请和释放内存,不使用队列 - 处理后图像:视频处理管道直接申请和释放内存 - H264 Frame Queue:使用V2队列管理,支持网络传输消费者
4.2 方案二:DVP摄像头 + 硬件双输出(YUV + H264)
┌─────────────────────────────────────────────────────────────────────────────┐
│ DVP Camera (硬件双输出:YUV + H264) │
│ 内部硬件模块直接输出两路数据 │
└────────────────────┬───────────────────────────┬────────────────────────────┘
│ │
│ YUV输出 │ H264输出
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ YUV Frame Queue (V2) │ │ H264 Frame Queue (V2) │
│ - 多消费者支持 │ │ - 多消费者支持 │
│ - consumer_mask: 0x02 │ │ - consumer_mask: 0x01 │
└──────────┬───────────────┘ └──────────┬───────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ LCD Display │ │ WiFi Transfer │
│ Consumer (0x02) │ │ Consumer (0x01) │
└──────────┬──────────┘ └────────┬────────────┘
│ │
│ 视频处理: │
│ 1. 旋转(可选) │
│ 2. 缩放(可选) │
│ 3. 格式转换 │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ LCD屏幕显示 │ │ 网络传输 │
│ │ │ (CS2/TCP/UDP) │
└─────────────────────┘ └────────┬────────────┘
│
▼
┌─────────────────────┐
│ 对端设备 │
│ 解码显示 │
└─────────────────────┘
流程说明:
DVP硬件双输出 - DVP摄像头内部集成硬件模块 - 同时输出两路数据:
YUV原始数据 → 通过
frame_queue_v2_complete()放入YUV Frame QueueH264编码数据 → 通过
frame_queue_v2_complete()放入H264 Frame Queue
无需软件编码器,硬件直接完成H264编码
显示路径(YUV消费) - LCD显示模块注册为YUV消费者(CONSUMER_DECODER = 0x02) - 通过
frame_queue_v2_get_frame(IMAGE_YUV, CONSUMER_DECODER, timeout)获取YUV帧 - 经过视频处理管道:软件/硬件旋转(支持0°/90°/180°/270°)
图像缩放(适配LCD分辨率)
像素格式转换(RGB565/RGB888等)
显示到LCD屏幕
使用完毕调用
frame_queue_v2_release_frame(IMAGE_YUV, CONSUMER_DECODER, frame)释放
网络传输路径(H264消费) - WiFi传输模块注册为H264消费者(CONSUMER_TRANSMISSION = 0x01) - 通过
frame_queue_v2_get_frame(IMAGE_H264, CONSUMER_TRANSMISSION, timeout)获取H264帧 - 直接通过CS2/TCP/UDP协议传输到对端设备 - 使用完毕调用frame_queue_v2_release_frame(IMAGE_H264, CONSUMER_TRANSMISSION, frame)释放性能优势 - 硬件双输出:DVP内部一次处理,同时输出YUV和H264 - 无软件编码开销:H264由硬件直接生成,CPU占用最低 - 零延迟共享:两路数据独立,互不影响 - 完全并行:显示和传输完全独立,无竞争 - 最低延迟:端到端延迟最短,适合实时性要求最高的场景
Frame Queue管理 - YUV Frame Queue:管理YUV帧,LCD显示消费 - H264 Frame Queue:管理H264帧,网络传输消费 - 两个队列完全独立,使用V2多消费者机制
4.3 两种方案对比
对比项 |
方案一:UVC摄像头 |
方案二:DVP摄像头 |
|---|---|---|
摄像头输出 |
MJPEG压缩流 |
硬件双输出:YUV + H264 |
使用的Frame Queue |
MJPEG Queue + H264 Queue |
YUV Queue + H264 Queue |
YUV数据管理 |
解码后直接内存申请(不用队列) |
DVP硬件输出到YUV Queue |
H264生成方式 |
软件编码(CPU消耗) |
硬件直接输出(无CPU消耗) |
是否需要解码 |
需要MJPEG解码 |
不需要解码 |
是否需要编码 |
需要H264软件编码 |
不需要编码(硬件输出) |
CPU占用 |
高(解码 + 编码) |
低(仅视频处理) |
端到端延迟 |
较高(解码+编码) |
最低(硬件直出) |
多消费者支持 |
MJPEG和H264支持 |
YUV和H264都支持 |
适用场景 |
USB摄像头 |
板载DVP摄像头 |
4.4 关键技术特性
4.4.1 多消费者架构
消费者类型: -
CONSUMER_TRANSMISSION (0x01):网络传输 -CONSUMER_DECODER (0x02):解码器 -CONSUMER_STORAGE (0x04):存储(可选) -CONSUMER_RECOGNITION (0x08):识别(可选)
4.4.2 帧队列配置
格式 |
帧数量 |
主要用途 |
使用场景 |
|---|---|---|---|
MJPEG |
4个 |
UVC摄像头输出,支持解码器消费 |
方案一:UVC摄像头 |
H264 |
6个 |
编码后的H264流,支持网络传输消费 |
方案一:软件编码输出;方案二:DVP硬件输出 |
YUV |
3个 |
DVP摄像头硬件输出,支持显示消费 |
方案二:DVP摄像头 |
重要说明:
- 方案一(UVC):
✅ MJPEG Frame Queue:UVC摄像头输出
❌ YUV数据:解码后**直接内存申请**(不使用YUV Frame Queue)
✅ H264 Frame Queue:软件编码器输出
- 方案二(DVP):
✅ YUV Frame Queue:DVP硬件直接输出YUV
✅ H264 Frame Queue:DVP硬件直接输出H264(无需软件编码)
📌 关键优势:DVP内部硬件一次处理,同时输出两路数据
4.4.3 性能优化
中断安全:支持在中断上下文调用malloc/complete
零拷贝:多消费者共享同一帧数据
自动复用:frame buffer自动回收复用
慢消费者保护:自动丢弃旧帧,不阻塞系统
4.4.4 典型性能指标
- 方案一(UVC)性能:
MJPEG解码:864x480@30fps,硬解延迟<33ms
H264软件编码:864x480@30fps,延迟<50ms
端到端延迟:<200ms(WiFi正常条件下)
CPU占用:中等(解码 + 编码)
- 方案二(DVP)性能:
YUV输出:硬件直出,零延迟
H264输出:硬件直出,零延迟
端到端延迟:<100ms(WiFi正常条件下,最优)
CPU占用:极低(仅YUV处理)
- 网络传输:
支持1Mbps~8Mbps自适应码率
支持CS2/TCP/UDP多种协议
5 API参考
本章节提供项目中核心功能的API接口说明,这些接口通过封装SDK实现了高级功能调用。
备注
建议开发者不要直接调用以下接口实现自定义方案,而是参考这些接口的实现方式,通过组合封装SDK接口来构建符合自身需求的功能模块。
5.1 摄像头管理API
5.1.1 doorbell_camera_turn_on
/**
* @brief 开启摄像头设备
*
* @param parameters 摄像头参数结构体指针
* - id: 摄像头设备ID (UVC_DEVICE_ID或其他DVP设备ID)
* - width: 图像宽度
* - height: 图像高度
* - format: 图像格式 (0:MJPEG, 1:H264)
* - protocol: 传输协议
* - rotate: 旋转角度
*
* @return int 操作结果
* - BK_OK: 成功
* - BK_FAIL: 失败
*
* @note 此函数会:
* 1. 初始化frame queue V2用于图像帧缓存管理(多消费者支持)
* - Frame_buffer: frame_queue_v2_init_all
* 2. 根据摄像头类型(UVC或DVP)分别调用相应的开启函数
* - DVP: dvp_camera_turn_on
* * 硬件直接输出YUV到YUV Frame Queue
* * 硬件直接输出H264到H264 Frame Queue
* - UVC: uvc_camera_turn_on
* * 输出MJPEG到MJPEG Frame Queue
* 3. 初始化视频处理管道和H264编码器(仅UVC方案需要)
* - H264: doorbell_h264_encode_turn_on(DVP方案无需此步骤)
* 4. 配置图像旋转处理(如果显示控制器已初始化)
* - ROTATE: bk_video_pipeline_open_rotate
* 5. 自动检测并选择硬件或软件解码(仅UVC方案)
*
* @note 内存管理方式:
* - UVC方案:MJPEG使用队列,解码后YUV直接申请内存,H264软件编码后使用队列
* - DVP方案:硬件直接输出YUV和H264,都使用Frame Queue管理(无需软件编码)
*/
int doorbell_camera_turn_on(camera_parameters_t *parameters);
5.1.2 doorbell_camera_turn_off
/**
* @brief 关闭摄像头设备
*
* @return int 操作结果
* - BK_OK: 成功
* - BK_FAIL: 失败
*
* @note 此函数会:
* 1. 如果当前摄像头类型为UVC摄像头,关闭H264编码器管道(软件编码器)
* 2. 根据摄像头类型调用相应的关闭函数
* - UVC: uvc_camera_turn_off()
* * 关闭H264软件编码器(如果已开启)
* * 断开UVC设备连接
* * 释放MJPEG解码器资源
* - DVP: dvp_camera_turn_off()
* * 停止DVP硬件输出(YUV + H264)
* * 关闭摄像头电源
* 3. 释放摄像头相关资源,包括:
* - 关闭摄像头硬件
* - 删除摄像头控制器
* - 取消flash操作通知注册
*
* @warning 调用此函数前应确保摄像头已正确开启,否则可能导致资源泄漏
* @see doorbell_camera_turn_on()
*/
int doorbell_camera_turn_off(void);
5.2 H.264编码API(仅UVC方案使用)
> 注意:此API仅用于UVC方案的软件H264编码。DVP方案使用硬件直接输出H264,无需此编码器。
5.2.1 doorbell_h264_encode_turn_on
/**
* @brief 开启H264编码器(仅UVC方案)
*
* @param parameters 摄像头参数结构体指针
* - width: 编码图像宽度
* - height: 编码图像高度
* - rotate: 图像旋转角度
*
* @return int 操作结果
* - BK_OK: 成功
* - BK_FAIL: 失败
*
* @note 此函数仅在UVC方案中使用,用于将解码后的YUV编码为H264:
* 1. 配置视频处理管道参数,包括JPEG解码回调函数
* 2. 如果视频管道句柄为空,创建新的视频处理管道
* 3. 配置H264编码器参数:
* - 设置编码分辨率(width x height)
* - 设置帧率为30FPS
* - 配置软件旋转角度
* - 设置H264编码回调函数(内存分配和编码完成回调)
* 4. 打开H264编码器管道
* 5. 使用doorbell_h264e_cbs回调结构体:
* - h264e_frame_malloc: 从H264 Frame Queue分配内存
* - h264e_frame_complete: 编码完成后放入H264 Frame Queue
*
* @note DVP方案不调用此函数,因为DVP硬件直接输出H264到H264 Frame Queue
*
* @warning 调用此函数前应确保摄像头设备已正确初始化
* @see doorbell_h264_encode_turn_off()
*/
int doorbell_h264_encode_turn_on(camera_parameters_t *parameters);
5.2.2 doorbell_h264_encode_turn_off
/**
* @brief 关闭H264编码器(仅UVC方案)
*
* @return int 操作结果
* - BK_OK: 成功
* - BK_FAIL: 失败
*
* @note 此函数仅在UVC方案中使用:
* 1. 检查视频管道句柄是否为空,如果为空则直接返回成功
* 2. 调用bk_video_pipeline_close_h264e()关闭H264编码器管道
* 3. 记录关闭日志信息
* 4. 释放H264编码器相关资源
*
* @note DVP方案不调用此函数,因为使用硬件直接输出H264
*
* @attention 此函数仅关闭H264编码器,不会关闭整个摄像头设备
* @warning 调用此函数前应确保H264编码器已正确开启
* @see doorbell_h264_encode_turn_on()
*/
int doorbell_h264_encode_turn_off(void);
5.3 图传API
5.3.1 doorbell_video_transfer_turn_on
/**
* @brief 开启视频传输功能
*
* @return int 操作结果
* - BK_OK: 操作成功
* - BK_FAIL: 操作失败
*
* @note 此函数负责开启视频传输功能,主要功能包括:
* - 检查视频信息结构体是否有效
* - 检查摄像头是否已开启
* - 验证视频传输回调函数是否设置
* - 注册传输任务为H264帧队列消费者(CONSUMER_TRANSMISSION)
* - 通过WiFi传输框架开启视频帧传输
* - 根据传输格式配置传输参数
*
* @note 数据来源:
* - UVC方案:从H264 Frame Queue获取软件编码后的H264帧
* - DVP方案:从H264 Frame Queue获取硬件直接输出的H264帧
*
* @warning 调用此函数前应确保摄像头已正确开启
*
* @see doorbell_video_transfer_turn_off()
*/
int doorbell_video_transfer_turn_on(void);
5.3.2 doorbell_video_transfer_turn_off
/**
* @brief 关闭视频传输功能
*
* @return int 操作结果
* - BK_OK: 操作成功
* - BK_FAIL: 操作失败
*
* @note 此函数负责关闭视频传输功能,主要功能包括:
* - 检查视频信息结构体是否有效
* - 检查摄像头是否已开启
* - 通过WiFi传输框架关闭视频帧传输
* - 注销传输任务消费者(CONSUMER_TRANSMISSION)
* - 自动回收该消费者未访问的帧
* - 清理视频传输相关资源
* - 根据配置关闭CS2图像定时器
*
* @warning 调用此函数前应确保视频传输功能已正确开启
*
* @see doorbell_video_transfer_turn_on()
*/
int doorbell_video_transfer_turn_off(void);
5.4 显示API
5.4.1 doorbell_display_turn_on
/**
* @brief 开启显示设备
*
* @param parameters 显示参数结构体指针
* - id: 显示设备ID
* - rotate_angle: 旋转角度
* - pixel_format: 像素格式 (0:硬件旋转, 1:软件旋转)
*
* @return int 操作结果
* - EVT_STATUS_OK: 成功
* - EVT_STATUS_ERROR: 失败
* - EVT_STATUS_ALREADY: 设备已开启
*
* @note 此函数会:
* 1. 初始化frame queue V2用于图像帧缓存管理(多消费者支持)
* 2. 检查显示设备是否已开启,如果已开启则返回EVT_STATUS_ALREADY
* 3. 根据设备ID获取LCD设备配置
* 4. 根据LCD类型(RGB/MCU8080)创建相应的显示控制器
* 5. 创建视频处理管道并配置旋转参数
* 6. 如果使用DVP摄像头,注册YUV帧队列的显示消费者
* 7. 打开显示控制器和LCD背光
* 8. 设置设备信息结构体中的LCD相关参数
*
* @note 数据来源和内存管理:
* - DVP方案:
* * 从YUV Frame Queue获取DVP硬件输出的YUV帧
* * 注册为YUV消费者(CONSUMER_DECODER)
* * 显示后通过frame_queue_v2_release_frame释放
* - UVC方案:
* * 解码器直接申请YUV内存(不使用YUV Frame Queue)
* * 视频处理管道直接使用解码器提供的YUV数据
* * 显示后直接释放内存(不调用frame_queue_v2_release_frame)
*
* @warning 如果设备初始化失败,会清理已分配的资源
* @see doorbell_display_turn_off()
*/
int doorbell_display_turn_on(display_parameters_t *parameters);
5.4.2 doorbell_display_turn_off
/**
* @brief 关闭显示设备
*
* @return int 操作结果
* - 0: 成功
* - EVT_STATUS_ALREADY: 设备已关闭
* - EVT_STATUS_ERROR: 失败
*
* @note 此函数会:
* 1. 检查显示设备是否已关闭,如果已关闭则返回EVT_STATUS_ALREADY
* 2. 关闭LCD背光
* 3. 关闭视频处理管道的旋转功能
* 4. 如果使用DVP摄像头,注销YUV帧队列的显示消费者(CONSUMER_DECODER)
* 5. 自动回收该消费者未访问的YUV帧(仅DVP方案)
* 6. 关闭显示控制器
* 7. 删除显示控制器句柄
* 8. 重置设备信息结构体中的LCD相关参数
*
* @note 消费者注销:
* - DVP方案:调用frame_queue_v2_unregister_consumer注销YUV消费者
* - UVC方案:不需要注销消费者(解码的YUV数据不使用帧队列)
*
* @warning 此函数会释放所有显示相关的资源
* @see doorbell_display_turn_on()
*/
int doorbell_display_turn_off(void);
5.5 音频API
5.5.1 doorbell_audio_turn_on
/**
* @brief 开启音频设备
*
* @param parameters 音频参数结构体指针
* - aec: 回声消除使能标志
* - uac: USB音频设备使能标志
* - rmt_recorder_sample_rate: 远程录音采样率
* - rmt_player_sample_rate: 远程播放采样率
* - rmt_recoder_fmt: 远程录音编码格式
* - rmt_player_fmt: 远程播放编码格式
*
* @return int 操作结果
* - BK_OK: 操作成功
* - BK_FAIL: 操作失败或设备已开启
*
* @note 此函数负责开启音频设备,主要功能包括:
* - 检查音频设备是否已开启
* - 配置音频参数(采样率、编码格式等)
* - 根据UAC标志选择音频配置方式
* - 配置AEC回声消除参数
* - 初始化音频编码器/解码器
* - 初始化音频读取和写入句柄
* - 启动音频处理流程
*
* @warning 调用此函数前应确保音频参数正确配置
*
* @see doorbell_audio_turn_off()
*/
int doorbell_audio_turn_on(audio_parameters_t *parameters);
5.5.2 doorbell_audio_turn_off
/**
* @brief 关闭音频设备
*
* @return int 操作结果
* - BK_OK: 操作成功
* - BK_FAIL: 操作失败或设备已关闭
*
* @note 此函数负责关闭音频设备,主要功能包括:
* - 检查音频设备是否已关闭
* - 设置音频设备状态为关闭
* - 通知服务音频状态变化
* - 停止音频读取和写入操作
* - 反初始化音频相关句柄
* - 清理音频资源
*
* @warning 调用此函数前应确保音频设备已正确初始化
*
* @see doorbell_audio_turn_on()
*/
int doorbell_audio_turn_off(void);
5.6 帧缓冲区队列管理API(V2多消费者版本)
本项目使用帧队列V2版本,支持多消费者模式、引用计数和自动内存管理。主要特性: * 多消费者支持:多个消费者可同时访问同一帧 * 引用计数管理:自动跟踪帧的使用状态 * 慢消费者保护:自动丢弃旧帧,不阻塞快速消费者 * 动态注册:支持消费者动态注册和注销 * 内存复用:frame buffer自动复用,减少内存分配
消费者类型定义
#define CONSUMER_TRANSMISSION (1 << 0) // 视频传输任务
#define CONSUMER_DECODER (1 << 1) // 解码器任务
#define CONSUMER_STORAGE (1 << 2) // 存储任务
#define CONSUMER_RECOGNITION (1 << 3) // 识别任务
#define CONSUMER_CUSTOM_1 (1 << 4) // 自定义任务1
#define CONSUMER_CUSTOM_2 (1 << 5) // 自定义任务2
#define CONSUMER_CUSTOM_3 (1 << 6) // 自定义任务3
#define CONSUMER_CUSTOM_4 (1 << 7) // 自定义任务4
5.6.1 frame_queue_v2_init_all
/**
* @brief 初始化所有图像格式的帧队列数据结构(V2版本)
*
* @return bk_err_t 初始化结果
* - BK_OK: 所有队列初始化成功
* - BK_FAIL: 任一队列初始化失败
*
* @note 此函数初始化V2版本的帧队列系统,支持:
* - MJPEG格式帧队列(默认4个帧缓存)
* - H264格式帧队列(默认6个帧缓存)
* - YUV格式帧队列(默认3个帧缓存)
* - 双链表管理结构
* - 多消费者支持(最多8个)
* - 线程安全的临界区保护
*
* @warning 调用此函数前应确保系统资源充足
*
* @see frame_queue_v2_deinit_all()
*/
bk_err_t frame_queue_v2_init_all(void);
5.6.2 frame_queue_v2_deinit_all
/**
* @brief 释放所有图像格式的帧队列数据结构(V2版本)
*
* @return bk_err_t 释放结果
* - BK_OK: 所有队列释放成功
*
* @note 此函数负责释放V2帧队列的所有资源:
* - 释放free_list中的所有节点
* - 释放ready_list中的所有节点
* - 释放所有frame buffer内存
* - 反初始化信号量
* - 打印统计信息(malloc/complete/free计数)
*
* @see frame_queue_v2_init_all()
*/
bk_err_t frame_queue_v2_deinit_all(void);
5.6.3 frame_queue_v2_register_consumer
/**
* @brief 注册消费者
*
* @param format 图像格式
* - IMAGE_MJPEG: MJPEG格式
* - IMAGE_H264: H264格式
* - IMAGE_YUV: YUV格式
*
* @param consumer_id 消费者ID(CONSUMER_XXX宏定义)
* - 每个消费者使用唯一的位标识
*
* @return bk_err_t 操作结果
* - BK_OK: 注册成功
* - BK_FAIL: 注册失败
*
* @note 此函数注册一个新消费者:
* - 更新active_consumers掩码
* - 初始化消费者信息结构
* - 清理ready_list中consumer_mask=0的旧帧
* - 之后该消费者可以访问所有新产生的帧
*
* @warning 消费者ID必须是2的幂(单一bit位)
*
* @see frame_queue_v2_unregister_consumer()
* @see frame_queue_v2_get_frame()
*/
bk_err_t frame_queue_v2_register_consumer(image_format_t format, uint32_t consumer_id);
5.6.4 frame_queue_v2_unregister_consumer
/**
* @brief 注销消费者
*
* @param format 图像格式
* @param consumer_id 消费者ID
*
* @return bk_err_t 操作结果
* - BK_OK: 注销成功
* - BK_FAIL: 注销失败
*
* @note 此函数注销消费者并清理资源:
* - 从active_consumers中移除该消费者
* - 更新ready_list中所有未访问帧的consumer_mask
* - 自动回收不再需要的帧到free_list(保留frame buffer供复用)
* - 打印统计信息(get/release计数)
*
* @warning 注销后该消费者不应再调用get_frame/release_frame
*
* @see frame_queue_v2_register_consumer()
*/
bk_err_t frame_queue_v2_unregister_consumer(image_format_t format, uint32_t consumer_id);
5.6.5 frame_queue_v2_malloc
/**
* @brief 分配帧缓存(生产者使用)
*
* @param format 图像格式
* @param size 请求的帧大小
*
* @return frame_buffer_t* 分配的帧缓存指针,失败返回NULL
*
* @note 此函数为生产者分配帧缓存:
* - 从free_list获取空闲节点
* - 复用现有frame buffer或分配新的
* - 设置consumer_mask为当前active_consumers
* - 支持中断上下文调用(使用spinlock保护)
* - 如果free_list为空,对于MJPEG和YUV会自动复用最旧的帧
*
* @warning 分配后必须调用complete或cancel
*
* @see frame_queue_v2_complete()
* @see frame_queue_v2_cancel()
*/
frame_buffer_t *frame_queue_v2_malloc(image_format_t format, uint32_t size);
5.6.6 frame_queue_v2_complete
/**
* @brief 将填充好的帧放入就绪队列(生产者使用)
*
* @param format 图像格式
* @param frame 填充好的帧缓存
*
* @return bk_err_t 操作结果
* - BK_OK: 放入成功
* - BK_FAIL: 放入失败
*
* @note 此函数将帧提交给消费者:
* - 从free_list移到ready_list
* - 设置帧的timestamp和consumer_mask
* - 通过信号量唤醒等待的消费者
* - 支持中断上下文调用
*
* @warning 不要对同一帧重复调用complete
*
* @see frame_queue_v2_malloc()
* @see frame_queue_v2_get_frame()
*/
bk_err_t frame_queue_v2_complete(image_format_t format, frame_buffer_t *frame);
5.6.7 frame_queue_v2_cancel
/**
* @brief 取消已分配但失败的帧(生产者使用)
*
* @param format 图像格式
* @param frame 要取消的帧缓存
*
* @return bk_err_t 操作结果
* - BK_OK: 取消成功
* - BK_FAIL: 取消失败
*
* @note 当生产者在malloc后遇到错误时调用:
* - 重置节点的in_use标志
* - 保留frame buffer供下次复用
* - 节点保留在free_list中
* - 避免内存泄漏
*
* @see frame_queue_v2_malloc()
*/
bk_err_t frame_queue_v2_cancel(image_format_t format, frame_buffer_t *frame);
5.6.8 frame_queue_v2_get_frame
/**
* @brief 消费者获取帧(支持多消费者同时访问同一帧)
*
* @param format 图像格式
* @param consumer_id 消费者ID
* @param timeout 超时时间(毫秒)
* - 0: 非阻塞,立即返回
* - >0: 等待指定毫秒数
* - BEKEN_WAIT_FOREVER: 永久等待
*
* @return frame_buffer_t* 获取的帧缓存指针,失败返回NULL
*
* @note 多消费者安全访问:
* - 自动跳过已访问的帧
* - 自动清理consumer_mask=0的过期帧
* - 增加帧的引用计数
* - 标记该消费者已访问
* - 使用后必须调用release_frame
*
* @warning 必须先调用register_consumer注册
*
* @see frame_queue_v2_register_consumer()
* @see frame_queue_v2_release_frame()
*/
frame_buffer_t *frame_queue_v2_get_frame(image_format_t format, uint32_t consumer_id, uint32_t timeout);
5.6.9 frame_queue_v2_release_frame
/**
* @brief 消费者释放帧
*
* @param format 图像格式
* @param consumer_id 消费者ID
* @param frame 要释放的帧缓存
*
* @return void
*
* @note 引用计数管理:
* - 减少帧的引用计数
* - 当ref_count=0且所有需要访问的消费者都已访问时:
* * 释放frame buffer内存
* * 重置节点状态
* * 将节点移回free_list
* - 线程安全,支持多个消费者并发释放
*
* @warning 不要重复释放同一帧
*
* @see frame_queue_v2_get_frame()
*/
void frame_queue_v2_release_frame(image_format_t format, uint32_t consumer_id, frame_buffer_t *frame);
5.6.10 frame_queue_v2_get_stats
/**
* @brief 获取队列统计信息
*
* @param format 图像格式
* @param free_count 输出:空闲帧数量
* @param ready_count 输出:就绪帧数量
* @param total_malloc 输出:总分配次数
* @param total_complete 输出:总完成次数
* @param total_free 输出:总释放次数
*
* @return bk_err_t 操作结果
* - BK_OK: 获取成功
* - BK_FAIL: 获取失败
*
* @note 用于性能监控和调试
*/
bk_err_t frame_queue_v2_get_stats(image_format_t format,
uint32_t *free_count,
uint32_t *ready_count,
uint32_t *total_malloc,
uint32_t *total_complete,
uint32_t *total_free);
5.7 帧队列V2使用示例
5.7.1 生产者示例(摄像头采集)
// 1. 初始化帧队列
frame_queue_v2_init_all();
// 2. 分配帧缓存
frame_buffer_t *frame = frame_queue_v2_malloc(IMAGE_MJPEG, 100*1024);
if (frame == NULL) {
// 分配失败处理
return;
}
// 3. 填充数据到frame->frame
// ... camera capture data ...
// 4. 填充成功,放入就绪队列
frame_queue_v2_complete(IMAGE_MJPEG, frame);
// 或者填充失败,取消该帧
// frame_queue_v2_cancel(IMAGE_MJPEG, frame);
5.7.2 消费者示例(视频传输)
// 1. 注册为传输消费者
frame_queue_v2_register_consumer(IMAGE_MJPEG, CONSUMER_TRANSMISSION);
// 2. 获取帧(阻塞等待)
frame_buffer_t *frame = frame_queue_v2_get_frame(IMAGE_MJPEG,
CONSUMER_TRANSMISSION,
BEKEN_WAIT_FOREVER);
if (frame) {
// 3. 使用帧数据
// ... send frame data ...
// 4. 使用完毕,释放帧
frame_queue_v2_release_frame(IMAGE_MJPEG, CONSUMER_TRANSMISSION, frame);
}
// 5. 关闭时注销消费者
frame_queue_v2_unregister_consumer(IMAGE_MJPEG, CONSUMER_TRANSMISSION);
5.7.3 多消费者示例
// 场景:同时支持图传和存储
// 消费者1:视频传输任务
frame_queue_v2_register_consumer(IMAGE_H264, CONSUMER_TRANSMISSION);
// 消费者2:存储任务
frame_queue_v2_register_consumer(IMAGE_H264, CONSUMER_STORAGE);
// 两个消费者可以同时访问同一帧
// 消费者1获取并使用
frame_buffer_t *frame1 = frame_queue_v2_get_frame(IMAGE_H264, CONSUMER_TRANSMISSION, 100);
// 传输处理...
frame_queue_v2_release_frame(IMAGE_H264, CONSUMER_TRANSMISSION, frame1);
// 消费者2获取并使用(同一帧)
frame_buffer_t *frame2 = frame_queue_v2_get_frame(IMAGE_H264, CONSUMER_STORAGE, 100);
// 存储处理...
frame_queue_v2_release_frame(IMAGE_H264, CONSUMER_STORAGE, frame2);
// 只有当两个消费者都释放后,帧才会真正回收
5.7.4 慢消费者处理
// V2版本自动处理慢消费者:
// - 快速消费者不会被慢消费者阻塞
// - 慢消费者会自动丢失一些旧帧
// - 慢消费者总是能获取到最新的可用帧
// 示例:传输任务处理较慢
frame_queue_v2_register_consumer(IMAGE_MJPEG, CONSUMER_TRANSMISSION);
while (running) {
// 即使处理较慢,也会跳过旧帧,获取最新帧
frame_buffer_t *frame = frame_queue_v2_get_frame(IMAGE_MJPEG,
CONSUMER_TRANSMISSION,
100);
if (frame) {
// 慢速处理...
slow_network_send(frame);
frame_queue_v2_release_frame(IMAGE_MJPEG, CONSUMER_TRANSMISSION, frame);
}
}
6 注意事项
默认使用GPIO_28控制USB的LDO,拉高上电,注意GPIO冲突问题
默认使用GPIO_13控制LCD的LDO,拉高上电,注意GPIO冲突问题
默认使用GPIO_7控制LCD的背光,拉高有效,注意GPIO冲突问题
帧队列V2注意事项: - 消费者必须先注册才能获取帧 - 获取的帧使用后必须调用release_frame释放 - 不要重复释放同一帧 - 消费者关闭前必须注销,避免资源泄漏 - V2版本会自动复用frame buffer,无需手动管理内存
内存管理差异: - UVC方案(MJPEG软解码 + 软件H264编码):
✅ MJPEG帧:使用MJPEG Frame Queue V2管理
❌ 解码后的YUV数据:直接内存申请,不使用YUV Frame Queue
❌ 视频处理管道的输出:直接内存申请
✅ H264帧:软件编码后使用H264 Frame Queue管理
📌 CPU开销:需要软件解码MJPEG + 软件编码H264
DVP方案(硬件双输出):
✅ YUV帧:DVP硬件直接输出到YUV Frame Queue V2
✅ H264帧:DVP硬件直接输出到H264 Frame Queue V2
❌ 无需软件编码器:DVP内部ISP硬件同时生成两路数据
📌 CPU开销:最低,硬件直接输出
关键区别:
YUV Frame Queue**仅用于DVP摄像头**硬件输出
UVC方案的YUV数据**不经过队列**,直接内存申请
DVP方案**无需H264编码器**,硬件直接输出H264
7 系统架构
项目采用模块化设计,主要包含以下模块:
WiFi模块:负责网络连接和数据传输
媒体处理模块:处理图像采集、编码和存储
显示模块:管理LCD显示
蓝牙模块:提供蓝牙通信功能
各模块之间通过明确的API接口进行交互,保证了系统的可维护性和扩展性。
8 配置说明
项目的主要配置选项位于Kconfig文件中,可以通过修改配置来启用或禁用特定功能:
9 故障排除
9.1 常见问题及解决方案
9.1.1 摄像头无法识别
检查UVC摄像头连接是否正确,USB接口是否松动
确保摄像头电源供应正常,检查USB LDO是否拉高
确认摄像头驱动是否正确加载,可通过日志查看初始化过程
尝试更换兼容的UVC摄像头模块
9.1.2 显示异常
检查LCD连接是否正确,排线是否插紧
确认LCD LDO和背光是否正常工作
检查LCD型号是否与配置匹配
9.1.3 WiFi连接失败
确认WiFi名称和密码输入正确
确保使用的是2.4G WiFi网络(不支持5G WiFi)
检查设备与路由器的距离是否过远
尝试通过蓝牙配网功能重新连接
9.1.4 视频传输卡顿
检查网络连接质量,确保带宽充足
尝试降低视频分辨率和帧率设置
确认H.264编码器工作正常
检查帧缓冲区管理是否存在异常
使用
frame_queue_v2_get_stats()查看队列统计信息