第3章:SDK包解析:海思SDK目录结构、osdrv与drv目录功能、sample与mpp库说明
拿到海思的SDK包,第一件事是什么?
别急着编译。我个人的习惯是,先泡杯茶,把目录结构捋一遍。这就像拿到一张新地图,你得先看懂图例,才知道往哪走。
海思的SDK包,说实话,刚开始看会觉得有点乱。目录多,命名也怪。但只要你摸清了它的套路,后面做裁剪和优化就会顺手很多。今天我就带你把这锅「大杂烩」拆开看看。
3.1 SDK顶层目录结构
解压完SDK后,你会看到类似这样的顶层目录:
Hi3516EV200_SDK_V1.0.0.0/
├── osdrv/ # 操作系统驱动层
├── drv/ # 硬件驱动层
├── mpp/ # 媒体处理平台
├── sample/ # 示例代码
├── tools/ # 工具链与烧录工具
├── doc/ # 文档
└── scripts/ # 编译脚本
嗯,这里要注意:不同芯片型号的SDK,目录名可能略有差异。比如有的叫Hi3516DV300_SDK_V2.0,但核心结构基本一致。
我最早接触海思SDK时,犯过一个低级错误——直接在mpp/目录下改代码,结果发现编译不过。后来才明白,mpp/里的源码是只读的,真正要改的是osdrv/和drv/里的东西。这个坑,我先帮你踩了。
3.2 osdrv目录:系统的「骨架」
osdrv/全称是OS Driver,说白了就是操作系统相关的驱动层。它负责把Linux内核和你的硬件板子「粘」在一起。
来看看它的内部结构:
osdrv/
├── kernel/ # Linux内核源码(海思补丁版)
├── uboot/ # U-Boot启动引导
├── rootfs/ # 根文件系统
├── pub/ # 编译产物与工具
└── Makefile # 一键编译脚本
kernel/:这里放的是海思打过补丁的Linux内核。为什么是补丁版?因为海思芯片有很多私有IP,比如视频编解码模块、ISP图像处理单元,这些在主线内核里是没有的。海思会提供一份.patch文件,你把它打到标准内核上,才能驱动这些硬件。
uboot/:启动引导程序。我建议你重点关注uboot/include/configs/下的头文件,板级配置都在那里。比如DDR大小、Flash分区表、网络参数等。我在项目里就遇到过因为分区表配错,导致系统启动后挂载不了文件系统的尴尬事。
rootfs/:根文件系统。海思默认给的是一个Busybox做的迷你文件系统,大概只有几兆。但说实话,这个默认配置很「糙」——很多你用不到的命令也编译进去了,比如telnetd、ftpd。做产品时,这些都得砍掉。
osdrv/rootfs/是重点关照对象。我曾经把一个4MB的根文件系统砍到1.2MB,就靠删掉那些用不上的Busybox命令和库文件。省下来的空间,全给了视频缓存。
3.3 drv目录:硬件的「驱动程序仓库」
drv/目录和osdrv/容易搞混。我简单解释一下:
- osdrv/:管的是操作系统层面的驱动,比如内核、文件系统、启动引导。
- drv/:管的是具体硬件外设的驱动,比如GPIO、I2C、SPI、UART、PWM等。
看个例子:
drv/
├── gpio/ # GPIO驱动
├── i2c/ # I2C总线驱动
├── spi/ # SPI总线驱动
├── uart/ # 串口驱动
├── pwm/ # PWM输出驱动
├── watchdog/ # 看门狗驱动
└── Makefile # 编译脚本
每个子目录里,通常包含一个.ko文件(内核模块)和对应的测试程序。比如gpio/里会有hi_gpio.ko和test_gpio。
为什么要单独搞个drv/目录?
因为海思芯片的外设驱动,很多是作为内核模块加载的,而不是编译进内核。这样做的好处是灵活——你不需要的驱动就不加载,省内存。坏处是,你得自己写加载脚本。
3.4 mpp目录:多媒体处理的核心
mpp/是海思SDK里最值钱的部分。MPP全称是Media Process Platform,它封装了视频编解码、图像处理、音频处理等硬件加速能力。
它的目录结构大致如下:
mpp/
├── include/ # 头文件(API接口)
├── lib/ # 静态库/动态库
├── component/ # 组件源码(如VENC、VDEC、VPSS)
├── sample/ # 示例程序(注意:这里也有sample)
└── Makefile # 编译脚本
include/:这是你写应用层代码时要包含的头文件。比如mpi_venc.h(视频编码接口)、mpi_vdec.h(视频解码接口)、mpi_vi.h(视频输入接口)。
lib/:编译好的库文件。有静态库(.a)和动态库(.so)两种。做产品时,我建议用静态库。为什么?因为动态库在运行时需要加载,会增加启动时间,而且容易因为库版本不一致出问题。静态库直接编译进可执行文件,省心。
component/:这是MPP的核心源码。每个组件对应一个硬件模块:
| 组件名 | 功能 | 对应硬件 |
|---|---|---|
| VENC | 视频编码(H.264/H.265) | 硬件编码器 |
| VDEC | 视频解码 | 硬件解码器 |
| VPSS | 视频处理子系统(缩放、裁剪、旋转) | 图像处理单元 |
| VI | 视频输入(从Sensor采集数据) | MIPI/DVP接口 |
| VO | 视频输出(显示到屏幕) | HDMI/LCD接口 |
| AI/AO | 音频输入/输出 | I2S接口 |
每个组件都提供了一套API,你只需要调用这些API,就能控制硬件做视频编解码,完全不用关心底层寄存器怎么配。说白了,MPP就是海思给你搭好的「积木」,你只管拼就行。
3.5 sample目录:最好的学习资料
sample/目录里放着各种示例程序。我强烈建议你,拿到SDK后,先跑一遍sample。
它的结构是这样的:
sample/
├── venc/ # 视频编码示例
├── vdec/ # 视频解码示例
├── vpss/ # 视频处理示例
├── vi/ # 视频输入示例
├── vo/ # 视频输出示例
├── audio/ # 音频示例
├── region/ # 区域叠加示例(OSD)
└── Makefile # 编译脚本
每个示例都是一个独立的工程,有完整的Makefile和main.c。比如sample/venc/里,你会看到:
sample_venc.c # 主程序
sample_comm_venc.c # 公共函数(参数配置等)
Makefile # 编译脚本
这些示例代码写得挺规范的。我当年入门海思平台时,就是照着sample_venc.c一行一行读下来的。读懂了它,你就知道怎么初始化MPP、怎么创建编码通道、怎么处理数据流。
HI_MPI_VENC_CreateChn创建的编码通道,在退出时没有调用HI_MPI_VENC_DestroyChn。所以,参考sample可以,但别直接复制粘贴,一定要检查资源释放。
3.6 MPP库的两种使用方式
在实际项目中,MPP库有两种用法:
方式一:动态库加载
把mpp/lib/下的.so文件放到板子的/lib目录下,然后在编译你的应用时链接动态库。优点是库可以单独升级,缺点是启动时多了一步加载过程。
# 编译时链接
arm-himix200-linux-gcc sample_venc.c -I./mpp/include -L./mpp/lib -lmpi -o sample_venc
# 运行时需要把libmpi.so拷贝到板子上
cp ./mpp/lib/libmpi.so /lib/
方式二:静态库链接
直接把.a文件链接进你的可执行程序。优点是部署简单,缺点是程序体积会变大。
# 编译时静态链接
arm-himix200-linux-gcc sample_venc.c -I./mpp/include ./mpp/lib/libmpi.a -o sample_venc
我个人更推荐静态链接。尤其是在做系统裁剪时,能少一个动态库就少一个,省得在文件系统里折腾依赖关系。
3.7 小结
好了,海思SDK的目录结构就讲到这里。总结一下:
- osdrv/:管操作系统,内核、U-Boot、文件系统都在这里。
- drv/:管硬件外设,GPIO、I2C这些驱动模块。
- mpp/:管多媒体,视频编解码、图像处理的核心库。
- sample/:管学习,最好的入门资料。
下一章,我们会深入osdrv/,手把手教你裁剪Linux内核。到时候,你会看到怎么把一颗「臃肿」的内核,瘦身成适合嵌入式设备的「精干」版本。
嗯,今天就到这里。有问题欢迎交流。