Giới thiệu

Các bài viết sẽ nhằm vào bất cứ đề tài gì mà tôi có hứng thú muốn đề cập, không theo một chủ đề nhất định. Nguyên tắc viết không theo khuôn khổ và tự do thoải mái nhất có thể, nội dung bài viết cũng sẽ thay đổi liên tục theo thời gian nên sẽ rất khó theo dõi. Bên cạnh đó, độ dài của các bài viết rất bất định, nhiều khi dài lê thê và tràn lan.

Các quy tắc chung (Đọc kỹ trước khi quyết định đọc nội dung bài viết):
Điều 1. Nội dung bài viết có thể dựa hoàn toàn vào quan điểm cá nhân, do đó sẽ khác với quan điểm của bạn. Bạn luôn có quyền ngừng đọc hoặc không cần đọc. Tuy nhiên, một khi đã đọc, bạn không được phép tranh cãi với tác giả. (Bạn được quyền nói xấu tác giả, nếu thích). Tác giả không chịu bất cứ trách nhiệm nào nếu nội dung bài viết gây ảnh hưởng xấu đến sức khỏe hay tâm lý của bạn.
Điều 2. Nội dung bài viết có thể chứa rất nhiều "spoiler" nên khuyên bạn không nên đọc trước khi xem/chơi tựa phim/anime/video game... được nhắc đến trong bài viết. Tác giả không chịu trách nhiệm vì làm bạn mất hứng. Bên cạnh đó, vì sở thích mỗi người mỗi khác, những chi tiết mà tác giả chê khen có thể không giống cách đánh giá của bạn, nên đừng dùng nó làm cơ sở lựa chọn. Ngoài ra, xem lại điều 1.
Điều 3. Bài viết chỉ được phép tồn tại trên blog này. Bạn không được quyền sao chép toàn bộ hay một phần nội dung của bất cứ bài viết nào sang nơi khác (bạn được phép đăng link của bài viết đi nơi khác, nếu muốn). Tác giả phủ nhận hoàn toàn trách nhiệm liên quan đến nội dung bài viết nếu nó được phát tán nơi khác dưới hình thức không phải link, tuy nhiên vẫn giữ quyền đối với nó.

2011-12-23

Sơ nét về VFR và cách xử lý nội dung VFR khi dùng FFMS2

Một số bạn đọc sau khi quyết định chuyển sang dùng FFMS2 đã vướng phải một vấn đề mà họ không gặp phải khi dùng DSS trong MeGUI, đó là lệch sub/audio sau khi encode, mà nguyên nhân là vì source là VFR và người dùng không nắm được. Bài viết này nhằm giúp bạn đọc có khái niệm cơ bản về VFR và cách xử lý nó khi dùng FFMS2 trong Avisynth đối với video được encode sẵn, như Share-RAW chẳng hạn.

Sơ lược về VFR:
Trước khi đi vào cách xử lý, xin nói sơ qua về VFR. Nội dung dừng ở mức rất cơ bản, mục tiêu là để người đọc chưa biết gì về VFR có cái nhìn khái quát về nó. Nếu muốn tìm hiểu nâng cao, bạn nên tự tìm các tài liệu khác trên Net để tham khảo.

VFR là viết tắt của variable framerate, nghĩa là tốc độ khung hình (framerate) có thể thay đổi, hay nói cách khác thời gian hiển thị (duration) của các khung hình (frame) có thể không đồng nhất, ngược lại với CFR. Thật ra, thuật ngữ framerate chỉ mang tính hình tượng cho dễ hình dung, còn bản chất không đơn giản vậy. Thêm nữa, khái niệm framerate phù hợp khi mô tả nội dung CFR nhưng đối với VFR nó có thể dễ gây nhầm lẫn. Trên thực tế, đa số container format sử dụng cái gọi là timestamp để biểu hiện thời điểm hiển thị của từng frame. Toàn bộ timestamp của tất cả frame sẽ được lưu thành timecodes theo quy định nào đó trong container. Ví dụ một đoạn timecodes v2 của một tập tin MKV CFR 23.976fps như sau:
# timecode format v2
0
42
83
125
167
209
250
292
334
375
...
nghĩa là frame #0 sẽ xuất hiện ở thời điểm 0ms sau khi phát, frame #1 hiển thị vào thời điểm 42ms sau khi phát, cứ thế... (lẽ ra mỗi khoảng cách phải là ~41.708ms nhưng vì mặc định timestamp của Matroska chỉ chính xác đến ms nên phải làm tròn lên và do đó mà phải bù lại ở những frame tiếp theo).

