你打开一个网页,视频开始播放。浏览器内核从头到尾检查了一遍——没找到<video>标签,没发现媒体流,连个<iframe>都没有。它看到的东西只有一样:一堆在 Canvas 上不停刷新的文字。
ASCILINE 做的事情核心就一句话:把视频拆成字符流,用 WebSocket 推到浏览器,拿 Canvas 当画板逐帧画出来。
浏览器不是不管,是压根不知道自己在播视频。
上面这张动图,左边是原始 MP4 视频,右边是 ASCILINE 的PIXEL Mode渲染结果。你看到的每一个像素块,背后是一个▮字符。浏览器以为自己只是在画彩色方块——它永远想不到自己在播放一段完整的视频。
它怎么做到的:Python 做重活,浏览器当画板
整个流程分三步:
后端用 Python+FastAPI 跑一个 WebSocket 服务。OpenCV 把视频逐帧解码,NumPy 把每个像素映射成对应的 ASCII 字符和颜色值。这块是真正的性能瓶颈——一帧画面要处理的像素矩阵,对于 360p 分辨率的等效网格来说,每一帧都是几万个字符的编码任务。
前端跑纯 Vanilla JS,不依赖任何框架。浏览器通过 WebSocket 收到二进制帧(Uint8Array格式),丢进一个 jitter buffer 缓冲,然后用requestAnimationFrame同步到 Canvas 上渲染。
同步机制最妙:音频轨道被设为"绝对主时钟"——画面必须严格跟随音频的节奏。音频快了,画面就跳帧跟上;音频慢了,画面就等。所以永远不会出现那种"嘴还在动,声音已经没了"的音画不同步。
重点:浏览器眼里,整个页面就是一个 Canvas 上「JavaScript 在更新文本」。所有基于<video>标签的限制——自动播放拦截、广告屏蔽规则、媒体策略管控——全部管不到它。
ASCII Text Mode 更夸张——你可以直接用鼠标选中画面里的字符,复制粘贴。一段"视频"变成了一段可以 Ctrl+C 的文本。这种体验在我第一次看到的时候,说实话,绷不住了。
第一次上手:四步把它跑起来
ASCILINE 不是开箱即用的 SaaS,需要自己在本地跑 Python 服务。但别慌——整个安装流程就四步,而且只要你跑通一次,以后每次用就是一个命令的事。
1. 拉代码
git clone https://github.com/YusufB5/ASCILINE.gitcd ASCILINE
2. 装 Python 依赖
pip install fastapi uvicorn opencv-python numpy websockets
这步的目的就是把 Python 端需要的几个核心库装上。FastAPI 负责 Web 服务,OpenCV 解码视频,NumPy 做像素到字符的矩阵映射,websockets 管前后端通信。
3. 装 FFmpeg(要听声音才装)
如果你只是想看画面,不关心音频,这步可以跳过。
需要音频的话,Windows 上跑一条命令:
winget install ffmpeg
macOS:brew install ffmpeg,Linux:sudo apt install ffmpeg。
这里最容易翻车:Windows 用户如果winget装不上,或者不想动系统环境变量——直接去 FFmpeg 官网下载 ZIP,把bin文件夹里的ffmpeg.exe解压出来,丢到 ASCILINE 项目根目录下,和stream_server.py放一起就行。
4. 启动服务
进项目目录后,选一种模式跑:
# 播单个视频python stream_server.py video.mp4 --cols 240# 播整个文件夹(按文件系统顺序,不是字母序)python stream_server.py --folder videos --cols 200# 无限循环python stream_server.py --folder videos --cols 230 --loop
然后浏览器打开http://localhost:8000,视频就开始播了。
配好之后,每次使用只需要两步:python stream_server.py video.mp4 --cols 240,然后打开http://localhost:8000。
如果你不想用浏览器界面,也可以在终端里直接看——ASCII 字符在 ANSI 终端里渲染,零闪烁、真彩色:
python ascii_video_player2.py video.mp4 --cols 100 --quality 0
唯一要注意的:播放期间别调终端窗口大小,字符排版会乱。
装好之后,你需要知道的几个核心玩法
--mode 渲染质量:1 到 5 档。1 是黑白,5 是 1600 万色全彩。日常用 mode 3 或 5 就够。
python stream_server.py --mode 5 --cols 240
--pixel 像素模式:把字符替换成彩色方块,画质接近 360p。这时候--cols建议拉到 600-900。
python stream_server.py video.mp4 --pixel --cols 600
--vol 音量控制:0 到 5。设成 0 的时候 FFmpeg 根本不会启动,省 CPU。
python stream_server.py video.mp4 --pixel --cols 560 --vol 0# 静音
--playlist 播放列表:不同视频可以分别指定 mode、vol、cols。
[ { "video": "intro.mp4", "mode": 1, "vol": 1 }, { "video": "main.mp4", "mode": 5, "pixel": true, "vol": 3, "cols": 520 }, { "video": "outro.mp4", "mode": 3, "vol": 2, "cols": 240 }]
其余命令(查看状态、调试等)平时基本用不到,完整列表见原文档。
为什么要做这个东西
ASCILINE 的作者 Yusuf 在 Reddit 上发了篇帖子介绍这个项目。他说自己一开始只是好奇——能不能让浏览器播视频但不让它知道自己播的是视频?
技术上这是可行的,因为 Canvas API 本来就允许你逐帧画任意内容。真正难的是性能。
"一开始纯文本渲染太重了。后来我用 Pixel command 优化加上二进制打包,终于把 360p 等效分辨率稳在了 30 帧。"
把几万个彩色字符块以每秒 30 次的频率推到浏览器 Canvas 上,还要不掉帧——这件事的难点不在算法,在工程细节。字符渲染相比原生视频解码,多了一层"字符→像素"的转换开销。他的优化思路是把编码好的像素数据直接用二进制格式(Uint8Array)通过 WebSocket 扔给前端,省掉了 JSON 序列化的 CPU 消耗和解码时间。
作者把项目的四个核心价值总结得很清楚:
- 绕过浏览器限制:不是媒体文件,不触发任何媒体策略
- 纯文字操控:可以给"视频"加 CSS 文字阴影、霓虹光效——因为每个像素块本质上是 DOM 文字
- AI 友好:字符流本身就是结构化的文本数据,本地 LLM 可以直接消费,不用先跑一套视觉模型
- 超低带宽:服务端做了视频解码的重活,只传"几 KB"的字符串包,弱网环境也能跑
还有个反广告条款,值得单独说说
ASCILINE 用的是 MIT 许可证,但加了一条特殊的反广告限制条款:
因为本引擎通过渲染纯文本绕过了浏览器标准限制和广告拦截器,我们严格禁止广告网络使用它来投放不可拦截的广告。
换句话说:这个工具可以绕过任何浏览器拦截,但作者用法律条款守住了一条底线——你不能拿它来发广告。
在开源项目里看到这种"道德护栏",说实话不多见。但也侧面说明了一个问题:一个能绕过所有浏览器限制的视频引擎,落到广告商手里会变成什么?
它的边界在哪里
ASCILINE 不是来取代<video>标签的。
性能有天花板。ASCII 模式推荐--cols 200-240,Pixel 模式推荐--cols 600-900。如果你在笔记本上把--cols拉到 1350,Python 后端来不及编码和发送帧数据——画面跟不上音频,音画会明显不同步。解决方法是降低--cols。
Python 是瓶颈。作者自己说了,Python 已经压榨到了极限,下一步是用 Rust 重写整个核心引擎。目前 Python 端能稳住的帧率和分辨率,取决于你的 CPU 有多强。
适用场景有限。它更适合实验、演示、iot 设备这些对带宽极度敏感或对浏览器限制极其头痛的场景。拿来替代 YouTube 或者日常看视频?不现实。
重点:这个工具真正适合的是那些「普通视频方案跑不动或被限制住」的开发者场景——嵌入式、IoT、数字标牌、AI 视频分析。如果你不属于这几类,收藏一下就好,等项目进化。
ASCILINE 让我想起当年人们第一次用<canvas>画游戏的震撼——限制不是用来遵守的,是用来绕过去的。而这个项目绕过去的姿态,还顺便给自己加了道道德保险。
参考链接:[1] GitHub 仓库:https://github.com/YusufB5/ASCILINE[2] Reddit 原帖:https://www.reddit.com/r/SideProject/comments/1u2z50q/i_built_an_unblockable_video_stream_it_renders/
点赞·转发·小心心❤️欢迎在评论区留下你的想法!
— 完 —