F1C100S「单片机」播放(软解)MP4 视频 480x272
序
继上回 折腾荔枝派 播放视频无果,这回群友推荐我购买另一块大一点的板子来开发 F1C200S,应该就能达到我想要的效果了——折腾出一个小电视机出来,可以播放视频。
重新选「派」,这次选了「芒果派」
按照群友的推荐,我购买了「芒果派」作为这次开发用的开发板。
烧写镜像
「芒果派」厂商给了我一些资料,说要烧写镜像。于是我开始折腾 zadig.exe
去 「Create USB Device」。这里面遇到了一些问题,不过基本上来讲都不是什么大问题,此处列举这么几个问题:
创建 FEL Device 的时候,无论如何也创建不成功。
设备管理器打开看不到任何设备。结果群友「喵叫专攻」提醒了一下「你可能要把 USB 线反过来插一下看看」。插上后,成功识别设备,sunxi-fel.exe -l
也能看到设备了。
#sunxi-fel.exe -l
USB device 001:035 Allwinner F1C100s
干!我特么以为我的是 F1C200s!
总之按住 BOOT 然后按住 RST 后,可以看到液晶屏变为白屏,它进入了 FEL 模式。我双击「from-fel-to-dfu.bat」后,看到它的液晶屏好像进了 u-boot,设备管理器也看到「FEL Device」是「拔出状态」。
在 zadig.exe 里创建了半天的 DFU Device,后面发现它自己把自己改名为「USB download gadget」了
我是靠它的 ID:1f3A:1010 发现它就是 DFU 设备的。于是开始烧写。
烧写后,才发现卖家早就帮我烧写好了。
在烧写的过程中我不断和卖家沟通如何烧写、怎么连不上、怎么不能显示 FEL 设备或者 DFU 设备、我要退货等各种问题,但是毕竟买了已经有 5 个多月了,卖家不愿意退货,并且也无法提供更多的资料,我只能自己继续研究。
不管怎样,我有两块板子,这块板子被我重复烧写了以下,另一块板子没动,最后发现两块板子的上电后行为一致,就先这样吧。
连接 OTG,稍等片刻,会有新设备冒出来
咦,这不是 Linux 么?哦不对,这应该是 buildroot。
大问题基本搞定,接下来是开发的问题
如何开发
首先我们需要确定它的 CPU 使用什么样的指令集,然后我可以使用 gcc 或者 gcc-arm-toolchain 去给它编译程序软件。
感觉这样询问的话,我得弄清楚「什么什么带t的那种」,于是运行了一下 uname -a
,得到了合适的答案:
# uname -a /bin/gpio
Linux mangopi-r3 5.4.92 #1 Tue May 11 10:39:00 CST 2021 armv5tejl GNU/Linux
啊,原来是 armv5tejl
。
控制 F1C100s 的 GPIO
经过一番简单的谷式搜索,我找到了这个库:
f1c100s-gpio-tools
一件很棒的事是这个小工程的代码简单易读,而且作者上传了 bin gpio
,只要放到 /bin
里,给个可执行权限就可以使用了。
它的接口让我想起了 WiringPi。像啊,很像啊。
播放视频
我现在最想做的事情是让它 能够播放视频 。
于是我连接上串口,然后在 /dev
里面瞄一眼看看都有些啥。
很眼熟地看到了 cedar_dev 和 ion,以及 fb0 。赶紧去群里问了 Aodzip 大佬。经过一番沟通,我放弃了 使用硬件外设解码 H264 视频 的这一想法,决定尝试 CPU 软件解码视频 。
解码视频后如何播放呢,很简单,直接写 /dev/fb0
,液晶屏就会有显示。每 open()
一次 /dev/fb0
,写一帧画面,写的时候就能看到液晶屏显示内容。用 close()
关闭设备,再重新 open()
打开再写就又是新的一帧画面。 从编程 IO 上它是这么个逻辑 。打开设备时,写的像素量如果超出了 480x272 这个分辨率,就会写入失败(写文件的函数返回一个错误码),因为它的 Framebuffer 就这么大。可以用 cat /dev/urandom > /dev/fb0
来欣赏花屏和写超出范围的报错(注意是 正常报错 而不是 Segmentation Fault
)
在使用 FFmpeg 的时候,指定 -pix_fmt bgra -f fbdev /dev/fb0
就能使 ffmpeg
输出每一帧到 /dev/fb0
,看到动画的播放效果。理论上是这样。
然而没有 ffmpeg 啊!
那就得自己编译 ffmpeg。经过一番折腾,我是编译出了 ffmpeg,大约 34 MB,直接塞 /bin
里,别忘了 chmod +x /bin/ffmpeg
。
结果在试用的时候报错了: glibc 版本不符 。
我用的是 gcc13,但看起来这个 buildroot 的镜像是 2020 年编译的。当年最新的 gcc 就是 gcc9,因此要用 gcc9 编译 ffmpeg.
我于是从 ffmpeg 官网找到 Linux 的 bin 的下载入口,然后打算根据日期挑选符合我的 glibc 版本的 ffmpeg 二进制。考虑到目标机器是 armv5tejl
,但是人家不一定有,于是我按照最可能的情况来猜出适合我的机器的 ffmpeg 二进制。
可能是这个
ffmpeg-4.3.1-armel-static.tar.xz 2020-07-22 16:39 12M
下载后解压 ffmpeg
到 /bin
里,然后 chmod +x /bin/ffmpeg
,再执行一下 ffmpeg
看看,果然打印出了信息:
进行视频播放
一开始只是为了测试,我没管视频文件本身的大小和它输出的帧数,想直接先跑起来看看,于是拔了 TF 卡,传了个 PV 上去,然后用 ffmpeg 播放,不过我需要先挂载 TF 卡到特定位置才行。
TF 卡的设备是 /dev/mmcblk0
,我的 TF 卡只有一个分区,这个分区的设备是 /dev/mmcblk0p0
。因此在 /etc/fstab
里使用这条语句来挂载:
/dev/mmcblk0p0 /mnt/sdcard vfat defaults 0 0
然后运行以下命令:
mount -a
成功挂载。接下来播放视频:
ffmpeg -i /mnt/sdcard/testvideo.mp4 -pix_fmt bgra -f fbdev /dev/fb0
帧数只有 1 fps,而且只能看到视频左上角,看不全。重新用命令设置一下输出分辨率后,可以看到极其卡顿的视频。
ffmpeg -i /mnt/sdcard/testvideo.mp4 -s 480x272 -pix_fmt bgra -f fbdev /dev/fb0
解决卡顿问题
我试过唯一能解决卡顿问题的方法是 预处理视频 ,预先把视频压缩到 480x272 分辨率,然后使用 xvid 编码格式, 不使用 h264 。
音频播放
按照卖家给的「产测工具」,我发现播放音频使用的命令是 tinyplay
。我尝试过使用 FFmpeg 以直接输出 ALSA 的方式来把音频推流到声卡驱动,结果发现不成功,FFmpeg 提示「[ALSA @Xxxxx]: Could not open device: No such file or directory.」(或者差不多类似的内容)。上网搜了一通,发现 FFmpeg 能把音频输出到管道里。那就有可能利用管道通信来实现音频的播放了。我于是尝试了一下 tinyplay
的管道输入功能,发现:
tinyplay 不支持管道输入
这简直是傻卵设计,气得我直接翻出它的源码:
tinyalsa
tinyplay 不支持管道输入,但是它的源码里面有打开管道进行输入的功能。
理想的情况是这样的
实际情况是这样的:
- 在
int main()
里,它 parse options 的时候,它的逻辑是认为所有的横杠 -
开头的都是命令参数。
- 在过完 options parsing 后,它的
ctx_init()
会检测文件名是不是 -
,是的话,打开 stdin
进行读取,然后进行音频播放。
- 命令行参数
-
会被过滤掉,ctx->filename
值是 NULL
。
- 它先判断
ctx->filename
值是不是 NULL
,然后提示:「Filename not specified.」并退出。
- 过了这一关后(假设它没有退出),它判断
ctx->filename
是不是 -
,是的话打开 stdin
进行一个管道输入播放。
简直是个傻卵 bug。气得我直接改它的源码。我的思路是:
- 首先
stdin
被我视作特殊文件名,一般人不会使用这个文件名。
- 其次
stdin
可以过它的 options parsing,被视作文件名。
- 我检测
ctx->filename
,如果是 stdin
我就打开 stdin
标准输入文件。
(其实也是一种傻卵设计)
编译 tinyplay
需要先编译 tinyalsa
编译的过程花费了我很多精力,最最最主要的原因是:
GLIBC 版本太高。
不能使用 none-eabi 编译工具链,因为 tinyalsa 库依赖 <poll.h>
。需要使用 linux-gnueabi 编译工具链。但是网上没有 2020 年的二进制的 arm-linux-gnueabi 工具链。我尝试过使用 Debian 添加 bullseye 或者 buster 源来安装 arm-linux-gnueabi-gcc-9、arm-linux-gnueabi-gcc-8,结果发现 不论 gcc 版本是多少,编译出来的 tinyplay 都依赖 GLIBC 2.34 。
使用卖家提供的 Ubuntu 16.04 虚拟机编译 tinyalsa
虚拟机下载
通过拖拽文件到虚拟机里,然后虚拟机安装 gcc-arm-linux-gnueabi(其中 gcc 版本是 5,可喜可贺),再修改源码。
再用以下命令编译,就得到了能用的 tinyplay
:
CROSS_COMPILE=arm-linux-gnueabi- make -j
使用管道进行音视频同时播放
tinymix set 1 63 #设置音量
tinymix set 2 1 #开音频输出
tinymix set 13 0 #关麦
ffmpeg -i /mnt/sdcard/要播放的文件.mp4 -s 480x272 -pix_fmt bgra -f fbdev /dev/fb0 -vn -ar 44100 -ac 1 -f wav pipe:1 | tinyplay stdin -r 44100 -c 1
播放不流畅,音频卡顿的解决方案
需要使用 FFmpeg 对原视频文件进行预处理,命令行如下:
ffmpeg -y -i 原始视频.mp4 -c:v libxvid -s 480x272 -r 12 -b:v 100K -c:a aac -b:a 64k -ac 1 -ar 44100 处理后的视频.mp4