Khoảng cách giữa các timestamp này chính là duration của từng frame tương ứng. Đối với nội dung CFR, duration của các frame là không đổi (hay đúng hơn là gần như không đổi). Ngược lại, với nội dung VFR, chúng có thể thay đổi với mức chênh lệch lớn, chẳng hạn một frame nào đó có thể có duration gấp vài lần các frame lân cận.

Avisynth (các phiên bản hiện tại) xem mọi nội dung mà nó xử lý đều là CFR, nó không có khái niệm về VFR, do đó khi gặp nội dung VFR nó sẽ suy diễn nội dung này thành CFR và cấp cho một framerate bằng cách lấy tổng số frame chia cho tổng thời gian dựa theo báo cáo của source filter. Vì lẽ đó, những frame có duration dài bị rút ngắn còn những frame có duration ngắn lại bị cho dài thêm, dẫn đến làm hình ảnh không còn ăn khớp với tiếng hoặc sub (đã được time sẵn).

Người dùng MeGUI chú ý: Những chỉ dẫn dưới đây nằm ở tab Script của Avisynth script creator.

Có hai hướng giải quyết vấn đề này:

1. Chuyển về CFR:
Đây là cách thức mặc định của MeGUI khi dùng DSS (bằng cách thêm convertfps=true), do đó mà người dùng không gặp hiện tượng lệch như khi dùng FFMS2. Bạn sẽ cần dùng tới hai tham số (parameter) fpsnum và fpsden như sau:
FFVideoSource("foo.mkv", fpsnum=X, fpsden=Y)
với X/Y là framerate mà bạn muốn chuyển về, ví dụ 30000/1001.

Nếu dùng hướng này, tốt nhất bạn nên chọn framerate tương ứng với framerate cao nhất của source. Vì cách thức chuyển VFR sang CFR của FFMS2 giống như cách hoạt động của ChangeFPS(), tức là đúp (duplicate) hoặc bỏ (drop) frames để đạt framerate đặt ra. Việc duplicate frames sẽ ít ảnh hưởng đến trải nghiệm xem hơn là việc drop frames. Nếu sử dụng encoder hiệu quả như x264 thì việc duplicate frames sẽ không làm tăng dung lượng đáng kể (vì các frame dup được cấp phát rất ít bit). Tuy nhiên, nhược điểm là làm tăng lượng frames nên nếu sử dụng filter-chain nặng thì sẽ làm tốn thêm nhiều thời gian xử lý, dẫn đến thời gian encode sẽ lâu hơn (đặc biệt khi dùng 2-pass).

Lưu ý: MeGUI (một số phiên bản) mặc định sẽ thêm .AssumeFPS(ZZZ) vào sau FFVideoSource(), hãy xóa bỏ nó hoặc sửa lại thành .AssumeFPS(X, Y)
Với hướng giải quyết này, khi muốn hardsub thì không phải làm thêm thao tác gì đặc biệt.

