找回密码
立即注册
搜索
发回帖 发新帖

4898

积分

0

好友

636

主题
发表于 昨天 23:34 | 查看: 3| 回复: 0

家里那块 i.MX6ULL 是单核 Cortex-A7,没有 VPU(视频硬解单元),内存 489MB。翻译一下:H.264 全靠 CPU 一颗核硬啃。某天盯着它那块 1024x600 的小屏,冒出个念头:能不能把它做成一台真正的电视?插上电自己联网、自己出画面、能换台。

终于有时间给它做完了。现在它每天早上跟我一起开机,播 360p 直播源,20 FPS 上下,能摸屏换台,也能按板载那两个小按键换台。

源码和整套 macOS 上的开发环境都在这里,AGPL-3.0 开源:

https://gitee.com/morixinguan/100ask_imx6ull_for_mac

先说明一句:本文只讨论技术实现,不提供任何直播源地址,实际使用时请使用你自己拥有合法接收权限的源。

下面这几张不是渲染图,是从板子显存里直接抓出来的真实画面(仅为技术演示)。

开机动画:

i.MX6ULL网络电视开机启动画面

正在播放:

i.MX6ULL播放CCTV直播频道画面

左上角那行白字是换台后叠 8 秒的频道名水印,到点自己消失。

点屏幕中间那块会弹出频道列表,当前台是绿的(下图里的频道名仅为技术演示):

i.MX6ULL网络电视频道选择列表

先说清楚:这板子有多弱

i.MX6ULL 是单核 Cortex-A7,没有 VPU(视频硬解单元),内存 489MB。我上电后一条条查出来的底细:

  • SoC:i.MX6ULL,单核 Cortex-A7
  • 内存:489 MB
  • 内核 / 根文件系统:Linux 4.9.88 / Buildroot 2020.02(glibc 2.30)
  • 屏幕:fb0 = 1024x600,32bpp
  • 触摸 / 按键:电容屏 + 板载两个 gpio 按键
  • 网络:RTL8723BU WiFi(这颗后面坑了我一下)
  • 显示输出:LCD 之外还有 HDMI,靠一颗 sii902x 转接芯片

板子还带了音频(wm8960)、SPI、I2C、看门狗这些,能玩的挺多。只是这次用不上。

思路:一行像素都不自己画

一开始我想的是拉流、解码、写屏全自己干。想了两分钟放弃了:软解 H.264 不是周末能写完的东西。

可板子上本来就跑着 ffmpeg 4.2.3。那就让它干重活。

最后写出来的主控程序不到 700 行 C,只干四件事:

  • 读频道表,决定现在该播哪个地址
  • 拼一条 ffmpeg 命令,fork + execvp 出去
  • 收触摸、按键、信号,翻译成换台 / 菜单 / 退出
  • 看护 ffmpeg 子进程(掉了重连),顺便盯着屏幕别黑、别闪光标

画出来是这样:

i.MX6ULL网络电视技术架构图

这么分层的几个理由:

  • 解码交给 ffmpeg:自己写 H.264 解码不现实。
  • 输出直接写 /dev/fb0:板子没跑 X11/Wayland,fbdev 是最短路径。
  • ffmpeg 当子进程:崩了不拖垮主控,换台就是杀掉重启。
  • 换台用信号:串口里一条 kill 就能换台,调试太方便了。

核心其实只有一条命令

主控最实在的产出,是拼出一条 ffmpeg 命令。真实长这样:

ffmpeg -nostdin -loglevel warning \
-rw_timeout 15000000 -probesize 10M -max_delay 800000 \
-i "https://.../index_4.m3u8" \
-map 0:v:0 -an \
-sws_flags fast_bilinear \
-vf "scale=1024:600:force_original_aspect_ratio=decrease,pad=1024:600:(ow-iw)/2:(oh-ih)/2:color=black,drawtext=text='频道名':x=36:y=28:fontsize=34:enable='lt(t,8)'" \
-pix_fmt bgra -f fbdev /dev/fb0

几个不那么显然的参数:

  • -rw_timeout:15 秒读不到数据就让 ffmpeg 自己退出,交给主控重连。不然它会卡在一条死链上一动不动。
  • -an:关音频。音频解码还要再吃一截 CPU,而这板子没接音箱。
  • enable='lt(t,8)':频道名水印只显示 8 秒。
  • -sws_flags fast_bilinear:别用默认的 bicubic,那是 ffmpeg 最慢的一档缩放。

最后一个是后面调优时才发现的关键,先记着。

开机:别让用户盯着黑屏

第一版跑起来的时候,从通电到出画面要黑着等几十秒,跟死机没区别。真机顶盒不是这样的。

改成两段:上电就先画一张静态的"正在启动",把联网、对时那几十秒盖住;播放器起来后再放一段 Logo 加进度条的动画:

i.MX6ULL机顶盒开机流程时序图

阶段一长这样(联网那几十秒屏幕上就是它):

网络电视启动中加载界面

