Chạy trên Orange Pi Rockchip 3588 · English · Screenshot
Công cụ chuyển trang HTML hoạt hình chia theo cảnh kèm TTS thành video MP4. Có giao diện web, render song song nhiều tiến trình Chromium, mã hóa CPU / NVIDIA NVENC / Rockchip MPP. Tối ưu Orange Pi 5 Plus (RK3588), chạy được trên Linux x86_64 hoặc ARM64.
Xem đủ ảnh: screenshot.md
- Tính năng
- Luồng xử lý
- Định dạng HTML
- Cấu trúc thư mục
- Cài đặt
- Cấu hình
- Sử dụng
- Hiệu năng & sự cố
- Bảo mật & giới hạn
- Upload
.html/.zip(HTML + ảnh, font, CSS); mỗi dự án một thư mục; xóa dự án trên UI - Xem trước theo tỉ lệ khung hình; danh sách cảnh / lời thoại / thời lượng ước tính
- Theme sáng / tối
- TTS: Edge (online), ElevenLabs / VieNeu (online, cần API key) hoặc Piper offline (load model 1 lần trong process, synth tuần tự)
- Khung 16:9 / 9:16 / 4:3, độ phân giải 720p–4K, FPS 24/25/30/60
- Phụ đề trên video + luôn xuất
.srt - Encoder:
libx264/libx265, NVENC,h264_rkmpp/hevc_rkmpp— tự dò trên máy - Render song song N Chromium; hàng đợi, tiến độ, hủy, xóa job (MP4/SRT +
build/)
Trình duyệt → Flask (app.py) → hàng đợi (1 job/lúc) → renderer.py
① Phân tích Chromium đọc .scene[data-say][data-t]
② TTS Edge/ElevenLabs/VieNeu song song thấp + rải · Piper warm + tuần tự
③ Render N worker Chromium · seek CSS animation · JPEG → ffmpeg → .ts
④ Ghép concat (-c:v copy) + audio AAC → MP4 + SRT
Ứng dụng không quay màn hình realtime. Mỗi khung g được tua bằng Web Animations API (currentTime), nên máy chậm vẫn đúng FPS và chia được nhiều Chromium.
HTML đầu vào: chỉ hỗ trợ CSS animation / transition. Không tua được requestAnimationFrame, canvas/WebGL, GSAP, <video>, GIF.
Mỗi cảnh: class="scene", data-say (lời TTS, có thể trống), data-t (thời lượng tối thiểu, giây). Cảnh active nhận class active.
<section class="scene" data-t="5" data-say="Xin chào các bạn.">
<h1 class="a">Tiêu đề</h1>
</section>
<style>
.scene{position:absolute;inset:0;opacity:0}
.scene.active{opacity:1}
.a{opacity:0}
.scene.active .a{animation:up .8s forwards}
@keyframes up{from{opacity:0;transform:translateY(40px)}to{opacity:1;transform:none}}
</style>Nếu trang tự chuyển cảnh bằng JS:
if (!navigator.webdriver) { /* slideshow tự động */ }Ví dụ đầy đủ: examples/demo.html. ZIP: ưu tiên index.html, dùng đường dẫn tương đối.
html2video/
├── app.py / renderer.py / bench.py
├── install.sh / run.sh / .env.example
├── static/index.html
├── scripts/ # ffmpeg-rockchip, perf-mode, get-piper-voice
├── examples/demo.html
├── voices/ # Piper *.onnx + *.onnx.json
├── uploads/ outputs/ build/
├── screenshots/ # ảnh UI
├── screenshot.md
├── README.md # tiếng Việt (file này)
└── README-en.md # English
- Quạt + tản nhiệt; nguồn đủ; nên dùng NVMe
- Kernel BSP Rockchip (có
/dev/mpp_service) nếu muốn encoder Rockchip
uname -r
ls -l /dev/mpp_service /dev/rga /dev/dri
# FFmpeg có rkmpp (20–40 phút) — tùy chọn
bash scripts/build-ffmpeg-rockchip.sh
# rồi trong .env: FFMPEG_BIN=/usr/local/bin/ffmpeg , FFPROBE_BIN=/usr/local/bin/ffprobe
./install.sh --service
sudo systemctl status html2video
# hoặc: ./install.sh && ./run.shMở http://<IP>:5000 → thanh trạng thái cần FFmpeg ✅. Upload examples/demo.html, render 720p thử.
Piper offline (tùy chọn):
bash scripts/get-piper-voice.shBấm ↻ Kiểm tra lại. Ưu tiên piper-tts trong .venv (warm model). PIPER_BIN chỉ là fallback native.
./install.sh --serviceNVIDIA: driver + FFmpeg có nvenc; số luồng ≤ giới hạn phiên NVENC. Docker/VM: --shm-size=2g (hoặc --ipc=host).
Sửa .env (từ .env.example), rồi sudo systemctl restart html2video.
| Biến | Mặc định | Ý nghĩa |
|---|---|---|
HOST / PORT |
0.0.0.0 / 5000 |
Địa chỉ web |
FFMPEG_BIN / FFPROBE_BIN |
ffmpeg / ffprobe |
Đường dẫn FFmpeg |
PIPER_BIN |
piper |
Fallback native |
PIPER_CONCURRENCY |
1 |
Giữ 1 trên RK3588 — song song dễ hard-reboot |
PIPER_ORT_THREADS / PIPER_OMP_THREADS |
2 / 2 |
Thread ORT/OpenMP |
EDGE_CONCURRENCY |
2 |
Số request Edge TTS song song (giữ thấp để tránh throttle) |
EDGE_STAGGER |
0.4 |
Giây rải đều các request Edge (0 = tắt) |
ELEVENLABS_API_KEY |
— | API key ElevenLabs (bật tính năng ElevenLabs) |
ELEVENLABS_VOICE_ID |
21m00Tcm4TlvDq8ikWAM |
Voice ID mặc định |
ELEVENLABS_MODEL_ID |
eleven_multilingual_v2 |
Model (hỗ trợ tiếng Việt) |
ELEVENLABS_CONCURRENCY |
1 |
Số request ElevenLabs song song (free tier giữ 1) |
VIENEU_BASE_URL |
URL mẫu instabox | Base URL máy chủ VieNeu-TTS |
VIENEU_API_KEY |
— | Bearer token VieNeu (bật tính năng VieNeu) |
VIENEU_MODEL |
vieneu-v3-nano |
Model VieNeu |
VIENEU_VOICE |
Mai Anh |
Giọng mặc định (preset) |
VIENEU_CONCURRENCY |
1 |
Số request VieNeu song song |
CHROMIUM_ARGS / CHROMIUM_GPU_ARGS |
— | Cờ Chromium |
- Upload / chọn dự án (có nút Xóa → xóa
uploads/<id>/) - Chọn TTS (Edge · Piper · ElevenLabs · VieNeu), giọng đọc, tốc độ đọc, khung hình, độ phân giải, encoder…
- Render MP4 → theo dõi Hàng đợi (Hủy / Xóa job / tải MP4·SRT)
- Đổi theme sáng/tối trên header
Mọi thao tác trên UI đều gọi được bằng REST: upload & chọn dự án, xem danh sách cảnh và thời lượng ước tính, render với đầy đủ tùy chọn (TTS + giọng mới, tốc độ đọc, khung hình, độ phân giải, encoder…), theo dõi hàng đợi, và lấy link tải MP4/SRT về máy.
➡️ Tài liệu đầy đủ: API.md — bảng tham số, mã lỗi, ví dụ end-to-end bằng Bash và Python.
Luồng tối thiểu:
BASE=http://localhost:5000
# 1) Upload → project id
PID=$(curl -s -F file=@examples/demo.html "$BASE/api/upload" | jq -r .id)
# 2) Render → job id
JID=$(curl -s -X POST "$BASE/api/render" -H 'Content-Type: application/json' -d "{
\"project\": \"$PID\", \"tts\": \"vieneu\", \"voice\": \"Mai Anh\", \"rate\": 5,
\"aspect\": \"9:16\", \"resolution\": \"1080p\", \"fps\": 30,
\"subtitles\": true, \"encoder\": \"rockchip\", \"codec\": \"h264\", \"workers\": 0
}" | jq -r .id)
# 3) Chờ tới khi status = "done"
curl -s "$BASE/api/jobs" | jq ".[] | select(.id==\"$JID\") | {status,progress,message}"
# 4) Tải MP4 (+ .srt cùng tên) về máy
OUT=$(curl -s "$BASE/api/jobs" | jq -r ".[] | select(.id==\"$JID\") | .output")
curl -s -o "$OUT" "$BASE/outputs/$OUT?dl=1"Nhóm route chính:
| Nhóm | Route |
|---|---|
| Năng lực & giọng | GET /api/capabilities · /api/voices · /api/elevenlabs/voices · /api/vieneu/voices |
| Dự án | POST /api/upload · GET /api/projects · GET /api/projects/<pid> · DELETE /api/projects/<pid> |
| Render | POST /api/render |
| Hàng đợi | GET /api/jobs · POST /api/jobs/<id>/cancel · DELETE /api/jobs/<id> |
| Kết quả | GET /api/outputs · GET /outputs/<name>?dl=1 · DELETE /api/outputs/<name> |
RK3588 gợi ý: Rockchip H.264 · luồng Tự động (4) · 30 fps · 1080p. Đo bằng:
.venv/bin/python bench.py examples/demo.html 1080p| Hiện tượng | Cách xử lý |
|---|---|
| Rockchip ❌ | Đặt FFMPEG_BIN, restart; quyền /dev/mpp_service; kernel BSP |
| Reboot khi Piper | Không tăng PIPER_CONCURRENCY / không spawn nhiều process ONNX |
| WAV rỗng / Piper lỗi | Job trước bị cắt; xóa job, chạy lại; dùng warm piper-tts |
| Chromium thiếu | playwright install chromium (+ install-deps) đúng user service |
| Worker crash / thiếu RAM | Giảm luồng hoặc độ phân giải |
| Animation lệch | Chỉ CSS animation; bọc slideshow trong !navigator.webdriver |
| Nóng → chậm dần | Tản nhiệt; scripts/perf-mode.sh |
- Không có đăng nhập, mặc định
0.0.0.0:5000— chỉ LAN tin cậy. Ra ngoài:HOST=127.0.0.1+ Nginx auth / VPN / HTTPS - HTML upload chạy trong Chromium trên máy chủ — chỉ render nguồn tin cậy
- 1 job render tại một thời điểm; hàng đợi không persist sau restart (file trong
outputs/vẫn còn) - Phụ đề ước lượng theo ký tự; Edge TTS là dịch vụ không chính thức
