Cách nhúng phụ đề mềm vào MP4 và MKV bằng ffmpeg
← Blog
🎬 Phụ đề & Caption7 phút đọc

Cách nhúng phụ đề mềm vào MP4 và MKV bằng ffmpeg

💡 Để nhúng phụ đề mềm vào MP4, dùng -c:s mov_text: đây là codec phụ đề văn bản duy nhất MP4 chấp nhận. Với MKV, dùng -c:s srt hoặc -c:s ass. Gán mã ISO 639-2 qua -metadata:s:s:0 language=vie cho tiếng Việt và thêm -disposition:s:0 default để player tự bật track này. Video và âm thanh được sao chép nguyên vẹn.

Ý chính

  • MP4 yêu cầu -c:s mov_text cho track phụ đề văn bản; codec khác sẽ lỗi hoặc tạo stream không đọc được.
  • MKV chấp nhận -c:s srt (timing thuần) hoặc -c:s ass (có style); cả hai đều mux sạch.
  • Mã ISO 639-2 cho tiếng Việt là vie; gán bằng -metadata:s:s:0 language=vie.
  • Đánh dấu track mặc định bằng -disposition:s:0 default; xóa cờ trên track khác bằng -disposition:s:1 0.
  • Dùng -c:v copy -c:a copy để chỉ stream phụ đề được chuyển đổi, video và âm thanh giữ nguyên.

Phụ đề mềm và phụ đề gắn cứng: thực sự khác nhau như thế nào?

Phụ đề gắn cứng (burn-in) được vẽ trực tiếp vào pixel của video và không thể tắt. Mọi frame video đều phải mã hóa lại, tốn thời gian và giảm chất lượng nếu không dùng bitrate cao.

Phụ đề mềm nằm trong một stream riêng bên trong container. Player render chúng khi phát, nên người xem có thể bật/tắt, chuyển ngôn ngữ hoặc thay đổi kích thước. Luồng video không bị động đến: không mất chất lượng, và việc mux chỉ mất vài giây kể cả với file dài vì chỉ đóng gói stream chứ không transcode.

Tôi chỉ dùng burn-in khi nền tảng không cho tôi lựa chọn nào khác, thường là export video dọc ngắn với file .ass được style sẵn. Còn lại, phụ đề mềm sạch hơn và tái sử dụng được nhiều hơn. Một track mềm trong MP4 hoặc MKV kèm file .srt sidecar riêng là đủ cho hầu hết yêu cầu giao hàng.

Lệnh ffmpeg cơ bản cho MP4

Đây là lệnh tôi dùng để nhúng file SRT tiếng Việt thành track phụ đề mềm vào một MP4 có sẵn:

ffmpeg -i video.mp4 -i vi.srt \
  -map 0 -map 1 \
  -c:v copy -c:a copy \
  -c:s mov_text \
  -metadata:s:s:0 language=vie \
  -metadata:s:s:0 title="Vietnamese" \
  -disposition:s:0 default \
  output.mp4

Ý nghĩa từng tham số:

  • -map 0: đưa tất cả stream từ input đầu tiên (video, âm thanh, mọi track hiện có) vào output.
  • -map 1: đưa stream đầu tiên của input thứ hai (file .srt) vào output.
  • -c:v copy -c:a copy: sao chép video và âm thanh mà không mã hóa lại.
  • -c:s mov_text: mã hóa phụ đề thành MOV text, định dạng 3GPP timed text mà MP4 yêu cầu.
  • -metadata:s:s:0 language=vie: gán mã ISO 639-2 tiếng Việt cho stream phụ đề đầu tiên. Bộ chỉ định s:s:0 có nghĩa: loại stream subtitle, index 0.
  • -metadata:s:s:0 title="Vietnamese": nhãn track hiển thị trong menu player.
  • -disposition:s:0 default: đánh dấu track này là mặc định cho player tự chọn.

Tại sao MP4 yêu cầu mov_text?

MP4 được xây dựng trên ISO Base Media File Format (ISOBMFF). Với timed text track, spec định nghĩa định dạng 3GPP Timed Text mà ffmpeg hiển thị là codec mov_text. Đây là loại phụ đề văn bản duy nhất được định nghĩa cho nhóm container MP4 và MOV.

Nếu dùng -c:s srt hoặc -c:s copy với nguồn SRT vào output MP4, ffmpeg sẽ báo lỗi hoặc ghi một track mà player âm thầm bỏ qua. Container không có slot cho dữ liệu SubRip thô. Tương tự với ASS/SSA: không dùng -c:s ass trong output MP4.

Giới hạn thực tế của mov_text: nó hỗ trợ style nội dòng cơ bản (đậm, nghiêng, màu sắc) nhưng không có định vị, lề và animation của ASS. Khi cần định vị phụ đề phức tạp, hãy burn-in với -vf ass=styled.ass và tùy chọn mux thêm một track mềm plain text riêng cho mục đích truy cập.

Nhúng phụ đề vào MKV và thêm nhiều track ngôn ngữ

Matroska (MKV) linh hoạt hơn. Nó lưu trữ gốc SubRip (srt), ASS/SSA (ass), WebVTT và nhiều định dạng khác. Có thể mux SRT mà không cần chuyển đổi, hoặc giữ nguyên toàn bộ ASS style sheet:

# MKV: nhúng track SRT tiếng Việt
ffmpeg -i video.mkv -i vi.srt \
  -map 0 -map 1 \
  -c:v copy -c:a copy \
  -c:s srt \
  -metadata:s:s:0 language=vie \
  -metadata:s:s:0 title="Vietnamese" \
  -disposition:s:0 default \
  output.mkv