2. Giữ nguyên VFR:
Cách thực hiện có phần phức tạp hơn, theo các bước sau:
- Bước 1: Lấy timecodes của video nguồn: có nhiều cách nhưng vì đang nói đến FFMS2 nên cách đơn giản nhất là trong Avisynth script, ở phần mở source dùng như sau:
FFVideoSource("foo.mkv", timecodes="timecodes.txt")
Lưu ý: Tên "timecodes.txt" là không bắt buộc, bạn có thể chọn tên tập tin tùy ý nhưng tốt nhất là dùng đuôi .txt rồi thay "timecodes.txt" bằng đường dẫn đến tập tin đó, ví dụ: "D:\Video\foo-tc.txt", nhớ là đường dẫn luôn nằm trong cặp dấu ngoặc kép. (Một điều nữa, bạn phải mở và preview Avisynth script được tạo ra ở trên trong MeGUI, VirtualDub, AvsPmod hoặc MPC-HC để FFMS2 tạo ra tập tin "timecodes.txt").
- Bước 2: Thực hiện encode. Nếu dùng x264 thì nên thêm vào --tcfile-in "timecodes.txt" để tăng tính hiệu quả. "timecodes.txt" ở đây thay bằng đường dẫn nơi lưu tập tin timecodes nêu ở bước 1. (Khi dùng MeGUI, mở x264 configuration dialog, qua tab Misc, thêm vào --tcfile-in "timecodes.txt" ở mục Custom Command Line, vì hiện tại không có mục chọn tcfile-in). Tốt nhất nên chọn chế độ File format là RAWAVC.
- Bước 3: Sau khi encode, mux video kết quả cùng với timecodes.txt ở Bước 1, cách thực hiện tùy theo muxer. Ở đây tôi sẽ nêu sơ lược đối với mkvmerge GUI và L-SMASH Timeline Editor.
  • L-SMASH Timeline Editor cho trường hợp muốn dùng MP4 container:
    $ muxer -i encoded.264 -o encoded.mp4
    $ timelineeditor --timecode "timecodes.txt" encoded.mp4 output.mp4
  • mkvmerge GUI cho trường hợp muốn dùng MKV container:
Trong trường hợp muốn hardsub, có hai tùy chọn:
- Thêm parameter vfr chỉ đến nơi lưu tập tin timecodes ở Bước 1 cho TextSub, TextSubMod hoặc AssRender, ví dụ:
TextSub("bar.ass", vfr="timecodes.txt")
# thay TextSub bằng TextSubMod nếu dùng VSFilterMod
# hoặc AssRender nếu dùng AssRender
 - Dùng chức năng Transform Framerate của Aegisub. Tiện lợi duy nhất của tùy chọn này là không cần phải thêm vfr="timecodes.txt" cho TextSub/TextSubMod hoặc AssRender.

Việc khiến cho hướng giải quyết này trở nên khó khăn hơn cả là khi cần cắt xén (Trim) trong Avisynth, bởi lẽ khi đó bạn sẽ cần điều chỉnh lại timecodes cho phù hợp và việc này chẳng đơn giản. (Bạn có thể dùng Vfr.py để làm điều này, mặc dù vẫn không phải dễ đối với những ai không quen với CLI).

Tán dóc Nói thêm về VFR:
VFR xuất phát từ hai nguồn:
- Từ nhà sản xuất: Nội dung VFR dễ bắt gặp trên Anime DVD/Broadcast Hybrid, trong đó OP/ED hoặc một số cảnh hành động nhanh hay CGI có thể sử dụng 30p hoặc 60i trong khi nội dung chính là 24p hard-telecined (30i), riêng broadcast thì các đoạn quảng cáo thường là 60i (tất nhiên chúng thường bị bỏ đi). Đặc tả của Blu-ray có hỗ trợ nội dung VFR, tuy nhiên số BD được phát hành có nội dung VFR hầu như rất hiếm. Đặc điểm nổi bật của nguồn này là framerate khác nhau trên từng đoạn, mỗi đoạn thực chất là CFR. Tuy nhiên, nguồn này còn phụ thuộc vào người encode có muốn encode thành VFR hay chuyển về CFR.
- Từ người encode: Trường hợp 1 như nêu ở trên, encode nội dung hybrid thành VFR. Trường hợp 2 là sử dụng filter như DeDup để làm giảm lượng frame bằng cách loại bỏ những frame giống nhau và sửa lại timecodes cho phù hợp. Vì nó làm giảm số frame cần xử lý nên trên lý thuyết điều này làm giảm thời gian xử lý filtering, giảm thời gian encode và giảm dung lượng của video sau encode.
  • Đối với filtering: nếu cần phải dùng filter-chain nặng nề thì việc giảm lượng frame cần xử lý sẽ có thể có hiệu quả đáng kể.
  • Đối với thời gian encode: Encoder tốt như x264 sẽ nhanh chóng nhận ra frame dup và dùng rất ít thời gian để xử lý nó, trong khi đó việc Dedup cũng tốn thời gian gần tương tự hoặc hơn nên lý do này không thuyết phục.
  • Đối với việc giảm dung lượng: Nếu dùng encoder hiệu quả như x264 thì các frame dup sẽ được cấp phát rất ít bit và không làm dung lượng tổng tăng thêm đáng kể nên lý do này cũng không thuyết phục.