有个细节:画这张图之前,脚本先把 fb0 的分辨率改回面板原生的 1024x600。原因后面讲 HDMI 那段说。

动画默认 5 秒,摸一下屏、按个键、或者发个信号都能跳过。想换成真正的开机广告,往 /etc/iptv/ 丢一个 boot.mp4 就行,不用改代码。

交互:屏幕分三块,够用

没上 UI 框架。屏幕切三份:上三分之一上一台、下三分之一下一台、中间弹列表;板载两个按键映射成上下;串口里 kill -USR1 也能换台。

换台中间有 1 到 2 秒空档(HLS 要重新探测加缓冲,改不掉)。这段时间屏幕上还留着上一个台的最后一帧,看着就像按了没反应。所以切台时先顶一张静态提示上去,0.3 秒就能画完:

切换频道时显示当前频道信息

起播慢治不了,但"看得出它在切"和"以为死机了"是两种体验。

从 9.7 FPS 到 20 FPS

第一版能播了,但只有 9.7 FPS,动起来一顿一顿。我以为是解码太慢,毕竟单核。

实测之后发现完全不是。

把 640x360 的源拆开单独测(CPU 锁 792MHz):纯解码、输出到 null,能到 47 FPS;加上缩放到 1024x600,直接掉到 8 FPS。

贵的根本不是解码,是 swscale 的插值。缩放这一下吃掉了 83% 的时间。

各环节帧率开销对比柱状图

优化后端到端帧率对比

于是按顺序做了三件事:

  1. 把 CPU 锁在 792MHz。默认的 ondemand 策略实测就赖在 198 / 528MHz,满载了都来不及升。9.7 到 14 FPS。
  2. 不放大。1:1 直通,四周补黑边。14 到 20 FPS。
  3. 缩放换 fast_bilinear。满屏档从 8 提到 14。

最后 360p 源稳定在 20 FPS,画面不抖。代价是画面只占屏 62%,四周有黑边。

过程中撞到好几个反直觉的结论:

  • 想把写屏带宽省一半,把 fb0 切成 16bpp,结果更慢(16 vs 21 FPS)。rgb565 的转换路径没有 bgra 优化得好。
  • 加 -threads 1 反而从 19 掉到 14 FPS。单核上多线程也能让解码和缩放流水重叠。
  • 放大到 85% 屏宽和放大到 100% 几乎一样贵。开销花在"放大"这个动作上,跟放大多少关系不大。所以要么不放大,要么直接满屏,中间档没意义。
  • 挑源比优化管用:有个国际新闻源的最低档正好是 1024x576,跟屏幕一样,不用插值,实测 13 FPS,比 640x360 放大到满屏(11 FPS)还流畅。
  • HLS 的 master.m3u8 会让 ffmpeg 默认挑码率最高的变体。有个源最高档是 1080p / 4.6Mbps,这块板实测 0 帧。所以频道表里要直接写低码率的子地址。

踩过的坑(这段最值钱)

画面根本没写进去,但一切看起来都正常。 这是最坑的一条。fbdev 的像素格式得跟 fb0 位深对上,写死 bgra 时 ffmpeg 只报一句 Pixel format bgra is not supported, use rgb565le,进程照样跑,日志照样有。

而且这块板的 fb0 参数每次重启还可能变,实测出现过 1024x600/32bpp 和 1280x720/16bpp 两种。现在程序启动先读 sysfs 决定尺寸和 pix_fmt。想确认画面到底有没有写进去,md5sum /dev/fb0 连采两次看变没变最快。

看十分钟自己黑屏。 内核的 console blanking,默认 600 秒。背光其实是好的,别去调背光。sysfs 那个 consoleblank 文件在这块板是只读的,写它只会 Permission denied。真正管用的是三层:fw_setenv 加 consoleblank=0、开机脚本往 tty1 写 ESC[9;0]、播放器每 10 秒再补写一次。

画面上一直有个光标在闪。 那是 fbcon 画在 framebuffer 上的,跟 ffmpeg 抢同一块显存(printk 和 getty 的登录提示也会直接叠在视频上)。要解绑的是 vtcon1 不是 vtcon0,vtcon0 是 dummy device 的空壳。

开机后串口敲命令没反应。 busybox init 串行跑 /etc/init.d/S*。我之前把联网和对时写在前台,ntpd 没网时卡住,init 被堵死,串口再也要不到 shell(有回显,命令不执行)。整个流程必须用 ( ... ) & 放后台。

WiFi 怎么都连不上。 RTL8723BU 在 wlan0 上 carrier 恒为 0、发不出数据帧,日志反复 nolinked power save;NetworkManager 还会跟手动的 wpa_supplicant 抢接口。重载驱动关掉省电、停掉 NetworkManager、改用 wlan1,就通了。

接上电视之后,坑更深

这块板的 HDMI 不是独立显示通道。SoC 没有 HDMI 控制器,底板上是一颗 sii902x 发射芯片,把 LCDIF 的并行 RGB 信号转成 HDMI,绑的还是同一个 fb0。所以电视上看到的就是 fb0 的内容,不存在第二块显存。

