0xAA55 发表于 2024-5-11 14:24:19

【嵌入式】F1C100S「单片机」播放(软解)MP4 视频 480x272

# F1C100S「单片机」播放(软解)MP4 视频 480x272

## 序
继上回 [折腾荔枝派](https://www.0xaa55.com/thread-27318-1-1.html) 播放视频无果,这回群友推荐我购买另一块大一点的板子来开发 F1C200S,应该就能达到我想要的效果了——折腾出一个小电视机出来,可以播放视频。

### 重新选「派」,这次选了「芒果派」
按照群友的推荐,我购买了「芒果派」作为这次开发用的开发板。
![芒果派正面](https://www.0xaa55.com/misc/mpifront.png)

![芒果派反面](https://www.0xaa55.com/misc/mpiback.png)

### 烧写镜像
「芒果派」厂商给了我一些资料,说要烧写镜像。于是我开始折腾 `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」是「拔出状态」。
!(https://www.0xaa55.com/misc/FEL.jpg)

#### 在 zadig.exe 里创建了半天的 DFU Device,后面发现它自己把自己改名为「USB download gadget」了
我是靠它的 ID:1f3A:1010 发现它就是 DFU 设备的。于是开始烧写。

#### 烧写后,才发现卖家早就帮我烧写好了。
!(https://www.0xaa55.com/misc/DFU.jpg)

在烧写的过程中我不断和卖家沟通如何烧写、怎么连不上、怎么不能显示 FEL 设备或者 DFU 设备、我要退货等各种问题,但是毕竟买了已经有 5 个多月了,卖家不愿意退货,并且也无法提供更多的资料,我只能自己继续研究。

不管怎样,我有两块板子,这块板子被我重复烧写了以下,另一块板子没动,最后发现两块板子的上电后行为一致,就先这样吧。

### 连接 OTG,稍等片刻,会有新设备冒出来
!(https://www.0xaa55.com/misc/mpir3dev.png)

![它显示为「rootfs」](https://www.0xaa55.com/misc/mpirootfs.png)

![双击打开](https://www.0xaa55.com/misc/mpiroot.png)

咦,这不是 Linux 么?哦不对,这应该是 buildroot。

### 大问题基本搞定,接下来是开发的问题
#### 如何开发
首先我们需要确定它的 CPU 使用什么样的指令集,然后我可以使用 gcc 或者 gcc-arm-toolchain 去给它编译程序软件。

![询问 gcc 编译目标](https://www.0xaa55.com/misc/mpigcc.png)
![回答1:gccv5xxxt](https://www.0xaa55.com/misc/mpigccwm.png)
![回答2:gccv5](https://www.0xaa55.com/misc/mpigccm.png)

感觉这样询问的话,我得弄清楚「什么什么带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

经过一番简单的谷式搜索,我找到了这个库:

(https://github.com/wuxx/f1c100s-gpio-tools)

一件很棒的事是这个小工程的代码简单易读,而且作者上传了 bin `gpio`,只要放到 `/bin` 里,给个可执行权限就可以使用了。

![看看都有啥 GPIO](https://www.0xaa55.com/misc/mpigpio.png)

它的接口让我想起了 WiringPi。像啊,很像啊。

### 播放视频

我现在最想做的事情是让它 ***能够播放视频*** 。

于是我连接上串口,然后在 `/dev` 里面瞄一眼看看都有些啥。

![看看都有啥设备](https://www.0xaa55.com/misc/mpidevdir.png)

很眼熟地看到了 ***cedar_dev 和 ion,以及 fb0*** 。赶紧去群里问了 (https://github.com/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 版本不符*** 。

!(https://www.0xaa55.com/misc/mpiglibc.png)

我用的是 gcc13,但看起来这个 buildroot 的镜像是 2020 年编译的。当年最新的 gcc 就是 gcc9,因此要用 gcc9 编译 ffmpeg.

#### 我放弃了自己编译 ffmpeg,虽然我找到了 gcc-arm-toolchain9,但是这玩意儿前缀名太长了,懒得弄。

我于是从 ffmpeg 官网找到 Linux 的 bin 的下载入口,然后打算根据日期挑选符合我的 glibc 版本的 ffmpeg 二进制。考虑到目标机器是 `armv5tejl`,但是人家不一定有,于是我按照最可能的情况来猜出适合我的机器的 ffmpeg 二进制。

![先找 Linux 的静态编译版](https://www.0xaa55.com/misc/mpiffmpeg.png)

![再找 Linux 的 Arm 版](https://www.0xaa55.com/misc/mpiffoldrelease.png)

![应该就是这里面的这几个](https://www.0xaa55.com/misc/mpiffarmel.png)

#### 可能是这个
(https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.3.1-armel-static.tar.xz)         2020-07-22 16:39   12M

下载后解压 `ffmpeg` 到 `/bin` 里,然后 `chmod +x /bin/ffmpeg` ,再执行一下 `ffmpeg` 看看,果然打印出了信息:

![就是这个](https://www.0xaa55.com/misc/mpiffisok.png)

### 进行视频播放

一开始只是为了测试,我没管视频文件本身的大小和它输出的帧数,想直接先跑起来看看,于是拔了 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
```
![ずっと真夜中でいいのに。『お勉強しといてよ』](https://www.0xaa55.com/misc/mpibenkyou.jpg)

#### 解决卡顿问题
我试过唯一能解决卡顿问题的方法是 ***预处理视频*** ,预先把视频压缩到 480x272 分辨率,然后使用 xvid 编码格式, ***不使用 h264*** 。

https://www.0xaa55.com/misc/mpihchrome.mp4

### 音频播放
按照卖家给的「产测工具」,我发现播放音频使用的命令是 `tinyplay`。我尝试过使用 FFmpeg 以直接输出 ALSA 的方式来把音频推流到声卡驱动,结果发现不成功,FFmpeg 提示「: Could not open device: No such file or directory.」(或者差不多类似的内容)。上网搜了一通,发现 FFmpeg 能把音频输出到管道里。那就有可能利用管道通信来实现音频的播放了。我于是尝试了一下 `tinyplay` 的管道输入功能,发现:

#### tinyplay 不支持管道输入
这简直是傻卵设计,气得我直接翻出它的源码:
(https://github.com/tinyalsa/tinyalsa/)

#### tinyplay 不支持管道输入,但是它的源码里面有打开管道进行输入的功能。

##### 理想的情况是这样的
- `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` 标准输入文件。

(其实也是一种傻卵设计)

![我提了 PR](https://www.0xaa55.com/misc/mpipr.png)

### 编译 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
[虚拟机下载](https://pan.baidu.com/s/1AHHTPn8XrRYqG4hJGtZkRw?pwd=aa55)

通过拖拽文件到虚拟机里,然后虚拟机安装 gcc-arm-linux-gnueabi(其中 gcc 版本是 5,可喜可贺),再修改源码。

![虚拟机 Ubuntu 16 系统](https://www.0xaa55.com/misc/mpiubuntu16.png)

再用以下命令编译,就得到了能用的 `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
```

AyalaRs 发表于 2024-5-11 17:28:28

这屏幕看起来也没必要bgra输出啊

0xAA55 发表于 2024-5-11 23:11:37

AyalaRs 发表于 2024-5-11 17:28
这屏幕看起来也没必要bgra输出啊

你给别的参数,它就说“不支持。请使用 bgra”

美俪女神 发表于 2024-5-16 14:16:58

这是不是自己做了一台山寨MP4播放器。

0xAA55 发表于 2024-5-17 09:33:24

美俪女神 发表于 2024-5-16 14:16
这是不是自己做了一台山寨MP4播放器。

我想把它做成类似古代苹果电脑那样的设计,大背头显示器,下部插 SD 卡,就像软盘一样,然后可以播放视频,可以有个桌面,可以做各种事。
页: [1]
查看完整版本: 【嵌入式】F1C100S「单片机」播放(软解)MP4 视频 480x272