Transcoding là quá trình chuyển đổi tệp phương tiện kỹ thuật số từ định dạng nén này sang định dạng nén khác với giải mã hoàn toàn và mã hóa lại. Không giống như transmuxing (chỉ thay đổi vùng chứa), transcoding thay đổi codec, tốc độ bit, độ phân giải và các tham số khác của luồng nén. Theo Apple AVFoundation documentation (2026), transcoding được sử dụng để điều chỉnh nội dung cho các thiết bị và điều kiện mạng khác nhau.
Những Điểm Chính
Transcoding là quá trình chuyển đổi tệp phương tiện từ định dạng nén này sang định dạng nén khác bằng cách giải mã hoàn toàn luồng nguồn thành định dạng PCM trung gian không nén và sau đó mã hóa với các tham số mới. Nếu tệp nguồn sử dụng codec H.264 với tốc độ bit 10 Mbps và đầu ra cần H.265 với tốc độ bit 3 Mbps — đó là transcoding.
Transcoding khác với việc đóng gói lại đơn giản (transmuxing), khi chỉ có vùng chứa thay đổi (ví dụ: MP4 sang MKV) trong khi luồng bit nén vẫn không thay đổi. Transcoding liên quan đến các hoạt động tốn kém về mặt tính toán: giải mã từng khung hình, áp dụng bộ lọc (thay đổi kích thước, hiệu chỉnh màu sắc, cắt xén) và mã hóa lại với các tham số mới. Điều này làm cho transcoding trở thành một trong những hoạt động tốn nhiều tài nguyên nhất khi làm việc với phương tiện.
Transcoding được sử dụng trong nhiều tác vụ: điều chỉnh video theo giới hạn băng thông, chuyển đổi sang định dạng hỗ trợ giải mã phần cứng trên thiết bị đích, tạo nhiều phiên bản cho phát trực tuyến HLS/DASH, trích xuất các rãnh âm thanh vào tệp riêng. Các dịch vụ OTT (Netflix, YouTube, Twitch) transcoding mỗi tệp được tải lên thành hàng chục biến thể với tốc độ bit, độ phân giải và codec khác nhau để cung cấp phát trực tuyến thích ứng cho hàng triệu người dùng.
Quá trình transcoding bao gồm ba giai đoạn chính: giải mã, xử lý và mã hóa. Mỗi giai đoạn có thể được thực hiện trên CPU hoặc khối GPU/phần cứng tùy thuộc vào khả năng sẵn có và hiệu suất yêu cầu.
Giai đoạn đầu tiên là giải mã luồng nguồn. Tệp nguồn được đọc từ vùng chứa (MP4, MOV, MKV), sau đó các gói video nén được gửi đến bộ giải mã. Giải mã có thể dựa trên phần cứng (nếu codec được hỗ trợ) hoặc dựa trên phần mềm qua FFmpeg. Đầu ra của quá trình giải mã là các khung hình không nén ở định dạng YUV420 hoặc BGRA — đây là giai đoạn transcoding khác biệt so với việc ghép kênh lại đơn giản.
Giai đoạn thứ hai là lọc và xử lý. Các khung hình đã giải mã đi qua một chuỗi các bộ lọc: thay đổi kích thước đến độ phân giải mục tiêu, thay đổi tốc độ khung hình, hiệu chỉnh màu sắc, chồng văn bản hoặc đồ họa. Chuỗi bộ lọc FFmpeg được xây dựng như một đồ thị, nơi mỗi bộ lọc là một mô-đun xử lý riêng biệt. Ví dụ, bộ lọc scale=1280:720 thay đổi độ phân giải, fps=30 thay đổi tốc độ khung hình và yadif thực hiện khử xen kẽ. Tất cả các hoạt động được thực hiện trên các khung hình không nén, làm cho giai đoạn thứ hai trở nên tốn nhiều tài nguyên nhất.
Giai đoạn thứ ba là mã hóa vào định dạng mục tiêu. Các khung hình đã xử lý được đưa vào bộ mã hóa, bộ mã hóa sẽ nén chúng theo thuật toán codec mục tiêu. Bộ mã hóa có thể dựa trên phần cứng (VideoToolbox trên iOS, MediaCodec trên Android) hoặc dựa trên phần mềm (libx264, libx265). Các tham số mã hóa: CRF (Constant Rate Factor) cho chất lượng không đổi, tốc độ bit cho CBR/VBR, hồ sơ và mức độ để tương thích với các thiết bị mục tiêu.
// Sơ đồ đường ống transcoding trên FFmpeg
AVFormatContext *inputCtx, *outputCtx;
AVCodecContext *decoderCtx, *encoderCtx;
AVFrame *frame = av_frame_alloc();
AVPacket packet;
while (av_read_frame(inputCtx, &packet) >= 0) {
// 1. Giải mã
avcodec_send_packet(decoderCtx, &packet);
avcodec_receive_frame(decoderCtx, frame);
// 2. Lọc (thay đổi kích thước + thay đổi fps)
sws_scale(swsCtx, frame->data, frame->linesize,
0, frame->height, scaledFrame->data,
scaledFrame->linesize);
// 3. Mã hóa
avcodec_send_frame(encoderCtx, scaledFrame);
avcodec_receive_packet(encoderCtx, &outPacket);
av_interleaved_write_frame(outputCtx, &outPacket);
}
Đường ống trên minh họa chu trình transcoding cổ điển. Hàm av_read_frame đọc các gói nén từ tệp đầu vào, avcodec_send_packet giải mã chúng thành các khung hình, sws_scale thực hiện thay đổi kích thước và avcodec_send_frame mã hóa khung hình đã xử lý thành định dạng đầu ra. Chu trình ba giai đoạn này lặp lại cho mỗi khung hình hoặc nhóm khung hình (GOP), tùy thuộc vào cài đặt bộ mã hóa.
Sự khác biệt giữa transcoding và transmuxing là một trong những điểm nhầm lẫn phổ biến nhất trong kỹ thuật phương tiện. Hiểu được sự khác biệt này rất quan trọng để chọn chiến lược xử lý phương tiện phù hợp.
| Tham số | Transcoding | Transmuxing |
|---|---|---|
| Những gì thay đổi | Codec, tốc độ bit, độ phân giải | Vùng chứa, siêu dữ liệu |
| Tải tính toán | Cao (giải mã + mã hóa) | Tối thiểu (sao chép gói) |
| Chất lượng | Có thể giảm (mất thế hệ) | Không mất mát |
| Thời gian thực thi | Phút–giờ cho video dài | Giây–phút |
| Ứng dụng | Thích ứng định dạng, nén | Thay đổi vùng chứa để tương thích |
Transmuxing là đóng gói lại luồng nén vào một vùng chứa khác mà không giải mã và mã hóa lại. Nếu video đã được nén bằng codec H.265 trong vùng chứa MP4 và cần đặt vào vùng chứa MOV hoặc MKV — transmuxing chỉ đơn giản sao chép các gói luồng bit từ vùng chứa này sang vùng chứa khác. Chất lượng không bị ảnh hưởng, thời gian xử lý là tối thiểu vì không cần giải mã khung hình. FFmpeg thực hiện transmuxing với cờ -codec copy.
Transcoding, mặt khác, giải mã và nén lại hoàn toàn luồng phương tiện. Mỗi khi video trải qua transcoding, mất mát thế hệ (generation loss) có thể xảy ra — sự suy giảm chất lượng nhẹ do nén mất dữ liệu lặp đi lặp lại. Ngay cả ở cùng tốc độ bit, thế hệ transcoding thứ ba thường kém hơn thế hệ đầu tiên. Đây là lý do tại sao các chuyên gia khuyên nên lưu trữ các bản sao gốc ở định dạng không nén hoặc nén tối thiểu (ProRes, DNxHR) và chỉ transcoding các phiên bản cuối cùng để phân phối.
Việc chọn công cụ transcoding phụ thuộc vào nền tảng, yêu cầu hiệu suất và trường hợp sử dụng. Đối với phát triển di động, cả API gốc và thư viện đa nền tảng đều có sẵn.
FFmpeg là tiêu chuẩn thực tế cho transcoding trên tất cả các nền tảng. Dòng lệnh FFmpeg cho phép thực hiện hầu như bất kỳ chuyển đổi nào: thay đổi codec, điều chỉnh tốc độ bit, cắt, nối, áp dụng bộ lọc. Đối với ứng dụng di động, FFmpeg được tích hợp qua các thư viện libavformat, libavcodec và libavfilter. Ví dụ về lệnh transcoding điển hình: ffmpeg -i input.mp4 -c:v libx265 -crf 23 -c:a aac -b:a 128k output.mp4.
Trên iOS, transcoding được thực hiện qua AVAssetWriter và AVAssetReader. AVAssetReader giải mã tệp nguồn bằng cách đọc các khung hình không nén và AVAssetWriter mã hóa chúng thành định dạng mục tiêu. Cách tiếp cận này tự động sử dụng bộ mã hóa phần cứng VideoToolbox, đảm bảo hiệu suất tối đa. Trên Android, chức năng tương tự có sẵn qua MediaCodec kết hợp với MediaExtractor và MediaMuxer — MediaExtractor trích xuất các gói nén, MediaCodec giải mã và mã hóa, MediaMuxer ghi kết quả.
Đối với transcoding phía máy chủ trong môi trường sản xuất, các dịch vụ đám mây được sử dụng: AWS Elemental MediaConvert, Azure Media Services, Google Transcoder API. Các dịch vụ này tự động mở rộng quy mô khi tải, hỗ trợ tất cả các định dạng phổ biến và có thể transcoding một tệp đầu vào thành hàng chục biến thể đầu ra cho phát trực tuyến thích ứng (HLS, DASH). Đối với ứng dụng di động, transcoding đám mây là giải pháp tối ưu vì nó không tải thiết bị của người dùng và cho phép chuẩn bị nội dung không đồng bộ.
Hãy xem xét các ví dụ thực tế về transcoding trên các nền tảng di động sử dụng tăng tốc phần cứng và cấu hình các tham số chất lượng chính.
import AVFoundation
func transcodeVideo(sourceURL: URL, destURL: URL) {
let asset = AVAsset(url: sourceURL)
let preset = AVAssetExportPresetHEVCHighestQuality
AVAssetExportSession(asset: asset, presetName: preset)?
.exportAsynchronously {
switch assetExportSession?.status {
case .completed:
print("Transcoding hoàn tất")
case .failed:
print("Lỗi: " + assetExportSession.error.localizedDescription)
default:
break
}
}
// Transcoding thủ công với AVAssetReader + AVAssetWriter
let reader = try AVAssetReader(asset: asset)
let writer = try AVAssetWriter(url: destURL,
fileType: .mp4)
let outputSettings: [String: Any] = [
AVVideoCodecKey: AVVideoCodecType.hevc,
AVVideoWidthKey: 1920,
AVVideoHeightKey: 1080,
AVVideoCompressionPropertiesKey: [
AVVideoAverageBitRateKey: 4_000_000,
AVVideoProfileLevelKey: AVVideoProfileLevelH265Main10
]
]
let adaptor = AVAssetWriterInput(
mediaType: .video,
outputSettings: outputSettings
)
writer.add(adaptor)
}
Ví dụ cho thấy hai cách tiếp cận transcoding trên iOS. AVAssetExportSession là một cách đơn giản với các cài đặt trước chất lượng (HEVCHighestQuality cho H.265). Đường ống thủ công qua AVAssetReader + AVAssetWriter cung cấp toàn quyền kiểm soát các tham số: tốc độ bit, hồ sơ, mức độ. Tham số AVVideoProfileLevelH265Main10 kích hoạt hồ sơ HDR Main10 với độ sâu màu 10 bit, điều quan trọng đối với nội dung HDR hiện đại.
class Transcoder(private val context: Context) {
fun transcodeToHevc(inputUri: Uri, outputFile: File) {
val extractor = MediaExtractor()
extractor.setDataSource(context, inputUri, null)
val trackFormat = extractor.getTrackFormat(videoTrackIndex)
val mime = trackFormat.getString(MediaFormat.KEY_MIME)
val decoder = MediaCodec.createDecoderByType(mime!!)
val encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC)
val outputFormat = MediaFormat.createVideoFormat(
MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080
).apply {
setInteger(MediaFormat.KEY_BIT_RATE, 4_000_000)
setInteger(MediaFormat.KEY_FRAME_RATE, 30)
setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2)
}
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
encoder.start()
}
}
Mã Android tạo một đường ống gồm MediaExtractor — bộ giải mã MediaCodec — bộ mã hóa MediaCodec — MediaMuxer. MediaExtractor xác định loại codec từ tệp đầu vào và chọn bộ giải mã tương ứng. Bộ mã hóa được cấu hình cho H.265 (HEVC) với tốc độ bit 4 Mbps và khoảng cách khung hình chính 2 giây, tối ưu cho phát trực tuyến. Quan trọng: bộ mã hóa MediaCodec hoạt động đồng bộ, do đó để transcoding thời gian thực, cần triển khai một vòng lặp với xử lý dấu thời gian PTS chính xác cho mỗi khung hình.
Transcoding trên thiết bị di động là một nhiệm vụ đòi hỏi tối ưu hóa cẩn thận do tài nguyên CPU, GPU hạn chế và các ràng buộc về nhiệt. Một số chiến lược giúp thực hiện transcoding hiệu quả.
Yếu tố hiệu suất chính là bộ mã hóa phần cứng. Trên iOS, VideoToolbox cung cấp mã hóa phần cứng H.264 và H.265 với tốc độ nhanh gấp 5–10 lần so với libx264 phần mềm. Trên Android, MediaCodec sử dụng các thành phần OMX phần cứng nếu có. Bật mã hóa phần cứng giảm thời gian transcoding video 10 phút từ 30–40 phút (phần mềm) xuống 3–5 phút (phần cứng) trên thiết bị flagship.
Đối với transcoding di động, sự cân bằng giữa chất lượng, kích thước và thời gian xử lý là rất quan trọng. Đối với H.265 trên thiết bị di động, tốc độ bit 4–8 Mbps được khuyến nghị cho video 1080p ở 30 FPS. Chế độ CRF (Constant Rate Factor) trong libx265 cho phép đặt chất lượng trực tiếp, trong đó 23–28 cung cấp chất lượng hình ảnh tốt với kích thước tệp vừa phải. Đối với bộ mã hóa phần cứng, hãy sử dụng chế độ CBR với tốc độ bit mục tiêu vì CRF không được hỗ trợ trong phần cứng.
Transcoding liên tục trên thiết bị di động gây ra hiện tượng nóng đáng kể. Sau 5–7 phút mã hóa cường độ cao video 4K, nhiệt độ bộ xử lý có thể đạt 50–55 độ, sau đó hiện tượng điều tiết nhiệt sẽ kích hoạt. Giải pháp là transcoding có tạm dừng hoặc giảm tốc độ khung hình xuống 30 FPS. Nếu ứng dụng yêu cầu transcoding hàng loạt (ví dụ: trình chỉnh sửa video), tốt hơn nên xử lý theo từng đợt 2–3 phút với khoảng thời gian làm mát. Đối với các tình huống sản xuất, tối ưu nhất là chuyển transcoding sang phía máy chủ và sử dụng các dịch vụ đám mây.
Câu Hỏi Thường Gặp
Encoding là nén dữ liệu thô không nén vào một codec mục tiêu. Transcoding bao gồm cả giải mã và mã hóa: đầu tiên giải mã luồng nén hiện có, sau đó mã hóa lại. Encoding đơn giản nhận dữ liệu không nén làm đầu vào (ví dụ: từ máy ảnh), trong khi transcoding nhận tệp đã được nén.
Để tương thích tối đa — H.264. Để nén tốt hơn — H.265 (HEVC). Nếu thiết bị hỗ trợ mã hóa phần cứng H.265 (iPhone 8+, Android với Snapdragon 845+), nó cung cấp kích thước tệp chỉ bằng một nửa với cùng chất lượng. Mã hóa AV1 trên thiết bị di động vẫn còn quá chậm ngay cả với tăng tốc phần cứng.
Nói một cách chính xác, transcoding không mất dữ liệu là không thể khi chuyển đổi giữa các codec mất dữ liệu. Nếu cả hai codec đều mất dữ liệu, mỗi thế hệ transcoding sẽ làm giảm chất lượng. Transcoding không mất dữ liệu chỉ có thể thực hiện giữa các định dạng không mất dữ liệu (FFV1, H.264 Lossless) hoặc khi thay đổi vùng chứa mà không mã hóa lại (transmuxing).
Có, nếu sử dụng bộ giải mã và bộ mã hóa phần cứng và độ phân giải mục tiêu không vượt quá 1080p. Trên các thiết bị có VideoToolbox (iOS) hoặc MediaCodec (Android), transcoding thời gian thực H.264→H.265 có thể thực hiện với độ trễ 1–3 giây. Đối với 4K thời gian thực, cần có SoC mạnh như Apple A17 Pro, Snapdragon 8 Gen 2 trở lên.
Transcoding mất dữ liệu tích lũy các hiện vật nén. Nếu tệp nguồn đã bị nén nhiều (tốc độ bit 2–3 Mbps cho 1080p), việc nén lại sẽ nhân đôi mất mát. Khuyến nghị chỉ transcoding từ các bản sao gốc có tốc độ bit cao (20+ Mbps) và sử dụng CRF 18–23 để giảm thiểu mất mát.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm