折腾了很久,终于实现了这个功能!!!
自从 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)
离线
楼主,
有没有实操视频看下
离线
请问下f1c100s/f1c200s支持h264硬件编码吗?
离线
楼主,
有没有实操视频看下
哪天有空我录一个好点的
荔枝派 zero 的电源好像设计的有点问题,ov5640 获取的图像噪音有点大
最近编辑记录 unturned3 (2022-09-15 10:35:08)
离线
请问下f1c100s/f1c200s支持h264硬件编码吗?
f1c 只有 h264 硬解。没有硬件编码器
离线
楼主 这个可以播放h264的mp4视频吗 有没有demo可以测试?
离线
楼主的硬解和硬编的资料从哪里来的
离线
刚玩linux系统,好多软件都不太会,太难了
离线
有谁下载烧录成功了f1c200s的固件了吗?我太难了,都没成功
离线
楼主的硬解和硬编的资料从哪里来的
在代码里扣出来的。
离线
楼主 这个可以播放h264的mp4视频吗 有没有demo可以测试?
我只弄了编码
而且播放好像还需要 DisplayEngine、DRM、LCD显示驱动啥的,我基本没了解过
离线
bigbat 说:楼主的硬解和硬编的资料从哪里来的
在代码里扣出来的。
确实,基本全靠摸索
这个仓库 有一些关于 API 的资料
https://linux-sunxi.org/Video_Engine 有关于 VE 寄存器的资料
最近编辑记录 unturned3 (2022-09-16 13:08:26)
离线
有谁下载烧录成功了f1c200s的固件了吗?我太难了,都没成功
我这个关于 V3s 硬编码的帖子可能对你烧录固件帮助不大……
离线
楼主你的rootfs启动就有一个dhcpcd进程,定时查找dhcp server。在哪里可以禁止这个进程?我的环境上不需要网络通信。dropbear我也不需要。
最近编辑记录 wuyu (2022-12-16 17:04:21)
离线
spieed有ax620啊,估计又能鸽一茬韭菜
离线
spieed有ax620啊,估计又能鸽一茬韭菜
读书人的事能叫噶韭菜吗?
离线
楼主你的rootfs启动就有一个dhcpcd进程,定时查找dhcp server。在哪里可以禁止这个进程?我的环境上不需要网络通信。dropbear我也不需要。
/etc 里头的某些 startup script 删了就行,具体哪些我有点忘了。注意查看一下其他 script 有没有依赖于你要删掉的 script,以免造成 init 失败
dropbear 不需要的话,用 buildroot 的 config 界面取消勾选就行
离线
@unturned3
谢谢回复。 我现在就是删除了/etc下的某个script。
离线
hi unturned3,
我这边运行demo报错了,kernel-5.19.3,找不到/dev/media0,需要修改什么地方吗?
# ./h264enc_demo 1280 720 30 500
debug : ionAlloc <__GetIonMemOpsS:985>:*** get __GetIonMemOpsS ***
debug : ionAlloc <ion_alloc_open:134>:begin ion_alloc_open
debug : cedarc <VeSetSpeed:1559>: *** set ve freq to 360 Mhz ***
debug : cedarc <VeInitialize:1198>: ve init ok
debug : ionAlloc <ion_alloc_open:175>:** phy offset = 40000000
debug : cedarc <VeRelease:1253>: ve release ok
debug : cedarc <LogVersionInfo:40>:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Cedar Codec <<<<<<<<<<<<<<<<<<<<<<<<<<<<
tag : createBranch_customize_sylix
branch: CedarC-v1.1.9
commit: 4a182bd42c931bea2f77b0ff9a531d233df8541e
date : Thu May 25 15:34:03 2017 +0800
author: jenkins8080
patch :
----------------------------------------------------------------------
debug : cedarc <VeSetSpeed:1559>: *** set ve freq to 360 Mhz ***
debug : cedarc <VeInitialize:1198>: ve init ok
debug : ionAlloc <__GetIonMemOpsS:985>:*** get __GetIonMemOpsS ***
debug : ionAlloc <ion_alloc_open:134>:begin ion_alloc_open
debug : cedarc <VideoEncInit:195>: (f:VideoEncInit, l:195)
debug : cedarc <VideoEncInit:210>: (f:VideoEncInit, l:210)
debug : cedarc <BitStreamCreate:102>: BitStreamCreate OK
Info: h264 encocder init OK
open /dev/media0: No such file or directory
Error: cam_media_init() failed
Error: cam_init() failed
debug : cedarc <VeRelease:1253>: ve release ok
离线
@我叫Lonely
demo是给摄像头编码,你板子上没有那个型号设备
日志上已经显示编码器初始化成功
改成直接送yuv帧进去或gst+omx都可以,lz代码是没问题的
离线
我也试试看。
先下载编译一下。
离线
@我叫Lonely
跑通了,之前没注意要软件reboot一下才能识别到ov5640。
修改了一下楼主的demo,用udp传输到电脑实时观看,ffplayer会有大概700ms延时;mplayer会快很多,几乎是实时的,但是会花屏,显示不全(应该是udp传输的问题
离线
@我叫Lonely
修改了一下楼主的demo,用udp传输到电脑实时观看,ffplayer会有大概700ms延时;mplayer会快很多,几乎是实时的,但是会花屏,显示不全(应该是udp传输的问题
对,看网上其他用户评论 ffplayer 的延迟确实比较高,不建议用来观看实时视频。
不妨提交一个你的 UDP 代码的 pull request ?这样其他人也可以帮忙 debug。能通过网络实时传输视频的话,这个 h264 encoding demo 就比较完善了
离线
@unturned3
代码上传到 https://github.com/Lisy051/v3s_h264_udp 了
只在你的基础上做了一点点修改
离线
感谢大佬分析,基于大佬代码,我研究了下,加了推流到rtmp的代码,我整理下提个pr
离线
rtsp rtmp都来一个啊
离线
麻烦问一下这个一直显示尺寸错误?
error:unsupported width/height
离线
麻烦问一下这个一直显示尺寸错误?
error:unsupported width/height
./h264enc_demo [width] [height] [FPS] [n_frames]
只支持 640x480, 1280x720, 1920x1080
离线
Uccccc 说:麻烦问一下这个一直显示尺寸错误?
error:unsupported width/height./h264enc_demo [width] [height] [FPS] [n_frames]
只支持 640x480, 1280x720, 1920x1080
楼主这个代码不适用于uvc是吗
离线
楼主这个代码不适用于uvc是吗
目前是不适用的
离线
unturned3 说:Uccccc 说:麻烦问一下这个一直显示尺寸错误?
error:unsupported width/height./h264enc_demo [width] [height] [FPS] [n_frames]
只支持 640x480, 1280x720, 1920x1080
楼主这个代码不适用于uvc是吗
使用UVC设备直接取yuv数据的话,带宽不够,得降分辨率或帧数;要么就是取mjpeg格式流,先解码再编码
离线
V3S最大能支持到多少分辨率呢刷新60.BIT24 能不能到1280X640呢、
离线
坐等结果,谁能告诉我呀
离线
找物理地址映射直接查/proc/self/pagemap就可以了吧,不需要动驱动
离线
V3S最大能支持到多少分辨率呢刷新60.BIT24 能不能到1280X640呢、
只说编码?最大4800x4800
h264 720p能稳60fps
关cabac开fastenc还能快50%
最近编辑记录 4610 (2023-10-18 22:32:46)
离线
cec0d5f1b6ab 说:请问下f1c100s/f1c200s支持h264硬件编码吗?
f1c 只有 h264 硬解。没有硬件编码器
f1c的硬解有demo么,现在我弄f1c100s效率奇差,但是个人又搞不到tina,有没有开源的demo
离线
有没有高手,接一个私单,反正闲着也是闲的。只是读卡播放视频与图片,调一个显示分辨率即可,FLASH启动,SD卡拷贝视频图片与文件
离线
有没有高手,接一个私单,反正闲着也是闲的。只是读卡播放视频与图片,调一个显示分辨率即可,FLASH启动,SD卡拷贝视频图片与文件
基于linux还是裸机
离线
给管理员提个建议:与帖子主题明显无关的回复应不予过审,并且建议回复者另开新帖来讨论。如果每个人都无视主题的话,那么论坛将会变得杂乱无章。
有谁下载烧录成功了f1c200s的固件了吗?我太难了,都没成功
wcq669 说:有没有高手,接一个私单,反正闲着也是闲的。只是读卡播放视频与图片,调一个显示分辨率即可,FLASH启动,SD卡拷贝视频图片与文件
基于linux还是裸机
离线