# MKV: nhúng subtitle ASS có style (giữ nguyên toàn bộ dữ liệu style)
ffmpeg -i video.mkv -i vi.ass \
  -map 0 -map 1 \
  -c:v copy -c:a copy \
  -c:s ass \
  -metadata:s:s:0 language=vie \
  output.mkv

Để có hai track ngôn ngữ trong một file (tiếng Anh mặc định, tiếng Việt thứ hai):

ffmpeg -i video.mp4 -i en.srt -i vi.srt \
  -map 0 -map 1 -map 2 \
  -c:v copy -c:a copy \
  -c:s mov_text \
  -metadata:s:s:0 language=eng \
  -metadata:s:s:0 title="English" \
  -metadata:s:s:1 language=vie \
  -metadata:s:s:1 title="Vietnamese" \
  -disposition:s:0 default \
  -disposition:s:1 0 \
  output.mp4
  • -map 2: thêm input thứ ba (file .srt thứ hai) thành stream mới.
  • -metadata:s:s:1: nhắm vào stream phụ đề thứ hai (index 1).
  • -disposition:s:1 0: xóa cờ default trên track tiếng Việt.

Kiểm tra kết quả bằng ffprobe:

ffprobe -v error \
  -select_streams s \
  -show_entries stream=index,codec_name:stream_tags=language,title \
  -of default=noprint_wrappers=1 \
  output.mp4

Output sạch của file hai track trông như sau:

index=2
codec_name=mov_text
TAG:language=eng
TAG:title=English

index=3
codec_name=mov_text
TAG:language=vie
TAG:title=Vietnamese

Nếu thấy language=und, index trong -metadata:s:s: bị sai. Nếu codec_name không phải mov_text trong MP4, hầu hết player sẽ âm thầm bỏ qua track đó.

Khi nào nên thuê dịch thuật phụ đề chuyên nghiệp?

Các lệnh ffmpeg trên xử lý tốt phần container. Điều chúng không thể làm là dịch văn bản. Quy trình của tôi khi client cần video tiếng Anh có track phụ đề tiếng Việt: dịch trong Subtitle Edit với cảnh báo CPS hiển thị trực tiếp, xuất file .srt hoàn chỉnh, rồi nhúng bằng lệnh trên.

Bẫy thường gặp: các file .srt dịch tự động được nhúng mà không kiểm tra CPS hay ngắt dòng. Container mux sạch, nhưng phụ đề chạy quá nhanh vì bản dịch làm dài thêm nội dung. Tiếng Việt thường dài hơn tiếng Anh gốc, vì vậy kiểm tra tốc độ đọc trước khi mux sẽ tránh được một vòng sửa lại.

Khi cần track phụ đề tiếng Việt chính xác và được định thời gian chuyên nghiệp, hoặc giao hàng cho streamer có spec Timed Text Style Guide, đó là lúc một người dịch bản địa thực sự xứng đáng với chi phí. Tôi trình bày toàn bộ quy trình dịch và QC trên trang dịch thuật phụ đề.

Câu hỏi thường gặp

Có thể nhúng WebVTT vào MP4 như track phụ đề mềm không?

Không. MP4 không hỗ trợ WebVTT như track subtitle trong container. WebVTT được cung cấp kèm video dưới dạng file sidecar, tải qua thẻ HTML <track> hoặc HLS playlist. Để có track mềm độc lập trong MP4, hãy chuyển VTT sang SRT trước bằng ffmpeg -i in.vtt out.srt, rồi mux với -c:s mov_text.

Sự khác nhau giữa -c:s copy và -c:s srt là gì?

Với -c:s copy, ffmpeg truyền qua các byte phụ đề nguyên vẹn mà không transcode. Cách này hoạt động khi codec nguồn đã tương thích với container đầu ra, ví dụ nguồn SRT vào output MKV. Với output MP4, copy sẽ thất bại vì MP4 cần mov_text, không phải dữ liệu SubRip thô. Luôn chỉ định rõ -c:s mov_text cho mọi output MP4.

Tại sao player hiển thị "und" thay vì tên ngôn ngữ?

Language tag dùng mã ISO 639-2 ba chữ cái. Với tiếng Việt, giá trị đúng là vie, không phải vi hay Vietnamese. Một số bản ffmpeg chấp nhận vi và chuẩn hóa ngầm; số khác ghi nguyên vi mà nhiều player không nhận ra. Tôi luôn dùng mã ba chữ cái: vie tiếng Việt, eng tiếng Anh, fra tiếng Pháp.

Có thể thêm track phụ đề mềm mà không encode lại video không?

Có. Với -c:v copy -c:a copy, video và âm thanh được truyền qua nguyên byte. Chỉ phụ đề được chuyển đổi (từ SRT sang mov_text cho MP4). Thao tác này mất vài giây kể cả với file dài và không làm giảm chất lượng video. File output sẽ lớn hơn một chút vì chứa thêm stream phụ đề.

Văn bản phụ đề bị lỗi trong MP4. Nguyên nhân là gì?

Nguyên nhân phổ biến nhất là mã hóa ký tự. File SRT nguồn phải là UTF-8. Nếu lưu bằng Windows-1252 hoặc mã hóa cũ khác, dấu tiếng Việt và ký tự đặc biệt sẽ bị lỗi sau khi mã hóa mov_text. Mở file trong Subtitle Edit, kiểm tra File > Character encoding, lưu lại dưới dạng UTF-8 không có BOM, rồi chạy lại ffmpeg. Nguyên nhân thứ hai là dùng -c:s copy với nguồn SRT vào output MP4, ghi dữ liệu SubRip thô vào slot mov_text và tạo ra văn bản bị lỗi.

Nguồn

Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →

Báo giáWhatsApp