插上 HDMI 的那一刻,驱动会读电视的 EDID,然后直接把 fb0 从 1024x600/32bpp 改成 1280x720/16bpp。播放器还按旧参数输出,轻则画面错位,重则一帧都写不进去。

更麻烦的是 LCD 被连累:面板物理只有 1024x600,一个物理像素对一个数据像素,不会自己缩放。你给它一路 1280x720 的信号,它只能按自己的时序去采,采不到的地方就是黑边,实测左边黑了约五分之一。

一个 fb0 接两块屏,分辨率只能二选一。所以留了个开关:默认优先 LCD,开机时把 fb0 改回面板原生时序(电视自己缩放,画面完整);想让电视点对点最清晰,设 IPTV_SCREEN=hdmi 保持 1280x720,代价是 LCD 有黑边。

HDMI连接电视播放航拍中国画面

频道从哪来

网上流传的那些电视台 m3u8 聚合列表基本都是未授权转发的盗链源,别用,也别放进频道表。用你自己有接收权限的源:

  • 家里运营商 IPTV 的组播地址(rtp://239.x.x.x:xxxx):你已经付过费的业务,从自家 IPTV 盒子的设置里查,或者用运营商公开的频道列表文件就能拿到,最稳也最合规。
  • 电视台官网 / 官方 CDN 上免费公开的那部分直播地址。

试过一个国际新闻台的官方 HLS,最低档正好 1024x576,播起来最舒服(下图仅为技术演示):

France 24新闻频道播放画面

顺便提醒:电视台自己公开的那批海外公共频道,在普通家庭宽带下基本连不上,别在这上面耗时间。

还能往哪走

板子上挂着一个 PXP 硬件 2D 加速器(/dev/pxp_device),缩放和色彩转换本来可以甩给它,只是板上 gstreamer 没装对应插件。自己写 PXP 调用(mmap + ioctl)预计还能再翻一倍。

想省事呢就换带 VPU 的平台。i.MX6ULL 天生没这个单元,360p 下 20 FPS 已经是这颗核的诚实水平。

别的方向:用 LVGL 画更讲究的 OSD(得处理它跟 ffmpeg 抢 fb0 的问题,双缓冲或者 overlay);音频其实已经支持 alsa,只是这块板没接音箱,默认关着。

说回这块板子

我买这块 100ask(韦东山老师团队)的 i.MX6ULL 本来是想学驱动的,结果先拿它当了一回电视。

做完这个项目之后,我觉得它特别适合干这种事,原因很实在:

  • 屏幕、电容触摸、按键、WiFi、HDMI、音频全都在板子上,插上就能用,不用额外买一堆模块。做"看得见摸得着"的项目,这点太重要了。
  • 资料是公开的。我全程查的是 100askTeam 在 GitHub 上的那群仓库,Linux 4.9.88 内核源码、裸机例程、驱动实验源码和文档、还有《嵌入式 Linux 应用开发完全手册》都在里面,对着板子上的实际情况一条条核对,都能对上。
  • 它足够"弱",弱到你必须去理解每一层。就这么一块没有 VPU 的单核 A7,逼着我把 framebuffer 像素格式、evdev 输入、HLS 分片、swscale 缩放全都摸了一遍——这些恰好就是嵌入式 Linux 应用开发最核心的那几条路。

板子的资料和社区都还在更新。碰到卡住的地方,能翻到别人踩过的记录,比一个人对着 datasheet 硬扛强太多。

真正值钱的其实不是那 700 行代码,是踩坑清单里那十几条。每一条都是熬出来的。

想自己动手

工程就在 examples/03_iptv/:一个 660 行的 main.c、一份频道表、一个开机脚本。整个仓库(含 macOS 上的交叉编译工具链、串口和 ZMODEM 下发脚本,还有另外两个例程)在这儿,AGPL-3.0 开源:

https://gitee.com/morixinguan/100ask_imx6ull_for_mac

编译和部署:

make -C examples/03_iptv deploy

推上去之后 /etc/init.d/S95iptv start 就开始播了。

软件分层、数据流、进程模型、每个模块怎么实现的,还有完整踩坑清单,都写在 examples/03_iptv/ARCHITECTURE.md 里,配了流程图,可以对着源码看。

祝你的板子也别再吃灰。这类偏底层的折腾过程,在云栈社区也能找到不少同好分享的踩坑记录,碰到问题不妨去翻一翻。

免责声明:本文为个人技术实践分享,只讲播放器的实现原理,不提供也不推荐任何直播源地址。文中出现的频道画面仅用于说明技术效果,相关权利归原权利人所有。实际使用时请使用自己拥有合法接收权限的源,并遵守当地法律法规。本文未附加任何购买链接。




上一篇:蛋仔派对UGC生态升级:双子星编辑器如何同时满足创作者与玩家
下一篇:NOR Flash僵局松动:AI服务器拉动价格翻倍,国产2Gb芯片送测
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-7 01:39 , Processed in 0.073293 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表