V3s 主线 Linux 5.19 实现 1920x1080 @ 30fps h.264 硬编码 / 全志 SOC / WhyCan Forum(哇酷开发者社区) 您所在的位置:网站首页 7e0937088a编码 V3s 主线 Linux 5.19 实现 1920x1080 @ 30fps h.264 硬编码 / 全志 SOC / WhyCan Forum(哇酷开发者社区)

V3s 主线 Linux 5.19 实现 1920x1080 @ 30fps h.264 硬编码 / 全志 SOC / WhyCan Forum(哇酷开发者社区)

2023-11-20 10:21| 来源: 网络整理| 查看: 265

折腾了很久,终于实现了这个功能!!!

自从 aodzip 大佬分享了他的 cedar 仓库后,我就一直想试试 V3s 的 h264 硬编码,可惜 OpenMax 这个中间层就是个渣渣,不仅莫名其妙的占用大量内存,在 ffmpeg 中也无法调整各种编码参数。V3s 上实测,编码 dvp 摄像头采集的 640x480 的视频就已经是极限了…… 显然这和 datasheet 中提及的 1080p @ 30fps 还差的有点远

于是我直接放弃 openmax、ffmpeg,参考了网上 V4L2 和全志 Cedar API 的例程,开始手写程序。全志 video_encoder_API_guide.pdf 里的描述可以说是“惜字如金”,而且网上各个例程使用这些函数的方法也有差异,比如有些每帧调用一次 AllocInputBuffer, 有些没有使用 ReturnOneAllocInputBuffer 等等,导致这些函数的具体作用比较难理解

这些例程大都是读取某个 yuv 文件,将每帧的数据 memcpy 到 CedarVE 申请的缓冲区内,然后再编码。对于 640x480 之类的低分辨率还没太大问题,但是编码 1920x1080 的时候帧率就直接掉到10以下并且CPU使用率占满,因为程序花费了大量的时间来拷贝数据,非常低效。

要实现零拷贝的话,一般是采取 V4L2_MEMORY_DMABUF(Linux DMA-BUF 机制)或者 V4L2_MEMORY_USERPTR(V4L2 直接将数据写入 userspace 的某个 buffer)这两种方法。可惜,libcedarc 的 API 不支持前者,而 Linux 的 sun6i-video 驱动又恰巧不支持后者。现在剩下的只有 V4L2_MEMORY_MMAP。不过如果我们希望将自己申请的 buffer 交给 libcedarc 使用的话,我们需要知道 buffer 的物理地址;可是 V4L2 API 中并没有能让我们获取 MMAP buffer 物理地址的功能……

由于本人能力不足,实在看不懂 cedar 驱动的架构,于是只好另辟蹊径,放弃为 cedar 添加 dma-buf 支持的想法。在深入研究 V4L2 MMAP 机制后,发现其实 MMAP 过程中返回 buffer 中一个 private struct 里的 dma_addr 就是 MMAP 缓冲区的 dma 地址。

不过 dma_addr 怎么转换成物理地址呢?无数次实验后,发现 kernel 自带的 virt_to_phys 函数貌似可以完成这个功能…… 这个函数返回的 phys_addr,经过 busybox devmem 读写数据验证,的确是相应 V4L2 MMAP buffer 的物理地址…… 这肯定不是 virt_to_phys 的正确用法,不过能用就先用着吧…… (路过的大神请手下留情哈哈)

知道如何获取 phys_addr 后,剩下的就是解决如何将这个信息传回给 userspace 的应用。仔细阅读 sun6i-video 中 V4L2 相关代码后,发现有一个叫做 vidioc_default 的无用 ioctl,简直完美!写了个简单的 ioctl handler 便解决了这个问题。现在 userspace 的应用只需将 V4L2 MMAP 返回的 buffer 传递给这个 ioctl,就能获取该 buffer 的物理地址。

最后一个问题:cedar 在 Linux 5.5 及以上的版本就无法编译了,因为 dma-buf 中某个结构体无法对齐。恼人的是,5.10 后又添加了许多对于全志 SoC 的驱动改进,实在是想用。于是我又花了很多时间仔细阅读 Linux 5.4、5.5 附近的 commit 记录,发现 Linux 在 5.5 以后移除了 Android ION 这个内存管理器的相关代码,并且顺带移除了 dma-buf 一个结构体中的元素。而老旧的 cedar 恰好又需要这个 ION 机制…… 我只好写了个 patch 将这个删掉的元素重新添加进 dma-buf 的结构体(顺带修改了一些头文件、syscall参数错误,等等),于是 cedar 在最新主线 Linux 5.19 上就能运行了,舒服!

代码:https://github.com/Unturned3/h264enc_demo

配套的 buildroot “包”:https://github.com/Unturned3/v3s3

顺便介绍一下:v3s3 是一个 buildroot external tree (“外部树”),并不是一个独立的开发环境,需要和一个 buildroot 包配合使用。它的优点是当前项目所有的文件和 buildroot 内部文件是分开的,非常干净,便于管理;日后更换、升级 buildroot 版本简直不要太爽。v3s3 仓库除了硬编码 demo 外,还有许多其他好东西,例如一个极优化的 Linux 内核 config,0.3 秒从 Starting kernel 到 /sbin/init,硬件AES256加速驱动,等等。具体详见 repo README。(如果对你有帮助的话,不妨给个star,逃

编译方法# 切换到某个合适的目录 cd /some/working/directory # 下载 buildroot external tree git clone https://github.com/Unturned3/v3s3 # 下载并解压任意一个 buildroot 版本(最近的版本应该都能用;我测试的是2022.05.1) wget buildroot.org/downloads/buildroot-x.y.z.tar.gz tar -xf buildroot-x.y.z.tar.gz # 切换至 buildroot 目录,并初始化配置 cd buildroot-x.y.z make BR2_EXTERNAL=../v3s3 licheepi_zero_defconfig # 打开 buildroot 配置目录 make nconfig # 勾选 h.264 硬编码 demo 选项 External options ---> [*] CedarVE H.264 encoding demo # 下载所需的软件包 make source # 编译 make all # 烧录 SD 卡 (将 /dev/sdX 替换成合适的路径) sudo dd if=output/images/sdcard.img of=/dev/sdX bs=4M使用方法

本人用的硬件是荔枝派Zero,OV5640 DVP (parallel) 摄像头。荔枝派Zero 第一次上电可能会出现 OV5640 i2c ID 错误的情况,目前暂不知原因,不过重启(软重启,i.e. reboot, 不是插拔电源)一次便可解决。只要 /dev/video0, /dev/media0 存在就没太大问题。media-ctl -p 这个指令也可以用于查看 OV5640 是否注册成功。

用串口、或者 ssh 登录后,/root 目录下会有一个 h264enc_demo 的二进制文件:

./h264enc_demo [width] [height] [FPS] [n_frames]

支持 640x480, 1280x720, 1920x1080。n_frames 为采集的帧数,如果不写的话默认450帧。

所有分辨率均支持 30 FPS;640x480 模式下额外支持 60FPS (这是 OV5640 驱动的限制)

h264enc_demo 会将编码流输出至 /mnt/out.h264,如果录制的 n_frames 较大,可考虑在 /mnt 上挂载一个大 SD 卡分区来存储数据。

copy_video.sh 可通过 scp 指令将 /mnt/out.h264 拷贝至某台远程电脑(需要你自己设置合适的 scp destination)

在装有 ffmpeg 的电脑上,可通过 ffmpeg -framerate 30 -i out.h264 -c copy out.mp4 指令将裸 h264 流转换为 MP4

最近编辑记录 unturned3 (2022-09-11 23:32:58)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有