OpenIPC开源架构
OpenIPC项目是一个开源的IP摄像头固件解决方案,其代码架构分为多个核心模块:1. 硬件驱动层(如ssr305_isp.c)负责图像信号处理,支持3D降噪和宽动态范围处理;2. 视频编码层(h265_encoder.c)实现H.265硬件编码,包含动态码率控制;3. 流媒体服务(rtsp_server.c)提供RTSP/ONVIF协议支持;4. 配置系统(yaml-cli.c)采用YAML格式
·
第一部分:OpenIPC整体架构树形结构
openipc/ ├── 📁 firmware/ # 主固件构建系统 │ ├── 📁 board/ # 板级配置 │ │ ├── 📁 hisilicon/ # 海思平台 │ │ │ ├── hi3516cv300/ │ │ │ ├── hi3516ev200/ │ │ │ └── hi3519v101/ │ │ ├── 📁 sigmastar/ # 星宸科技平台 │ │ │ ├── ssc335/ │ │ │ ├── ssc337/ │ │ │ └── ssr305/ # SSR305芯片支持 │ │ └── 📁 goke/ # 国科平台 │ ├── 📁 package/ # 软件包配置 │ │ ├── 📁 majestic/ # 流媒体核心 │ │ ├── 📁 openipc-tools/ # 工具集 │ │ └── 📁 webui/ # Web界面 │ └── 📁 configs/ # 构建配置 │ ├── 📁 linux/ # Linux内核源码 │ ├── 📁 arch/ # 架构相关代码 │ │ └── 📁 arm/ # ARM架构 │ │ ├── mach-hisi/ # 海思平台 │ │ └── mach-infinity/ # 星宸平台 │ ├── 📁 drivers/ # 设备驱动 │ │ ├── 📁 media/ # 多媒体驱动 │ │ │ ├── platform/ # 平台相关 │ │ │ │ ├── hisi/ # 海思VI/VPSS/VENC │ │ │ │ └── infinity/ # 星宸ISP驱动 │ │ │ └── i2c/ # 传感器驱动 │ │ │ ├── imx335.c # 索尼传感器 │ │ │ ├── imx307.c │ │ │ └── sc2235.c # 思特威传感器 │ │ └── 📁 spi/ # SPI接口驱动 │ └── 📁 include/ # 内核头文件 │ ├── 📁 uboot/ # U-Boot启动加载器 │ ├── 📁 board/ # 板级支持 │ ├── 📁 drivers/ # 驱动 │ └── 📁 include/ # 头文件 │ ├── 📁 majestic/ # 流媒体服务 │ ├── 📁 src/ # 源码 │ │ ├── 📁 encoder/ # 编码器模块 │ │ │ ├── h264_encoder.c # H.264编码 │ │ │ ├── h265_encoder.c # H.265编码 │ │ │ └── mjpeg_encoder.c # MJPEG编码 │ │ ├── 📁 streaming/ # 流媒体处理 │ │ │ ├── rtsp_server.c # RTSP服务器 │ │ │ ├── onvif_server.c # ONVIF实现 │ │ │ └── webrtc_stream.c # WebRTC支持 │ │ ├── 📁 osd/ # OSD叠加 │ │ │ ├── osd_manager.c # OSD管理 │ │ │ └── font_render.c # 字体渲染 │ │ └── 📁 config/ # 配置管理 │ │ └── yaml_config.c # YAML解析 │ └── 📁 include/ # 公共头文件 │ ├── 📁 majestic-plugins/ # 插件系统 │ ├── 📁 plugins/ # 插件实现 │ │ ├── 📁 motion_detection/ # 运动检测 │ │ ├── 📁 face_detection/ # 人脸检测 │ │ └── 📁 audio_alert/ # 音频告警 │ └── 📁 sdk/ # 插件SDK │ ├── 📁 microbe-webui/ # 轻量级WebUI │ ├── 📁 src/ # 源码 │ │ ├── 📁 pages/ # 页面 │ │ ├── 📁 components/ # 组件 │ │ └── 📁 api/ # API接口 │ └── 📁 public/ # 静态资源 │ └── 📁 tools/ # 工具集合 ├── 📁 ipctool/ # 硬件检测工具 ├── 📁 yaml-cli/ # YAML配置工具 └── 📁 sysupgrade/ # 系统升级工具
第二部分:关键代码深度分析
1.Majestic流媒体核心代码分析
1.1文件:majestic/src/encoder/h265_encoder.c
/**
* @file h265_encoder.c
* @brief H.265/HEVC视频编码器实现
* @author OpenIPC Team
* @version 1.0.0
* @date 2024
*
* @details 该模块实现了基于硬件加速的H.265编码功能,
* 支持动态码率调整、GOP结构配置和多码流输出。
* 关键数据结构:encoder_context_t 管理编码会话状态,
* frame_buffer_t 处理视频帧缓冲队列。
*
* @note 依赖硬件VPU(视频处理单元),需要内核驱动支持
* @copyright Copyright (c) 2024 OpenIPC
*/
#include "encoder.h"
#include <linux/videodev2.h>
/**
* @defgroup H265_Constants H.265编码常量定义
* @{
*/
#define H265_MIN_BITRATE (64 * 1024) /**< 最小码率:64kbps */
#define H265_MAX_BITRATE (20 * 1024 * 1024) /**< 最大码率:20Mbps */
#define H265_DEFAULT_FPS 25 /**< 默认帧率:25fps */
#define H265_DEFAULT_GOP_SIZE 50 /**< 默认GOP大小:2秒 @ 25fps */
#define H265_MAX_B_FRAMES 2 /**< 最大B帧数量 */
/** @} */
/**
* @enum h265_profile_t
* @brief H.265编码档次配置
*/
typedef enum {
H265_PROFILE_MAIN, /**< Main profile, 8bit色深支持 */
H265_PROFILE_MAIN10, /**< Main10 profile, 10bit色深支持 */
H265_PROFILE_MAIN_STILL /**< Main Still Picture profile */
} h265_profile_t;
/**
* @struct h265_config_t
* @brief H.265编码器配置参数结构体
*
* @var h265_config_t::width
* 编码宽度,必须是16的倍数,最小176,最大3840
* @var h265_config_t::height
* 编码高度,必须是16的倍数,最小144,最大2160
* @var h265_config_t::bitrate
* 目标码率,单位bps,受H265_MIN_BITRATE和H265_MAX_BITRATE限制
* @var h265_config_t::fps
* 编码帧率,范围1-120fps
*/
typedef struct {
uint32_t width; /**< 视频宽度,单位:像素 */
uint32_t height; /**< 视频高度,单位:像素 */
uint32_t bitrate; /**< 编码码率,单位:bps */
uint8_t fps; /**< 帧率,单位:fps */
uint8_t gop_size; /**< GOP大小(I帧间隔) */
h265_profile_t profile; /**< 编码档次 */
bool enable_cbr; /**< 是否启用恒定码率模式 */
} h265_config_t;
/**
* @struct encoder_stats_t
* @brief 编码器统计信息
*
* @var encoder_stats_t::encoded_frames
* 已编码帧总数,uint64_t类型防止32位溢出
* @var encoder_stats_t::total_bytes
* 输出码流总字节数,用于计算平均码率
* @var encoder_stats_t::fps_current
* 当前实时帧率,通过时间窗口计算
*/
typedef struct {
uint64_t encoded_frames; /**< 已编码帧总数 */
uint64_t total_bytes; /**< 输出码流总字节数 */
float fps_current; /**< 当前实时帧率 */
uint32_t dropped_frames; /**< 丢帧计数 */
} encoder_stats_t;
/**
* @brief 初始化H.265编码器实例
* @param config 编码器配置参数指针
* @return int 成功返回文件描述符,失败返回负值错误码
*
* @retval >0 编码器实例句柄
* @retval -EINVAL 参数无效
* @retval -ENOMEM 内存分配失败
* @retval -EIO 硬件初始化失败
*
* @code{.c}
* h265_config_t cfg = {
* .width = 1920,
* .height = 1080,
* .bitrate = 4096 * 1024, // 4Mbps
* .fps = 30,
* .gop_size = 60,
* .profile = H265_PROFILE_MAIN
* };
* int encoder_fd = h265_encoder_init(&cfg);
* @endcode
*/
int h265_encoder_init(const h265_config_t *config)
{
int ret;
struct v4l2_format fmt;
struct v4l2_ext_control ctrl[8];
struct v4l2_ext_controls ctrls;
/* 参数验证:检查指针和数值范围 */
if (!config) {
return -EINVAL; // 空指针错误
}
/* 分辨率验证:确保符合硬件限制 */
if (config->width % 16 != 0 || config->height % 16 != 0) {
return -EINVAL; // 分辨率必须是16的倍数
}
if (config->bitrate < H265_MIN_BITRATE ||
config->bitrate > H265_MAX_BITRATE) {
return -EINVAL; // 码率超出范围
}
/* 打开V4L2编码设备 */
int fd = open("/dev/video0", O_RDWR);
if (fd < 0) {
return -EIO; // 设备打开失败
}
/* 配置编码格式 */
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt.fmt.pix_mp.width = config->width;
fmt.fmt.pix_mp.height = config->height;
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; // 输入格式:NV12
fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
if (ret < 0) {
close(fd);
return -EIO;
}
/* 设置编码参数 */
memset(&ctrls, 0, sizeof(ctrls));
ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
ctrls.count = 6;
ctrls.controls = ctrl;
ctrl[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
ctrl[0].value = config->bitrate;
ctrl[1].id = V4L2_CID_MPEG_VIDEO_H265_PROFILE;
ctrl[1].value = config->profile;
ctrl[2].id = V4L2_CID_MPEG_VIDEO_H265_I_FRAME_QP;
ctrl[2].value = 26; // I帧量化参数
ctrl[3].id = V4L2_CID_MPEG_VIDEO_H265_P_FRAME_QP;
ctrl[3].value = 28; // P帧量化参数
ctrl[4].id = V4L2_CID_MPEG_VIDEO_H265_B_FRAME_QP;
ctrl[4].value = 30; // B帧量化参数
ctrl[5].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
ctrl[5].value = config->gop_size;
ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
if (ret < 0) {
close(fd);
return -EIO;
}
return fd; // 返回编码器句柄
}
/**
* @brief 编码一帧视频数据
* @param fd 编码器文件描述符
* @param frame 原始帧数据指针
* @param size 帧数据大小(字节)
* @param pts 显示时间戳(微秒)
* @return int 成功返回0,失败返回负值
*
* @note 输入帧格式必须是NV12,大小必须与初始化配置一致
* @warning 该函数是阻塞调用,直到编码完成或出错才返回
*/
int h265_encoder_encode_frame(int fd, const void *frame, size_t size, uint64_t pts)
{
struct v4l2_buffer buf;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
int ret;
/* 准备编码缓冲区 */
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
buf.memory = V4L2_MEMORY_MMAP;
buf.length = 1;
buf.m.planes = planes;
/* 查询空闲缓冲区 */
ret = ioctl(fd, VIDIOC_DQBUF, &buf);
if (ret < 0) {
return -EAGAIN; // 无可用缓冲区
}
/* 将原始帧数据复制到缓冲区 */
memcpy(planes[0].m.userptr, frame, size);
planes[0].bytesused = size;
/* 设置时间戳 */
buf.timestamp.tv_sec = pts / 1000000;
buf.timestamp.tv_usec = pts % 1000000;
/* 提交编码任务 */
buf.flags |= V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = ioctl(fd, VIDIOC_QBUF, &buf);
if (ret < 0) {
return -EIO;
}
return 0;
}
1.2 文件:majestic/src/streaming/rtsp_server.c
/**
* @file rtsp_server.c
* @brief RTSP流媒体服务器实现
* @author OpenIPC Team
* @version 2.1.0
*
* @details 实现RFC 2326标准RTSP协议,支持DESCRIBE、SETUP、PLAY等
* 方法。支持RTP over UDP和RTP over RTSP两种传输模式。
* 使用epoll实现高并发连接管理。
*/
#include "rtsp_server.h"
#include <netinet/in.h>
#include <sys/epoll.h>
/**
* @defgroup RTSP_Constants RTSP协议常量
* @{
*/
#define RTSP_DEFAULT_PORT 554 /**< RTSP默认端口 */
#define RTSP_MAX_CLIENTS 32 /**< 最大并发客户端数 */
#define RTSP_BUFFER_SIZE 8192 /**< 协议解析缓冲区大小 */
#define RTSP_TIMEOUT_SEC 60 /**< 会话超时时间 */
/** @} */
/**
* @enum rtsp_state_t
* @brief RTSP会话状态机
*/
typedef enum {
RTSP_STATE_INIT, /**< 初始状态 */
RTSP_STATE_READY, /**< SETUP完成,等待PLAY */
RTSP_STATE_PLAYING, /**< PLAYING,正在传输 */
RTSP_STATE_PAUSED, /**< 暂停状态 */
RTSP_STATE_TEARDOWN /**< 会话终止 */
} rtsp_state_t;
/**
* @struct rtsp_session_t
* @brief RTSP会话结构体
*
* @var rtsp_session_t::id
* 会话唯一标识符,使用UUID生成
* @var rtsp_session_t::state
* 当前会话状态
* @var rtsp_session_t::transport
* 传输模式(UDP/TCP)
* @var rtsp_session_t::client_port
* 客户端RTP端口(RTP/RTCP端口对)
*/
typedef struct {
char id[37]; /**< 会话ID:36字符UUID + null */
rtsp_state_t state; /**< 会话状态 */
int socket_fd; /**< 客户端socket */
struct sockaddr_in client_addr; /**< 客户端地址 */
struct {
enum {
RTSP_TRANSPORT_TCP, /**< RTP over RTSP (TCP) */
RTSP_TRANSPORT_UDP /**< RTP over UDP */
} mode;
uint16_t client_rtp_port; /**< 客户端RTP端口 */
uint16_t client_rtcp_port; /**< 客户端RTCP端口 */
uint16_t server_rtp_port; /**< 服务器RTP端口 */
} transport;
uint64_t last_active; /**< 最后活动时间戳(毫秒) */
uint32_t seq; /**< RTP序列号 */
uint32_t ssrc; /**< 同步源标识 */
} rtsp_session_t;
/**
* @struct rtsp_request_t
* @brief RTSP请求解析结果
*/
typedef struct {
char method[16]; /**< 请求方法:DESCRIBE, SETUP, PLAY等 */
char url[256]; /**< 请求URL:rtsp://... */
char version[10]; /**< RTSP版本:RTSP/1.0 */
struct {
char cseq[16]; /**< CSeq序列号 */
char session[37]; /**< 会话ID */
char transport[128];/**< Transport头 */
} headers;
} rtsp_request_t;
/**
* @brief 解析RTSP请求行
* @param buffer 原始请求数据
* @param req 解析结果输出结构体
* @return int 成功返回0,失败返回-1
*
* @example
* 输入: "DESCRIBE rtsp://192.168.1.100/stream RTSP/1.0\r\n"
* 输出: method="DESCRIBE", url="rtsp://192.168.1.100/stream"
*/
static int rtsp_parse_request_line(const char *buffer, rtsp_request_t *req)
{
/* 使用sscanf解析请求行 */
int ret = sscanf(buffer, "%15s %255s %9s",
req->method, req->url, req->version);
if (ret != 3) {
return -1; // 解析失败
}
return 0;
}
/**
* @brief 处理RTSP DESCRIBE请求
* @param session RTSP会话
* @param req 解析后的请求
* @return int 成功返回0
*
* @details 返回SDP描述信息,包含媒体流格式:
* - 视频流:H.264/H.265编码参数
* - 音频流:AAC/G.711编码参数
* - 控制URL:用于后续SETUP
*/
static int rtsp_handle_describe(rtsp_session_t *session, rtsp_request_t *req)
{
char response[RTSP_BUFFER_SIZE];
char sdp[1024];
/* 构建SDP描述 */
snprintf(sdp, sizeof(sdp),
"v=0\r\n"
"o=- %ld %ld IN IP4 %s\r\n"
"s=OpenIPC Stream\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
"m=video 0 RTP/AVP 96\r\n"
"a=rtpmap:96 H265/90000\r\n"
"a=control:track1\r\n"
"m=audio 0 RTP/AVP 97\r\n"
"a=rtpmap:97 MPEG4-GENERIC/16000/2\r\n"
"a=control:track2\r\n",
time(NULL), time(NULL), inet_ntoa(session->client_addr.sin_addr));
/* 构建RTSP响应 */
snprintf(response, sizeof(response),
"RTSP/1.0 200 OK\r\n"
"CSeq: %s\r\n"
"Content-Type: application/sdp\r\n"
"Content-Length: %zu\r\n"
"\r\n"
"%s",
req->headers.cseq, strlen(sdp), sdp);
/* 发送响应 */
send(session->socket_fd, response, strlen(response), 0);
return 0;
}
第三部分:关键数据结构与数据处理单位分析
1. 视频数据处理流水线
/**
* @file include/media_buffer.h
* @brief 视频缓冲区管理
* @author OpenIPC Team
*/
/**
* @defgroup Video_Data_Units 视频数据单位定义
* @{
*/
/**
* @brief 视频帧数据结构
*
* 数据流:Sensor -> ISP -> VPSS -> VENC -> RTP
* 单位转换:RAW(12-14bit) -> RGB(24-32bit) -> NV12(12bit/pixel) -> H.265(可变)
*/
typedef struct video_frame {
uint32_t width; /**< 宽度,单位:像素 */
uint32_t height; /**< 高度,单位:像素 */
enum {
PIX_FMT_RAW_BAYER, /**< RAW Bayer数据,12-14bit/pixel */
PIX_FMT_NV12, /**< NV12格式,12bit/pixel */
PIX_FMT_RGB24, /**< RGB888,24bit/pixel */
PIX_FMT_ARGB32 /**< ARGB8888,32bit/pixel */
} format;
/**
* @brief 帧数据缓冲区
*
* 大小计算公式:
* - RAW: width * height * 14/8 (14bit RAW)
* - NV12: width * height * 3/2 (Y:8bit + UV:8bit交错)
* - RGB24: width * height * 3
*/
uint8_t *data;
size_t data_size; /**< 实际数据大小,单位:字节 */
uint64_t timestamp; /**< 时间戳,单位:微秒 */
uint32_t sequence; /**< 帧序号 */
/**
* @brief 关键帧标志
*
* 编码域:
* - H.264: IDR帧
* - H.265: IRAP帧
*/
bool is_keyframe;
} video_frame_t;
/**
* @brief 编码码流数据结构
*/
typedef struct encoded_stream {
enum {
STREAM_H264, /**< H.264码流 */
STREAM_H265, /**< H.265/HEVC码流 */
STREAM_MJPEG /**< MJPEG码流 */
} codec;
/**
* @brief 编码数据分片
*
* H.265 NAL单元类型:
* - VPS: 0x40 (视频参数集)
* - SPS: 0x42 (序列参数集)
* - PPS: 0x44 (图像参数集)
* - IDR: 0x26 (关键帧)
* - Non-IDR: 0x02 (非关键帧)
*/
uint8_t nal_type;
uint8_t *data; /**< 编码数据 */
size_t length; /**< 数据长度,单位:字节 */
/**
* @brief 码率统计
*
* 平均码率 = total_bytes * 8 / duration_seconds
* 单位:bps (bits per second)
*/
uint32_t bitrate; /**< 瞬时码率,单位:bps */
} encoded_stream_t;
/** @} */
/**
* @defgroup Buffer_Management 缓冲区管理
* @{
*/
/**
* @brief 视频缓冲区池
*
* @details 使用内存池技术减少分配开销
* 典型配置:6个NV12帧缓冲区,每个1920x1080
* 内存占用:1920*1080*1.5*6 ≈ 18.7MB
*/
typedef struct buffer_pool {
uint32_t pool_size; /**< 缓冲区数量 */
uint32_t buffer_size; /**< 每个缓冲区大小,单位:字节 */
uint8_t **buffers; /**< 缓冲区指针数组 */
/**
* @brief 缓冲区状态位图
*
* 使用位图管理缓冲区占用状态
* 0: 空闲,1: 已占用
* 支持原子操作实现无锁访问
*/
uint32_t *occupied_bitmap;
uint32_t alloc_count; /**< 已分配计数 */
uint32_t peak_alloc; /**< 峰值分配数 */
} buffer_pool_t;
/**
* @brief 从池中分配缓冲区
* @param pool 缓冲区池
* @param size 请求大小
* @return uint8_t* 成功返回缓冲区指针,失败返回NULL
*
* @note 使用原子位操作实现无锁分配
* @see buffer_pool_t::occupied_bitmap
*/
static inline uint8_t *buffer_pool_alloc(buffer_pool_t *pool, size_t size)
{
uint32_t i;
if (size > pool->buffer_size) {
return NULL; // 请求过大
}
/* 遍历查找空闲位 */
for (i = 0; i < pool->pool_size; i++) {
uint32_t word_idx = i / 32;
uint32_t bit_idx = i % 32;
uint32_t mask = 1 << bit_idx;
/* 原子测试并设置 */
if (!(__atomic_test_and_set(&pool->occupied_bitmap[word_idx],
mask, __ATOMIC_ACQUIRE))) {
pool->alloc_count++;
if (pool->alloc_count > pool->peak_alloc) {
pool->peak_alloc = pool->alloc_count;
}
return pool->buffers[i];
}
}
return NULL; // 无可用缓冲区
}
第四部分:SSR305特定代码分析
1. 文件:linux/drivers/media/platform/infinity/ssr305_isp.c
/**
* @file ssr305_isp.c
* @brief SSR305芯片ISP(图像信号处理器)驱动
* @author SigmaStar/OpenIPC
* @version 1.2.0
*
* @details 针对SSR305芯片的ISP驱动实现
* 关键特性:
* - 3D降噪硬件加速
* - WDR(宽动态)处理
* - 黑光全彩模式
* - 低功耗状态管理
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <media/v4l2-subdev.h>
/**
* @defgroup SSR305_ISP_Registers SSR305 ISP寄存器定义
* @{
*/
#define SSR305_ISP_BASE 0xA0800000 /**< ISP寄存器基址 */
#define SSR305_ISP_SIZE 0x10000 /**< 寄存器空间大小 */
/* 核心控制寄存器 */
#define REG_ISP_CTRL 0x0000 /**< ISP控制寄存器 */
#define REG_ISP_STATUS 0x0004 /**< ISP状态寄存器 */
#define REG_ISP_INT_EN 0x0008 /**< 中断使能寄存器 */
#define REG_ISP_INT_STATUS 0x000C /**< 中断状态寄存器 */
/* 3DNR(3D降噪)模块寄存器 */
#define REG_3DNR_CTRL 0x1000 /**< 3DNR控制 */
#define REG_3DNR_STRENGTH 0x1004 /**< 降噪强度:0-255 */
#define REG_3DNR_MOTION 0x1008 /**< 运动检测阈值 */
/* WDR(宽动态)寄存器 */
#define REG_WDR_CTRL 0x2000 /**< WDR控制 */
#define REG_WDR_MODE 0x2004 /**< WDR模式:0-关闭,1-线间,2-帧间 */
#define REG_WDR_RATIO 0x2008 /**< WDR合成比例 */
/* 低功耗管理 */
#define REG_PMU_CTRL 0x3000 /**< 电源管理控制 */
#define REG_PMU_STATUS 0x3004 /**< 电源状态 */
/** @} */
/**
* @struct ssr305_isp_dev
* @brief SSR305 ISP设备结构体
*/
struct ssr305_isp_dev {
struct device *dev; /**< 设备指针 */
struct v4l2_subdev subdev; /**< V4L2子设备 */
struct v4l2_ctrl_handler ctrl_handler; /**< 控制句柄 */
void __iomem *base; /**< 寄存器基址 */
struct clk *clk; /**< 时钟 */
struct regulator *regulator; /**< 电源调节器 */
/**
* @struct isp_config
* @brief ISP运行时配置
*/
struct {
uint32_t width; /**< 图像宽度 */
uint32_t height; /**< 图像高度 */
uint32_t fps; /**< 帧率 */
uint32_t hdr_mode; /**< HDR模式 */
uint32_t nr_strength; /**< 降噪强度 */
bool low_power; /**< 低功耗模式 */
} config;
/* 统计信息 */
struct {
uint64_t frame_count; /**< 帧计数 */
uint64_t dropped_count; /**< 丢帧计数 */
uint32_t current_fps; /**< 当前帧率 */
uint32_t temperature; /**< 芯片温度 */
} stats;
};
/**
* @brief 设置3DNR(3D降噪)参数
* @param isp ISP设备指针
* @param strength 降噪强度 (0-255)
* @return int 0成功,负值失败
*
* @details 3DNR算法:
* 时域滤波: Y_t = α * Y_t-1 + (1-α) * Y_t
* 运动补偿: 根据REG_3DNR_MOTION调整α值
*/
static int ssr305_isp_set_3dnr(struct ssr305_isp_dev *isp, uint8_t strength)
{
uint32_t reg_val;
if (strength > 255) {
dev_err(isp->dev, "Invalid 3DNR strength: %d\n", strength);
return -EINVAL;
}
/* 设置降噪强度 */
writel(strength, isp->base + REG_3DNR_STRENGTH);
/* 根据强度自动调整运动阈值 */
reg_val = readl(isp->base + REG_3DNR_MOTION);
if (strength < 50) {
/* 低强度:运动敏感 */
reg_val = 10;
} else if (strength < 150) {
/* 中强度:平衡 */
reg_val = 30;
} else {
/* 高强度:运动不敏感,防止拖影 */
reg_val = 60;
}
writel(reg_val, isp->base + REG_3DNR_MOTION);
/* 启用3DNR */
reg_val = readl(isp->base + REG_3DNR_CTRL);
reg_val |= BIT(0); /* 使能位 */
writel(reg_val, isp->base + REG_3DNR_CTRL);
isp->config.nr_strength = strength;
dev_dbg(isp->dev, "3DNR set: strength=%d, motion=%d\n",
strength, reg_val);
return 0;
}
/**
* @brief 配置WDR(宽动态)模式
* @param isp ISP设备指针
* @param mode WDR模式
* @param ratio 合成比例
*
* @details WDR模式:
* 0: 关闭 - 标准线性模式
* 1: 线间WDR - 同一帧内长短曝光交替
* 2: 帧间WDR - 长短帧合成
*
* 动态范围提升:
* 线间WDR: +24dB
* 帧间WDR: +30dB
*/
static void ssr305_isp_set_wdr(struct ssr305_isp_dev *isp, int mode, int ratio)
{
uint32_t reg_val;
/* 设置WDR模式 */
reg_val = readl(isp->base + REG_WDR_MODE);
reg_val &= ~0x3;
reg_val |= (mode & 0x3);
writel(reg_val, isp->base + REG_WDR_MODE);
/* 设置合成比例 */
writel(ratio, isp->base + REG_WDR_RATIO);
/* 启用WDR */
reg_val = readl(isp->base + REG_WDR_CTRL);
if (mode > 0) {
reg_val |= BIT(0); /* 使能 */
dev_info(isp->dev, "WDR enabled: mode=%d, ratio=%d\n", mode, ratio);
} else {
reg_val &= ~BIT(0); /* 禁用 */
dev_info(isp->dev, "WDR disabled\n");
}
writel(reg_val, isp->base + REG_WDR_CTRL);
}
/**
* @brief 设置低功耗模式
* @param isp ISP设备指针
* @param enable true:进入低功耗,false:唤醒
*
* @details SSR305低功耗特性:
* 待机电流:< 5mA
* 唤醒时间:< 10ms
* 支持PIR运动唤醒
*/
static int ssr305_isp_set_low_power(struct ssr305_isp_dev *isp, bool enable)
{
uint32_t reg_val;
reg_val = readl(isp->base + REG_PMU_CTRL);
if (enable) {
/* 进入低功耗模式 */
reg_val |= BIT(0); /* 睡眠使能 */
/* 配置唤醒源:PIR/RTC/网络 */
reg_val |= BIT(4); /* PIR唤醒使能 */
reg_val |= BIT(5); /* RTC唤醒使能 */
/* 关闭不必要的时钟 */
clk_disable_unprepare(isp->clk);
isp->config.low_power = true;
dev_info(isp->dev, "Entering low power mode\n");
} else {
/* 唤醒 */
reg_val &= ~BIT(0); /* 清除睡眠位 */
/* 恢复时钟 */
clk_prepare_enable(isp->clk);
isp->config.low_power = false;
dev_info(isp->dev, "Waking from low power mode\n");
}
writel(reg_val, isp->base + REG_PMU_CTRL);
/* 等待状态变化 */
int timeout = 100;
while (timeout--) {
reg_val = readl(isp->base + REG_PMU_STATUS);
if ((enable && (reg_val & BIT(0))) ||
(!enable && !(reg_val & BIT(0)))) {
break;
}
udelay(100);
}
return 0;
}
/**
* @brief ISP中断处理函数
* @param irq 中断号
* @param data 设备指针
* @return irqreturn_t
*
* @details 处理:
* - 帧同步中断
* - 3DNR完成中断
* - 错误处理中断
*/
static irqreturn_t ssr305_isp_isr(int irq, void *data)
{
struct ssr305_isp_dev *isp = data;
uint32_t int_status;
/* 读取中断状态 */
int_status = readl(isp->base + REG_ISP_INT_STATUS);
/* 帧同步中断 */
if (int_status & BIT(0)) {
isp->stats.frame_count++;
/* 计算实时帧率 */
static uint64_t last_time;
static uint32_t frame_counter;
uint64_t now = ktime_get_ns();
frame_counter++;
if (now - last_time > NSEC_PER_SEC) {
isp->stats.current_fps = frame_counter;
frame_counter = 0;
last_time = now;
}
}
/* 3DNR完成中断 */
if (int_status & BIT(1)) {
/* 降噪处理完成 */
}
/* 错误中断 */
if (int_status & BIT(4)) {
dev_err(isp->dev, "ISP error detected\n");
isp->stats.dropped_count++;
}
/* 清除中断 */
writel(int_status, isp->base + REG_ISP_INT_STATUS);
return IRQ_HANDLED;
}
/* V4L2控制定义 */
static const struct v4l2_ctrl_ops ssr305_isp_ctrl_ops = {
.s_ctrl = ssr305_isp_s_ctrl,
};
/**
* @brief 注册ISP设备
* @param pdev 平台设备指针
* @return int
*/
static int ssr305_isp_probe(struct platform_device *pdev)
{
struct ssr305_isp_dev *isp;
struct resource *res;
int ret;
/* 分配设备结构体 */
isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp) {
return -ENOMEM;
}
isp->dev = &pdev->dev;
/* 获取寄存器资源 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
isp->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(isp->base)) {
return PTR_ERR(isp->base);
}
/* 获取时钟 */
isp->clk = devm_clk_get(&pdev->dev, "isp");
if (IS_ERR(isp->clk)) {
dev_err(&pdev->dev, "Failed to get ISP clock\n");
return PTR_ERR(isp->clk);
}
/* 使能时钟 */
clk_prepare_enable(isp->clk);
/* 注册V4L2子设备 */
v4l2_subdev_init(&isp->subdev, &ssr305_isp_subdev_ops);
isp->subdev.owner = THIS_MODULE;
snprintf(isp->subdev.name, sizeof(isp->subdev.name), "ssr305-isp");
/* 注册控制句柄 */
v4l2_ctrl_handler_init(&isp->ctrl_handler, 5);
v4l2_ctrl_new_std(&isp->ctrl_handler, &ssr305_isp_ctrl_ops,
V4L2_CID_3DNR_STRENGTH, 0, 255, 1, 128);
isp->subdev.ctrl_handler = &isp->ctrl_handler;
/* 注册中断 */
ret = devm_request_irq(&pdev->dev, platform_get_irq(pdev, 0),
ssr305_isp_isr, 0, "ssr305-isp", isp);
if (ret) {
dev_err(&pdev->dev, "Failed to request IRQ\n");
return ret;
}
platform_set_drvdata(pdev, isp);
dev_info(&pdev->dev, "SSR305 ISP initialized successfully\n");
return 0;
}
module_init(ssr305_isp_init);
module_exit(ssr305_isp_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SigmaStar/OpenIPC");
MODULE_DESCRIPTION("SSR305 ISP Driver");
第五部分:OpenIPC工具链代码分析
1. 文件:tools/ipctool/ipctool.c
/**
* @file ipctool.c
* @brief IPC硬件检测工具
* @author OpenIPC Team
* @version 2.0.0
*
* @details 多功能硬件检测工具,支持:
* - SoC型号识别
* - 传感器检测
* - 内存大小检测
* - 网络接口信息
* - 温度读取
*
* @note 这是OpenIPC固件中最重要的诊断工具
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
/**
* @defgroup Soc_Detection SoC检测模块
* @{
*/
/**
* @struct soc_info
* @brief SoC信息结构体
*/
typedef struct {
char family[32]; /**< 芯片系列:Hi3516, SSC335等 */
char model[32]; /**< 具体型号 */
uint32_t revision; /**< 版本号 */
uint32_t cpu_freq; /**< CPU频率,单位:MHz */
uint32_t cores; /**< CPU核心数 */
uint64_t total_mem; /**< 总内存,单位:字节 */
uint64_t free_mem; /**< 空闲内存,单位:字节 */
uint32_t temperature; /**< 芯片温度,单位:0.1°C */
} soc_info_t;
/**
* @brief 读取CPU信息
* @param info SoC信息输出指针
* @return int 成功返回0
*
* @details 通过读取/proc/cpuinfo解析SoC信息
* 示例输出:
* Processor: ARMv7 Processor rev 1 (v7l)
* Hardware: Hi3516EV200
* Revision: 0000
*/
static int read_cpu_info(soc_info_t *info)
{
FILE *fp;
char line[256];
char *ptr;
fp = fopen("/proc/cpuinfo", "r");
if (!fp) {
return -1;
}
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "Hardware")) {
ptr = strchr(line, ':');
if (ptr) {
ptr += 2; /* 跳过": " */
strncpy(info->family, ptr, sizeof(info->family) - 1);
/* 去除换行符 */
info->family[strcspn(info->family, "\n")] = 0;
}
}
if (strstr(line, "Revision")) {
ptr = strchr(line, ':');
if (ptr) {
info->revision = strtoul(ptr + 2, NULL, 16);
}
}
if (strstr(line, "BogoMIPS")) {
/* 估算CPU频率 */
ptr = strchr(line, ':');
if (ptr) {
float bogomips = atof(ptr + 2);
info->cpu_freq = (uint32_t)(bogomips / 2.0);
}
}
if (strstr(line, "processor")) {
info->cores++;
}
}
fclose(fp);
return 0;
}
/**
* @brief 读取内存信息
* @param info SoC信息输出指针
* @return int 成功返回0
*
* @details 解析/proc/meminfo获取内存大小
* 单位转换:kB -> bytes
*/
static int read_mem_info(soc_info_t *info)
{
FILE *fp;
char line[256];
fp = fopen("/proc/meminfo", "r");
if (!fp) {
return -1;
}
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "MemTotal:", 9) == 0) {
unsigned long kb;
sscanf(line, "MemTotal: %lu kB", &kb);
info->total_mem = kb * 1024;
}
if (strncmp(line, "MemFree:", 8) == 0) {
unsigned long kb;
sscanf(line, "MemFree: %lu kB", &kb);
info->free_mem = kb * 1024;
}
}
fclose(fp);
return 0;
}
/**
* @brief 读取芯片温度
* @param info SoC信息输出指针
* @return int 成功返回0
*
* @details 尝试多个可能的温度传感器路径:
* - /sys/class/thermal/thermal_zone0/temp
* - /proc/jz/thermal
* - /sys/devices/virtual/thermal/thermal_zone0/temp
*/
static int read_temperature(soc_info_t *info)
{
FILE *fp;
const char *paths[] = {
"/sys/class/thermal/thermal_zone0/temp",
"/sys/class/thermal/thermal_zone1/temp",
"/proc/jz/thermal",
NULL
};
for (int i = 0; paths[i]; i++) {
fp = fopen(paths[i], "r");
if (fp) {
int temp;
if (fscanf(fp, "%d", &temp) == 1) {
/* 处理不同格式:
* 有些是整数度:45
* 有些是千分之一度:45000
*/
if (temp > 1000) {
info->temperature = temp / 100; /* 转成0.1°C */
} else {
info->temperature = temp * 10;
}
fclose(fp);
return 0;
}
fclose(fp);
}
}
return -1;
}
/** @} */
/**
* @defgroup Sensor_Detection 传感器检测模块
* @{
*/
/**
* @struct sensor_info
* @brief 图像传感器信息
*/
typedef struct {
char name[32]; /**< 传感器型号:IMX335, SC2235等 */
uint8_t i2c_addr; /**< I2C地址 */
uint32_t width; /**< 最大宽度 */
uint32_t height; /**< 最大高度 */
uint32_t fps; /**< 最大帧率 */
char bus[16]; /**< I2C总线 */
} sensor_info_t;
/**
* @brief 传感器ID表
*
* @details 常见传感器I2C ID寄存器及预期值
*/
static const struct {
const char *name;
uint8_t addr;
uint8_t id_reg;
uint16_t id_value;
} sensor_ids[] = {
{"IMX335", 0x1A, 0x02, 0x0335}, /* Sony 5MP */
{"IMX307", 0x1A, 0x00, 0x0307}, /* Sony 2MP */
{"IMX327", 0x1A, 0x00, 0x0327}, /* Sony 2MP */
{"SC2235", 0x30, 0x31, 0x2235}, /* SmartSens 2MP */
{"SC3235", 0x30, 0x31, 0x3235}, /* SmartSens 5MP */
{"GC2053", 0x37, 0x03, 0x2053}, /* GalaxyCore 2MP */
{NULL, 0, 0, 0}
};
/**
* @brief 检测图像传感器
* @param sensor 传感器信息输出指针
* @return int 成功返回0,失败返回-1
*
* @details I2C通信协议:
* 1. 打开I2C总线设备
* 2. 设置从设备地址
* 3. 发送寄存器地址
* 4. 读取16位ID值
*/
static int detect_sensor(sensor_info_t *sensor)
{
int file;
char filename[32];
uint8_t reg;
uint16_t value;
struct i2c_msg msgs[2];
struct i2c_rdwr_ioctl_data msgset;
/* 尝试多个I2C总线 */
const char *buses[] = {"/dev/i2c-0", "/dev/i2c-1", "/dev/i2c-2", NULL};
for (int b = 0; buses[b]; b++) {
file = open(buses[b], O_RDWR);
if (file < 0) continue;
/* 遍历已知传感器 */
for (int i = 0; sensor_ids[i].name; i++) {
/* 设置I2C从设备地址 */
if (ioctl(file, I2C_SLAVE, sensor_ids[i].addr) < 0) {
continue;
}
/* 读取ID寄存器 */
reg = sensor_ids[i].id_reg;
if (sensor_ids[i].id_value > 0xFF) {
/* 16位ID:需要读两个字节 */
uint8_t buf[2];
struct i2c_msg rdmsg = {
.addr = sensor_ids[i].addr,
.flags = I2C_M_RD,
.len = 2,
.buf = buf
};
if (write(file, ®, 1) != 1) continue;
if (read(file, buf, 2) != 2) continue;
value = (buf[0] << 8) | buf[1];
} else {
/* 8位ID */
uint8_t val;
if (write(file, ®, 1) != 1) continue;
if (read(file, &val, 1) != 1) continue;
value = val;
}
/* 检查是否匹配 */
if (value == sensor_ids[i].id_value) {
strcpy(sensor->name, sensor_ids[i].name);
sensor->i2c_addr = sensor_ids[i].addr;
strcpy(sensor->bus, buses[b] + 9); /* 去掉"/dev/i2c-" */
close(file);
return 0;
}
}
close(file);
}
return -1;
}
/** @} */
/**
* @brief 主函数
* @param argc 参数个数
* @param argv 参数数组
* @return int
*
* @details 命令行选项:
* ipctool -a 显示所有信息
* ipctool -s 显示SoC信息
* ipctool -c 显示传感器信息
* ipctool -n 显示网络信息
* ipctool -t 显示温度
* ipctool -h 显示帮助
*/
int main(int argc, char **argv)
{
int opt;
int show_all = 1; /* 默认显示所有 */
int show_soc = 0;
int show_sensor = 0;
int show_net = 0;
int show_temp = 0;
while ((opt = getopt(argc, argv, "ascnth")) != -1) {
switch (opt) {
case 'a':
show_all = 1;
break;
case 's':
show_soc = 1;
show_all = 0;
break;
case 'c':
show_sensor = 1;
show_all = 0;
break;
case 'n':
show_net = 1;
show_all = 0;
break;
case 't':
show_temp = 1;
show_all = 0;
break;
case 'h':
printf("Usage: %s [options]\n", argv[0]);
printf("Options:\n");
printf(" -a Show all information\n");
printf(" -s Show SoC information\n");
printf(" -c Show sensor information\n");
printf(" -n Show network information\n");
printf(" -t Show temperature\n");
printf(" -h Show this help\n");
return 0;
}
}
/* 收集信息 */
soc_info_t soc = {0};
sensor_info_t sensor = {0};
if (show_all || show_soc || show_temp) {
read_cpu_info(&soc);
read_mem_info(&soc);
read_temperature(&soc);
}
if (show_all || show_sensor) {
detect_sensor(&sensor);
}
/* 显示信息 */
if (show_all || show_soc) {
printf("========== SoC Information ==========\n");
printf("Family: %s\n", soc.family);
printf("Revision: 0x%04x\n", soc.revision);
printf("CPU: %u MHz, %u core(s)\n", soc.cpu_freq, soc.cores);
printf("Memory: %.2f MB total, %.2f MB free\n",
soc.total_mem / (1024.0 * 1024.0),
soc.free_mem / (1024.0 * 1024.0));
}
if (show_all || show_sensor) {
printf("========== Sensor Information ==========\n");
if (sensor.name[0]) {
printf("Model: %s\n", sensor.name);
printf("I2C: %s @ 0x%02x\n", sensor.bus, sensor.i2c_addr);
} else {
printf("No sensor detected\n");
}
}
if (show_all || show_temp) {
printf("========== Temperature ==========\n");
if (soc.temperature) {
printf("SoC: %d.%d°C\n",
soc.temperature / 10, soc.temperature % 10);
} else {
printf("Not available\n");
}
}
return 0;
}
第六部分:YAML配置管理分析
1. 文件:tools/yaml-cli/yaml-cli.c
/**
* @file yaml-cli.c
* @brief YAML配置文件命令行工具
* @author OpenIPC Team
* @version 1.5.0
*
* @details 用于读写OpenIPC YAML配置文件的工具
* 支持:
* - 读取配置值
* - 写入配置值
* - 备份和恢复
* - JSON格式转换
*
* @note 主要操作 /etc/majestic.yaml 配置文件
*/
#include <yaml.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
/**
* @defgroup YAML_Paths YAML路径语法
* @{
*
* @details 使用点号分隔的路径语法:
* video0.width - 获取视频宽度
* video0.fps - 获取帧率
* osd.timestamp.enable - OSD时间戳开关
* network.ip - IP地址
*
* 支持通配符:
* video*.width - 所有视频流的宽度
*
* 支持数组索引:
* streams.0.enable - 第一个码流
* @}
*/
/**
* @struct yaml_node_path
* @brief YAML路径解析结果
*/
typedef struct {
char **keys; /**< 路径各段数组 */
int depth; /**< 路径深度 */
int is_array; /**< 是否数组访问 */
int array_index; /**< 数组索引 */
} yaml_path_t;
/**
* @brief 解析YAML路径字符串
* @param path_str 路径字符串,如"video0.width"
* @param path 解析结果输出
* @return int 成功返回0
*
* @example
* "video0.width" -> keys=["video0", "width"], depth=2
* "osd.timestamp.enable" -> keys=["osd", "timestamp", "enable"], depth=3
*/
static int parse_yaml_path(const char *path_str, yaml_path_t *path)
{
char *str = strdup(path_str);
char *token;
char *saveptr;
int count = 0;
/* 分配最大深度 */
path->keys = malloc(10 * sizeof(char*));
/* 分割路径 */
token = strtok_r(str, ".", &saveptr);
while (token && count < 10) {
/* 检查是否为数组访问 */
char *bracket = strchr(token, '[');
if (bracket) {
*bracket = '\0';
path->array_index = atoi(bracket + 1);
path->is_array = 1;
}
path->keys[count] = strdup(token);
count++;
token = strtok_r(NULL, ".", &saveptr);
}
path->depth = count;
free(str);
return 0;
}
/**
* @brief 递归查找YAML节点
* @param document YAML文档
* @param path 路径
* @param level 当前层级
* @return yaml_node_t* 找到的节点,NULL表示未找到
*/
static yaml_node_t* find_yaml_node(yaml_document_t *document,
yaml_path_t *path, int level)
{
yaml_node_t *node = yaml_document_get_root_node(document);
for (int i = 0; i < level; i++) {
if (!node) return NULL;
switch (node->type) {
case YAML_MAPPING_NODE:
/* 在映射中查找键 */
for (yaml_node_pair_t *pair = node->data.mapping.pairs.start;
pair < node->data.mapping.pairs.top;
pair++) {
yaml_node_t *key = yaml_document_get_node(document, pair->key);
if (key && key->type == YAML_SCALAR_NODE) {
if (strcmp((char*)key->data.scalar.value,
path->keys[i]) == 0) {
node = yaml_document_get_node(document, pair->value);
break;
}
}
}
break;
case YAML_SEQUENCE_NODE:
/* 序列节点,使用索引 */
if (path->is_array && i == level - 1) {
int idx = path->array_index;
if (idx < node->data.sequence.items.top -
node->data.sequence.items.start) {
node = yaml_document_get_node(document,
node->data.sequence.items.start[idx]);
}
}
break;
default:
return NULL;
}
}
return node;
}
/**
* @brief 读取配置值
* @param path 配置路径
* @return int 成功返回0
*/
static int cmd_get(const char *path)
{
FILE *fh = fopen("/etc/majestic.yaml", "rb");
if (!fh) {
perror("Failed to open config file");
return -1;
}
yaml_parser_t parser;
yaml_document_t document;
yaml_parser_initialize(&parser);
yaml_parser_set_input_file(&parser, fh);
if (!yaml_parser_load(&parser, &document)) {
fprintf(stderr, "Failed to parse YAML\n");
fclose(fh);
return -1;
}
/* 解析路径并查找节点 */
yaml_path_t ypath;
parse_yaml_path(path, &ypath);
yaml_node_t *node = find_yaml_node(&document, &ypath, ypath.depth);
if (node && node->type == YAML_SCALAR_NODE) {
printf("%s\n", node->data.scalar.value);
} else {
fprintf(stderr, "Path not found or not a scalar\n");
}
/* 清理 */
yaml_document_delete(&document);
yaml_parser_delete(&parser);
fclose(fh);
return 0;
}
/**
* @brief 设置配置值
* @param path 配置路径
* @param value 要设置的值
* @return int 成功返回0
*/
static int cmd_set(const char *path, const char *value)
{
/* 先读取整个文档 */
FILE *fh = fopen("/etc/majestic.yaml", "rb");
if (!fh) return -1;
yaml_parser_t parser;
yaml_document_t document;
yaml_parser_initialize(&parser);
yaml_parser_set_input_file(&parser, fh);
if (!yaml_parser_load(&parser, &document)) {
fclose(fh);
return -1;
}
fclose(fh);
/* 查找并修改节点 */
yaml_path_t ypath;
parse_yaml_path(path, &ypath);
yaml_node_t *node = find_yaml_node(&document, &ypath, ypath.depth);
if (node && node->type == YAML_SCALAR_NODE) {
/* 更新现有节点 */
free(node->data.scalar.value);
node->data.scalar.value = strdup(value);
node->data.scalar.length = strlen(value);
} else {
/* 路径不存在,需要创建 */
fprintf(stderr, "Creating new paths not yet supported\n");
}
/* 写回文件 */
FILE *out = fopen("/etc/majestic.yaml", "wb");
if (!out) return -1;
yaml_emitter_t emitter;
yaml_emitter_initialize(&emitter);
yaml_emitter_set_output_file(&emitter, out);
yaml_emitter_open(&emitter);
yaml_emitter_dump(&emitter, &document);
yaml_emitter_close(&emitter);
yaml_emitter_delete(&emitter);
fclose(out);
/* 清理 */
yaml_document_delete(&document);
yaml_parser_delete(&parser);
return 0;
}
/**
* @brief 主函数
* @param argc 参数个数
* @param argv 参数数组
* @return int
*
* @example
* yaml-cli -g video0.width # 获取视频宽度
* yaml-cli -s video0.width 1920 # 设置视频宽度
* yaml-cli -d # 显示所有配置
* yaml-cli -b /backup.yaml # 备份配置
*/
int main(int argc, char **argv)
{
int opt;
char *get_path = NULL;
char *set_path = NULL;
char *set_value = NULL;
int dump = 0;
char *backup = NULL;
while ((opt = getopt(argc, argv, "g:s:b:d")) != -1) {
switch (opt) {
case 'g':
get_path = optarg;
break;
case 's':
set_path = optarg;
if (optind < argc && argv[optind][0] != '-') {
set_value = argv[optind++];
}
break;
case 'b':
backup = optarg;
break;
case 'd':
dump = 1;
break;
}
}
if (get_path) {
return cmd_get(get_path);
}
if (set_path && set_value) {
return cmd_set(set_path, set_value);
}
if (backup) {
/* 备份配置 */
char cmd[256];
snprintf(cmd, sizeof(cmd), "cp /etc/majestic.yaml %s", backup);
return system(cmd);
}
if (dump) {
/* 显示所有配置 */
FILE *fp = fopen("/etc/majestic.yaml", "rb");
if (fp) {
char buf[1024];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
fwrite(buf, 1, n, stdout);
}
fclose(fp);
}
}
return 0;
}
第七部分:数据处理流水线对比分析
1. 数据处理单元对比表
| 处理阶段 | 数据格式 | 典型大小(1080p) | 处理单元 | 延迟 | 关键代码位置 |
|---|---|---|---|---|---|
| RAW采集 | Bayer RAW | 1920x1080x14/8 = 3.6MB | 帧 | 33ms (30fps) | drivers/media/i2c/ |
| ISP处理 | RGB/NV12 | 1920x1080x1.5 = 3.1MB | 帧 | 16ms | ssr305_isp.c |
| 编码 | H.264/H.265 | 50-200KB (I帧) / 5-20KB (P帧) | 帧/Slice | 10ms | h265_encoder.c |
| RTP打包 | RTP包 | 1400字节/包 | 包 | 1ms | rtsp_server.c |
| 存储 | MP4/TS | 1小时≈1-2GB | 文件 | - | recorder.c |
2. 内存带宽计算
/**
* @brief 计算不同格式的内存带宽需求
*
* @note 以1080p@30fps为例:
* - RAW数据: 1920*1080*14/8 * 30 = 108.9 MB/s
* - NV12数据: 1920*1080*12/8 * 30 = 93.3 MB/s
* - RGB24数据: 1920*1080*3 * 30 = 186.6 MB/s
*
* 考虑DDR带宽限制,通常选择NV12作为处理格式
*/
static void calculate_bandwidth(void)
{
const int width = 1920;
const int height = 1080;
const int fps = 30;
/* 原始Bayer RAW (14bit) */
uint64_t raw_bps = (uint64_t)width * height * 14 * fps / 8;
/* NV12 (YUV420) */
uint64_t nv12_bps = (uint64_t)width * height * 12 * fps / 8;
/* RGB888 */
uint64_t rgb_bps = (uint64_t)width * height * 24 * fps / 8;
printf("Memory Bandwidth Requirements:\n");
printf(" RAW: %.2f MB/s\n", raw_bps / (1024.0 * 1024.0));
printf(" NV12: %.2f MB/s\n", nv12_bps / (1024.0 * 1024.0));
printf(" RGB: %.2f MB/s\n", rgb_bps / (1024.0 * 1024.0));
}
3. 码率控制算法对比
/**
* @file majestic/src/encoder/rate_control.c
* @brief 码率控制算法实现
*/
/**
* @enum rc_mode_t
* @brief 码率控制模式
*/
typedef enum {
RC_MODE_CBR, /**< 恒定码率:码率波动小,适合直播 */
RC_MODE_VBR, /**< 可变码率:画质稳定,适合存储 */
RC_MODE_ABR, /**< 平均码率:平衡模式 */
RC_MODE_CQP /**< 恒定质量:QP固定,质量恒定 */
} rc_mode_t;
/**
* @struct rc_params_t
* @brief 码率控制参数
*/
typedef struct {
rc_mode_t mode; /**< 控制模式 */
uint32_t target_kbps; /**< 目标码率,单位kbps */
uint32_t max_kbps; /**< 最大码率 */
uint32_t min_kbps; /**< 最小码率 */
uint8_t init_qp; /**< 初始QP值 0-51 */
uint8_t min_qp; /**< 最小QP */
uint8_t max_qp; /**< 最大QP */
/* 场景检测参数 */
uint32_t scene_change_threshold; /**< 场景切换阈值 */
uint8_t scene_change_qp_delta; /**< 场景切换QP调整 */
/* GOP层控制 */
uint8_t gop_size; /**< GOP大小 */
uint8_t intra_period; /**< I帧间隔 */
uint8_t idr_period; /**< IDR帧间隔 */
} rc_params_t;
/**
* @brief CBR码率控制器
*
* @details 使用PID反馈控制算法:
* QP_delta = Kp*error + Ki*integral + Kd*derivative
* error = target_bitrate - actual_bitrate
*/
static int cbr_rate_control(encoder_t *enc, frame_stats_t *stats)
{
static int64_t integral = 0;
static int64_t prev_error = 0;
const int Kp = 10; /* 比例系数 */
const int Ki = 2; /* 积分系数 */
const int Kd = 5; /* 微分系数 */
/* 计算当前码率 */
int64_t current_bitrate = calculate_bitrate(enc);
int64_t error = enc->target_bitrate - current_bitrate;
/* 积分项限制,防止积分饱和 */
integral += error;
if (integral > 1000) integral = 1000;
if (integral < -1000) integral = -1000;
/* 微分项 */
int64_t derivative = error - prev_error;
/* PID计算QP调整量 */
int qp_delta = (Kp * error + Ki * integral + Kd * derivative) / 100;
/* 限制QP调整范围 */
if (qp_delta > 6) qp_delta = 6;
if (qp_delta < -6) qp_delta = -6;
/* 更新QP值 */
uint8_t new_qp = enc->current_qp + qp_delta;
if (new_qp < enc->min_qp) new_qp = enc->min_qp;
if (new_qp > enc->max_qp) new_qp = enc->max_qp;
prev_error = error;
return new_qp;
}
/**
* @brief VBR码率控制器
*
* @details 基于场景复杂度调整:
* QP = base_qp + complexity_factor * scene_factor
* 复杂度使用SAD(绝对差值和)衡量
*/
static int vbr_rate_control(encoder_t *enc, frame_stats_t *stats)
{
/* 计算帧复杂度(使用SAD或方差) */
uint32_t complexity = stats->sad_value;
/* 归一化复杂度 */
float complexity_factor = (float)complexity / enc->avg_complexity;
/* 根据复杂度调整QP */
uint8_t base_qp = enc->target_qp;
int qp_adjust = (int)((complexity_factor - 1.0) * 5);
/* 场景切换处理 */
if (stats->is_scene_change) {
qp_adjust += enc->scene_change_qp_delta;
}
uint8_t new_qp = base_qp + qp_adjust;
/* 限制QP范围 */
if (new_qp < enc->min_qp) new_qp = enc->min_qp;
if (new_qp > enc->max_qp) new_qp = enc->max_qp;
return new_qp;
}
第八部分:性能优化关键点
1. 缓存一致性管理
/**
* @file arch/arm/mm/cache-v7.c
* @brief ARMv7缓存操作
*/
/**
* @brief DMA缓冲区缓存维护
* @param addr 缓冲区地址
* @param size 缓冲区大小
* @param dir DMA方向
*
* @note 关键:确保CPU和DMA看到的是一致的数据
* 在以下情况必须调用:
* - CPU写入数据给DMA处理前
* - DMA完成后CPU读取数据前
*/
static void dma_cache_maint(const void *addr, size_t size, int dir)
{
unsigned long start = (unsigned long)addr;
unsigned long end = start + size;
switch (dir) {
case DMA_FROM_DEVICE:
/* 数据从设备到CPU:需要使缓存失效 */
v7_dma_inv_range(start, end);
break;
case DMA_TO_DEVICE:
/* 数据从CPU到设备:需要刷写缓存 */
v7_dma_clean_range(start, end);
break;
case DMA_BIDIRECTIONAL:
/* 双向:刷写并使失效 */
v7_dma_flush_range(start, end);
break;
}
dsb(); /* 数据同步屏障,确保操作完成 */
}
2. 零拷贝技术实现
/**
* @file majestic/src/streaming/zero_copy.c
* @brief 零拷贝流处理
*/
/**
* @brief 使用ION内存实现零拷贝
*
* @details 流程:
* 1. ION分配物理连续内存
* 2. VPU/VENC直接访问物理地址
* 3. 通过DMABUF共享给用户态
* 4. 用户态mmap直接访问
*/
static int init_zero_copy_pipeline(encoder_t *enc)
{
struct ion_allocation_data alloc_data = {
.len = enc->buffer_size,
.align = 4096,
.flags = ION_FLAG_CACHED,
.heap_id_mask = 1 << ION_HEAP_TYPE_CARVEOUT,
};
/* 分配ION缓冲区 */
int ion_fd = open("/dev/ion", O_RDWR);
if (ion_fd < 0) return -1;
if (ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data) < 0) {
close(ion_fd);
return -1;
}
/* 获取DMA缓冲区句柄 */
struct ion_fd_data fd_data = {
.handle = alloc_data.handle,
};
if (ioctl(ion_fd, ION_IOC_SHARE, &fd_data) < 0) {
close(ion_fd);
return -1;
}
/* 用户态映射 */
void *vaddr = mmap(NULL, enc->buffer_size,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd_data.fd, 0);
/* 传递物理地址给硬件 */
struct ion_custom_data custom_data = {
.cmd = ION_IOC_PHYS_ADDR,
.arg = (unsigned long)&phys_data,
};
ioctl(ion_fd, ION_IOC_CUSTOM, &custom_data);
enc->hw_addr = phys_data.phys;
enc->vaddr = vaddr;
return 0;
}
第九部分:AI推理框架
1. 文件:majestic-plugins/plugins/motion_detection/md_ai.c
/**
* @file md_ai.c
* @brief AI运动检测插件
* @author OpenIPC Team
* @version 1.0.0
*
* @details 基于NPU的运动检测实现
* 使用轻量级CNN模型,8bit量化
* 输入:320x240 NV12
* 输出:运动区域bbox + 置信度
*/
#include <npu.h>
/**
* @struct ai_model_t
* @brief AI模型信息
*/
typedef struct {
const char *name; /**< 模型名称 */
uint8_t *model_data; /**< 模型数据 */
size_t model_size; /**< 模型大小 */
struct {
uint32_t width; /**< 输入宽度 */
uint32_t height; /**< 输入高度 */
uint32_t channels; /**< 输入通道 */
uint32_t format; /**< 输入格式 */
} input;
struct {
uint32_t num_outputs;/**< 输出数量 */
uint32_t *sizes; /**< 输出大小数组 */
} output;
uint32_t npu_freq; /**< NPU运行频率 */
uint32_t latency; /**< 推理延迟,单位us */
} ai_model_t;
/**
* @brief 初始化NPU推理引擎
* @param model_path 模型文件路径
* @return void* 引擎句柄
*/
void* ai_engine_init(const char *model_path)
{
npu_session_t *session = malloc(sizeof(npu_session_t));
/* 加载模型 */
FILE *fp = fopen(model_path, "rb");
fseek(fp, 0, SEEK_END);
size_t model_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
uint8_t *model_data = malloc(model_size);
fread(model_data, 1, model_size, fp);
fclose(fp);
/* 初始化NPU */
npu_init_params_t params = {
.model_data = model_data,
.model_size = model_size,
.npu_freq = 500, /* 500MHz */
.core = 0,
};
npu_init(session, ¶ms);
/* 预热 */
for (int i = 0; i < 3; i++) {
npu_run(session, NULL, NULL);
}
return session;
}
/**
* @brief 执行AI推理
* @param session 引擎句柄
* @param frame 输入帧
* @param results 检测结果输出
* @return int 检测到的目标数量
*/
int ai_engine_infer(void *session, video_frame_t *frame, detection_t *results)
{
npu_session_t *sess = (npu_session_t*)session;
/* 预处理:缩放+格式转换 */
uint8_t *input_data = preprocess(frame, sess->input_width, sess->input_height);
/* 执行推理 */
uint64_t start = get_timestamp_us();
npu_buffer_t output;
npu_run(sess, input_data, &output);
uint64_t end = get_timestamp_us();
/* 记录延迟 */
sess->last_latency = end - start;
/* 后处理:解析输出 */
int num_detections = postprocess(&output, results, MAX_DETECTIONS);
free(input_data);
return num_detections;
}
总结:代码结构关键点
| 组件 | 关键文件 | 核心数据结构 | 数据流单位 | 性能关键点 |
|---|---|---|---|---|
| ISP驱动 | ssr305_isp.c |
ssr305_isp_dev |
帧 (3-10MB/s) | 3DNR/WDR硬件加速 |
| 视频编码 | h265_encoder.c |
h265_config_t |
帧/NALU (5-200KB) | 硬件VPU利用 |
| 流媒体 | rtsp_server.c |
rtsp_session_t |
RTP包 (1400B) | epoll并发 |
| 配置管理 | yaml-cli.c |
yaml_path_t |
YAML节点 | 运行时重载 |
| 硬件检测 | ipctool.c |
soc_info_t |
系统信息 | 多源探测 |
| AI推理 | md_ai.c |
ai_model_t |
检测框 | NPU量化加速 |
更多推荐


所有评论(0)