Cách tạo style subtitle .ass cho video dọc 1080x1920
💡 Để tạo style subtitle .ass cho video dọc 1080x1920, đặt
PlayResX: 1080vàPlayResY: 1920trong[Script Info], sau đó dùngFontsize=68,Outline=3,Alignment=2vàMarginV=180trong[V4+ Styles]. Mọi giá trị ASS đều tính tương đối theo PlayRes, nên chúng chỉ đúng khi PlayRes khớp với kích thước frame thực tế. Nhúng bằng:ffmpeg -i input.mp4 -vf "ass=in.ass" -c:v libx264 -crf 18 -preset slow -c:a copy output.mp4.
Ý chính
- Đặt
PlayResX: 1080vàPlayResY: 1920trong[Script Info]trước khi thay đổi bất kỳ giá trị nào khác. ASS scale mọi cỡ chữ, lề và vị trí theo PlayRes; kích thước sai dẫn đến output lệch hết. - Dùng
Fontsize=68đến80trong dòng Style cho canvas 1080x1920. Đây là khoảng tôi dùng cho bản giao hàng creator sau khi test trên màn hình điện thoại thực tế. - Đặt
MarginV=180để tránh các nút like/share/comment của TikTok và Reels chồng lên subtitle ở phía dưới. 180px trên canvas 1920px tương đương khoảng 9,4% từ mép dưới. - Dùng
Outline=3vàBorderStyle=1. Nền video dọc phức tạp hơn video ngang; viền dày hơn giữ cho chữ dễ đọc trên mọi cảnh quay. - Màu trong ASS dùng định dạng ABGR hex: trắng =
&H00FFFFFF, viền đen =&H00000000. Hai ký tự hex đầu là kênh alpha,00= hoàn toàn không trong suốt.
PlayResX và PlayResY thực sự kiểm soát điều gì?
ASS là định dạng độc lập với độ phân giải. Mọi giá trị độ dài trong file - cỡ chữ tính theo điểm, lề và vị trí - đều được chỉ định tương đối so với canvas ảo xác định bởi PlayResX và PlayResY trong [Script Info]. Khi libass hoặc ffmpeg render file này lên frame video thực tế, tất cả các giá trị đó được scale từ canvas PlayRes sang độ phân giải đầu ra thực.
Nếu file .ass ghi PlayResX: 1280 và PlayResY: 720 (giá trị mặc định phổ biến), nhưng video đầu ra là 1080x1920, libass sẽ render trên canvas cao 720px rồi scale kết quả lên để lấp đầy 1920 pixel. Một MarginV=50 trông ổn ở canvas 720px sẽ chỉ tương đương khoảng 133px trên canvas 1920px - không đủ để tránh UI của nền tảng. Cỡ chữ cũng thu nhỏ tương ứng.
Cách khắc phục: luôn đặt PlayResX và PlayResY khớp với kích thước đầu ra thực tế của video trước khi đặt bất kỳ giá trị cỡ chữ hay lề nào. Với video dọc 1080x1920: PlayResX: 1080 và PlayResY: 1920.
Khối style .ass hoàn chỉnh cho 1080x1920
Đây là cấu trúc file .ass tôi bắt đầu từ đó trong mọi dự án video dọc. Tôi lưu đây làm template và sao chép trước mỗi công việc mới.
[Script Info]
ScriptType: v4.00+
PlayResX: 1080
PlayResY: 1920
WrapStyle: 0
ScaledBorderAndShadow: yes
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,68,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,3,0,2,20,20,180,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,First subtitle line.
Dialogue: 0,0:00:04.50,0:00:07.00,Default,,0,0,0,,Second subtitle line.
Các trường chính trong dòng Style, theo đúng thứ tự của dòng Format:
- Fontsize=68: cỡ chữ tính theo điểm ASS, scale theo PlayResY. Với canvas 1920px, giá trị 68 hiển thị cỡ đọc thoải mái trên màn hình điện thoại.
- PrimaryColour=&H00FFFFFF: màu trắng, hoàn toàn không trong suốt (kênh alpha
00). - OutlineColour=&H00000000: viền đen. Luôn đặt giá trị này rõ ràng; nếu bỏ trống, libass có thể dùng viền xám hoặc không hiển thị viền.
- Bold=0: tắt in đậm. Với Fontsize=68 và Outline=3, chữ đã đủ nặng. Tôi chỉ chuyển thành
-1(bật in đậm trong ASS) khi dùng font nhẹ cần thêm trọng lượng. - BorderStyle=1: chế độ viền và bóng tùy chọn. Đặt thành
3sẽ chuyển sang hộp nền mờ đằng sau chữ, tôi dùng khi nền cảnh quay quá phức tạp. - Outline=3: viền 3px. Nặng hơn 1px so với mặc định 2px tôi dùng cho video ngang, vì nền video dọc phức tạp hơn.
- Shadow=0: tắt bóng đổ. Bóng đổ chồng lên viền 3px ở cỡ chữ này gây rối mắt hơn là giúp ích.
- Alignment=2: dưới giữa (theo bàn phím số: 1=DT, 2=DG, 3=DP, 4=GT, 5=GG, 6=GP, 7=TT, 8=TG, 9=TP).
- MarginL=20, MarginR=20: lề ngang 20px mỗi bên. Quan trọng hơn khi canvas chỉ rộng 1080px so với 1920px ở video ngang.
- MarginV=180: 180px tính từ mép dưới canvas 1920px.
- ScaledBorderAndShadow: yes (trong [Script Info]): scale giá trị Outline và Shadow đồng bộ khi video được scale sang độ phân giải khác sau này.
Những trường nào trong dòng Style cần thay đổi cho video dọc?
Khi chuyển template .ass từ video ngang sang 1080x1920, đây là các trường tôi điều chỉnh cụ thể. Mọi trường còn lại giữ nguyên.
| Trường | Video ngang 1920x1080 | Video dọc 1080x1920 | Lý do thay đổi |
|---|---|---|---|
PlayResX / PlayResY | 1920 / 1080 | 1080 / 1920 | Canvas xoay chiều; tất cả giá trị khác scale từ đây. |
Fontsize | 52 | 68-80 | Tính theo PlayResY. Canvas cao hơn cần số lớn hơn để đạt cùng cỡ chữ nhìn thấy được. |
Outline | 2 | 3 | Nền video dọc phức tạp hơn. Viền dày hơn giữ chữ rõ trên mọi cảnh. |
MarginV | 50 | 180 | Phải tránh UI nền tảng (nút TikTok/Reels). Video ngang không có lớp chồng tương đương trên canvas. |
MarginL / MarginR | 10 | 20 | Canvas rộng 1080px thay vì 1920px. Cùng tỉ lệ lề ngang cần số pixel lớn hơn trên canvas hẹp hơn. |
Thứ tự trường trong dòng Style là cố định và phải khớp với dòng Format:: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding. Đếm dấu phẩy nếu bạn chỉnh sửa dòng này trực tiếp.
Làm thế nào để đặt MarginV tránh UI của TikTok và Reels?
Với video ngang, MarginV=50 thoải mái tránh mọi thanh điều khiển player. Video dọc 9:16 khác hoàn toàn. TikTok, Instagram Reels và YouTube Shorts đặt các nút tương tác (like, bình luận, chia sẻ, theo dõi, âm thanh) trực tiếp lên trên canvas video. Các nút đó nằm trong một dải chạy từ khoảng 10-20% phía dưới lên.
Các nền tảng không công bố kích thước pixel chính xác cho các lớp chồng này theo dạng đặc tả subtitle. Quy tắc an toàn của tôi: với 1080x1920, dùng MarginV=180 làm điểm khởi đầu. 180 pixel trên canvas 1920px tương đương khoảng 9,4% từ mép dưới. Trên mọi nền tảng short-form tôi đã test, giá trị này đủ để tránh các nút tương tác.
Nếu video có thêm chữ hoặc đồ họa nổi bật phía dưới, tôi đẩy lên MarginV=220. Khi cần hiển thị một dòng subtitle lên đầu frame - ví dụ người nói đang ở phần dưới và các nút nền tảng ở phía dưới - tôi thêm thẻ override inline {\an8} vào đầu trường Text của dòng Dialogue đó. Thẻ \an8 chỉ ghi đè Alignment cho duy nhất cue đó mà không ảnh hưởng các cue còn lại.
Nhúng cứng file .ass vào video với ffmpeg -vf ass=
Khi file .ass đã được style và căn chỉnh thời gian, lệnh burn-in rất đơn giản. Bộ lọc ass render file trực tiếp bằng libass, với toàn bộ style đã nằm sẵn trong file. Không cần thêm force_style.
ffmpeg -i input.mp4 \
-vf "ass=in.ass" \
-c:v libx264 -crf 18 -preset slow \
-pix_fmt yuv420p \
-c:a copy \
output.mp4
Giải thích từng cờ:
-vf "ass=in.ass": render file .ass bằng libass. Vì PlayRes trong file khớp với kích thước video, không xảy ra biến dạng khi scale.-c:v libx264 -crf 18 -preset slow: mã hóa lại H.264 ở chất lượng gần lossless. Burn-in luôn đòi mã hóa lại video; không thể dùng-c:v copykhi có video filter.-pix_fmt yuv420p: lấy mẫu màu 4:2:0 để tương thích rộng với thiết bị và nền tảng.-c:a copy: sao chép âm thanh nguyên vẹn. Subtitle filter không ảnh hưởng đến luồng âm thanh.
Trước khi encode cuối, tôi xem thử nhanh bằng ffplay:
ffplay -vf "ass=in.ass" input.mp4
Lệnh này render subtitle trong cửa sổ xem thời gian thực mà không ghi file, giúp tôi kiểm tra cỡ chữ và lề trên frame thực tế trước khi commit encode đầy đủ.
Khi nào nên dùng soft subtitle track thay vì burn-in?
Burn-in phù hợp khi bản giao hàng cuối là một video social duy nhất và người xem không có nút điều khiển subtitle. Nhưng tôi dùng soft track (luồng subtitle riêng được ghép vào container video) trong các trường hợp sau:
- Nhiều phiên bản ngôn ngữ: một file video với track subtitle tiếng Việt và tiếng Anh dễ quản lý hơn nhiều so với mã hóa lại video hai lần.
- Giao hàng cho nền tảng streaming: Netflix, Amazon và Apple TV+ yêu cầu file subtitle riêng, không phải video đã burn-in. Xem dịch vụ subtitle dọc của tôi cho cả workflow burn-in creator lẫn sidecar delivery cho streaming.
- Vòng review với khách hàng: tôi giao file .ass kèm video để khách hàng sửa nội dung mà không cần mã hóa lại.
- Tuân thủ WCAG: WCAG 1.2.2 (video theo yêu cầu) yêu cầu caption có thể tắt và thay đổi kiểu chữ. Subtitle burn-in không thể tắt được, nên cần soft track cho website cần tuân thủ accessibility chính thức.
Khi dự án có dịch subtitle sang tiếng Việt, tôi xây dựng timing từ ngôn ngữ gốc, dịch trong chế độ xem song ngữ của Subtitle Edit, kiểm tra CPS theo giới hạn tiếng Việt, rồi giao cả track .srt mềm lẫn bản burn-in theo yêu cầu.
Câu hỏi thường gặp
Có thể dùng font khác Arial cho subtitle .ass video dọc không?
Được. Đặt Fontname thành tên bất kỳ font nào đã cài trên máy chạy ffmpeg. Với video dọc, tôi thường dùng Noto Sans (xử lý tốt ký tự Latin và tiếng Việt) hoặc sans-serif đậm. Font phải được cài system-wide; ffmpeg không thể nhúng file font vào video burn-in. Nếu font bị thiếu, libass tự thay thế bằng font khác mà không báo lỗi, kết quả trông sẽ khác.
WrapStyle: 0 trong [Script Info] làm gì?
WrapStyle: 0 yêu cầu libass ngắt dòng tại ranh giới từ (smart wrap). Đây là mặc định của ASS và gần như lúc nào cũng phù hợp. Với video dọc, tôi cố giữ mỗi sự kiện subtitle đủ ngắn (5-7 từ) để ít khi cần ngắt dòng, nhưng WrapStyle: 0 đảm bảo nó ngắt đúng nếu xảy ra. Đặt rõ ràng tránh phụ thuộc vào mặc định của renderer có thể khác nhau.
Làm thế nào để đẩy một cue lên đầu frame mà không thay dòng Style?
Thêm thẻ override inline {\an8} vào đầu trường Text của dòng Dialogue đó: Dialogue: 0,0:00:05.00,0:00:07.00,Default,,0,0,0,,{\an8}Dòng subtitle ở đầu. Thẻ \an8 (alignment numpad 8 = trên giữa) ghi đè Alignment trong dòng Style chỉ cho cue đó. Tất cả cue khác vẫn ở vị trí mặc định (Alignment=2, dưới giữa).
Tại sao subtitle vẫn hiển thị quá nhỏ dù đã đặt PlayRes đúng?
Nguyên nhân phổ biến nhất: đã đặt PlayRes đúng nhưng quên tăng Fontsize tương ứng với canvas mới. Một Fontsize=52 viết cho PlayResY=1080 sẽ trông đúng ở canvas 1080px nhưng nhỏ thấy rõ khi bạn chuyển sang PlayResY=1920 mà không cập nhật giá trị. Với canvas 1920px, bắt đầu từ Fontsize=68 đến 80 và xác nhận bằng ffplay trước khi encode đầy đủ.
File .ass có thể dùng chung cho cả render ngang và dọc không?
Không đáng tin cậy. PlayRes, Fontsize và MarginV đều khác nhau giữa hai chiều, và các giá trị phải khớp với canvas đầu ra. Workflow gọn nhất: duy trì hai file template riêng (một cho PlayResX:1920/PlayResY:1080 ngang, một cho PlayResX:1080/PlayResY:1920 dọc). Trong Subtitle Edit, bạn có thể giữ một file timing và xuất hai lần với style header khác nhau cho mỗi lần xuất.
Nguồn
- FFmpeg Documentation: bộ lọc ass - render subtitle .ass/SSA bằng libass trong ffmpeg. Kiểm tra tháng 9/2026.
- FFmpeg Documentation: bộ lọc subtitles - tùy chọn force_style và render libass tổng quát. Kiểm tra tháng 9/2026.
- Subtitle Edit (nikse.dk) - phần mềm miễn phí để soạn thảo .ass, chỉnh subtitle song ngữ và kiểm tra CPS. Kiểm tra tháng 9/2026.
Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →
