Giải mã là quá trình chuyển đổi luồng phương tiện nén sang định dạng không nén phù hợp để xuất ra màn hình và loa. Trong các thiết bị di động, giải mã được thực hiện qua phần mềm thông qua CPU hoặc qua phần cứng thông qua các khối GPU và DSP chuyên dụng. Theo MDN Web Docs (2026), các codec hiện đại nén luồng từ 100–500 lần và giải mã khôi phục chất lượng gốc không mất mát khi chọn đúng cấu hình nén.
Những điểm chính
Giải mã là quá trình chuyển đổi dữ liệu số nén trở lại định dạng không nén ban đầu. Trong bối cảnh phương tiện, giải mã khôi phục các khung hình video từ luồng bit nén được tạo bởi bộ mã hóa. Nếu không có giải mã, người dùng không thể xem video hoặc nghe âm thanh vì tất cả các định dạng phương tiện hiện đại đều sử dụng nén để tiết kiệm băng thông và dung lượng đĩa.
Một luồng video điển hình ở định dạng H.264 với tốc độ bit 5 Mbps chiếm dung lượng ít hơn 100 lần so với luồng RGB không nén có cùng độ phân giải. Thuật toán giải mã phải khôi phục từng khung hình về độ phân giải và không gian màu gốc, tuân theo thông số kỹ thuật của codec theo thứ tự ngược lại so với mã hóa. Để làm điều này, bộ giải mã xử lý dữ liệu nội khung (I-frame) và liên khung (P-frame, B-frame).
Trên thiết bị di động, giải mã có thể diễn ra trên CPU hoặc trên các khối phần cứng chuyên dụng. Các SoC hiện đại từ Apple (dòng A), Qualcomm (Snapdragon) và MediaTek (Dimensity) đều có bộ giải mã tích hợp cho mọi định dạng phổ biến. Bộ xử lý video đảm nhận công việc nặng về biến đổi cosin rời rạc ngược và bù chuyển động, giải phóng CPU cho các tác vụ khác.
Quá trình giải mã bao gồm nhiều giai đoạn tuần tự đảo ngược các bước mã hóa. Đầu tiên, các tiêu đề và tham số nén — cấu hình, mức, độ phân giải, không gian màu — được trích xuất từ luồng bit. Sau đó, bộ giải mã xử lý tuần tự các macroblock nén, áp dụng các biến đổi ngược cho chúng.
Giai đoạn đầu tiên là trích xuất mã entropy. Giải mã entropy sử dụng thuật toán CABAC hoặc CAVLC để khôi phục các hệ số biến đổi cosin rời rạc. Giai đoạn này không phụ thuộc vào độ phân giải video — nó xử lý luồng bit chứ không phải pixel, và độ phức tạp được xác định bởi tốc độ bit, không phải kích thước khung hình.
Giai đoạn thứ hai là lượng tử hóa ngược và DCT ngược. Bộ giải mã nhân các hệ số đã lượng tử hóa với bước lượng tử, khôi phục giá trị hệ số DCT gần đúng, sau đó áp dụng biến đổi DCT ngược. DCT ngược khôi phục dữ liệu không gian từ miền tần số, tạo thành một macroblock pixel. Biến đổi được thực hiện độc lập cho chroma và luma.
Giai đoạn thứ ba là bù chuyển động. Đối với khung P và B, bộ giải mã sử dụng vector chuyển động được trích xuất từ luồng bit và tham chiếu đến các khung tham chiếu đã được giải mã trước đó. Bù chuyển động tạo ra một bộ dự đoán cho macroblock hiện tại, được cộng với tín hiệu dư sau DCT ngược. Kết quả là một khung hình được khôi phục hoàn toàn sẵn sàng để xuất ra.
// Mã giả cơ bản của giải mã khung hình video
struct DecodedFrame {
uint8_t* y_plane;
uint8_t* u_plane;
uint8_t* v_plane;
int width, height;
};
class Decoder {
public:
bool decodeNALUnit(const uint8_t* nalUnit, size_t size) {
if (!parseNALUHeader(nalUnit, size))
return false;
int sliceType = parseSliceType(nalUnit);
entropyDecode(nalUnit);
inverseQuantize();
inverseDCT();
if (sliceType != I_SLICE)
motionCompensation();
return true;
}
};
Ví dụ trên cho thấy cấu trúc cơ bản của bộ giải mã H.264. Hàm decodeNALUnit nhận một đơn vị NAL — khối cơ bản của luồng H.264 nén. Bộ giải mã phân tích tuần tự tiêu đề, trích xuất loại slice, áp dụng giải mã entropy, lượng tử hóa ngược và DCT ngược. Đối với slice P và B, bù chuyển động được thực hiện thêm bằng cách sử dụng các khung tham chiếu từ bộ đệm DPB.
Các codec video hiện đại khác nhau về thuật toán nén, hiệu quả và yêu cầu tài nguyên tính toán. Lựa chọn định dạng ảnh hưởng trực tiếp đến kích thước tệp, chất lượng hình ảnh và mức tiêu thụ điện năng khi giải mã trên thiết bị di động.
| Codec | Năm | Nén | Hỗ trợ phần cứng |
|---|---|---|---|
| H.264 | 2003 | 1:100 | Tất cả SoC hiện đại |
| H.265 | 2013 | 1:200 | Apple A8+, Snapdragon 805+ |
| VP9 | 2013 | 1:180 | Snapdragon 820+, Exynos |
| AV1 | 2018 | 1:300 | Apple A17+, Snapdragon 8 Gen 2+ |
H.264 là codec video phổ biến nhất, được hỗ trợ bởi tất cả thiết bị di động. Ưu điểm chính của nó là tính phổ quát: mọi điện thoại thông minh Android và iPhone đều có thể giải mã H.264 bằng phần cứng. Tuy nhiên, ở cùng tốc độ bit, H.264 kém chất lượng hơn so với các codec hiện đại như H.265 và AV1, yêu cầu tốc độ bit cao hơn 30–50% để có chất lượng hình ảnh tương tự.
H.265 cung cấp khả năng nén gấp đôi so với H.264 ở cùng chất lượng. Giải mã H.265 yêu cầu khối phần cứng mạnh hơn: VideoToolbox trên iOS hỗ trợ H.265 từ iPhone 6 (A8), và thiết bị Android từ Snapdragon 805 trở lên. Khi chọn H.265 cho ứng dụng di động, cần lưu ý rằng thiết bị cũ có thể không có hỗ trợ phần cứng và sẽ giải mã định dạng này bằng phần mềm, làm tăng mạnh mức tiêu thụ điện năng.
AV1 là codec mở từ Alliance for Open Media, cung cấp khả năng nén tốt nhất trong số tất cả các định dạng hiện đại. AV1 hiệu quả hơn 30% so với H.265 và 50% so với H.264 ở cùng chất lượng hình ảnh. Giải mã AV1 bằng phần cứng chỉ xuất hiện trong các SoC từ năm 2023+: Apple A17 Pro, Qualcomm Snapdragon 8 Gen 2 và mới hơn. Đối với thiết bị cũ, giải mã AV1 chỉ khả thi bằng phần mềm qua thư viện dav1d, tạo ra tải đáng kể lên CPU.
Lựa chọn giữa giải mã phần mềm và phần cứng là quyết định kiến trúc quan trọng khi phát triển trình phát phương tiện di động. Mỗi cách tiếp cận đều có ưu điểm và hạn chế cần được xem xét khi thiết kế ứng dụng.
Giải mã phần cứng được thực hiện trên các khối xử lý video chuyên dụng tiêu thụ ít năng lượng hơn đáng kể so với CPU khi thực hiện cùng một tác vụ. Theo Qualcomm, bộ giải mã H.265 phần cứng tiêu thụ năng lượng ít hơn 5–10 lần so với giải mã phần mềm trên CPU Snapdragon 8 Gen 1 khi phát video 4K. Điều này rất quan trọng đối với thiết bị di động, nơi mỗi miliwatt đều ảnh hưởng đến thời lượng pin.
Mặt khác, giải mã phần mềm mang lại sự linh hoạt tối đa. FFmpeg với thư viện libavcodec hỗ trợ hàng chục codec và container, bao gồm các định dạng hiếm và lỗi thời không có hỗ trợ phần cứng. Nhà phát triển có thể sửa đổi quy trình giải mã, thêm xử lý hậu kỳ và bộ lọc ngay lập tức, điều không thể khi sử dụng các khối phần cứng đóng.
Giải mã phần mềm phù hợp trong một số tình huống: khi phát các định dạng hiếm (ProRes, DNxHD, Motion JPEG), khi cần kiểm soát chính xác từng giai đoạn xử lý khung hình và khi giải mã AV1 trên thiết bị không có hỗ trợ phần cứng. libavcodec từ FFmpeg có thể giải mã hầu như mọi định dạng đã biết, khiến nó trở thành tiêu chuẩn thực tế cho các trình phát phương tiện đa năng.
Hạn chế của giải mã phần mềm là tản nhiệt. Giải mã video 4K liên tục trên CPU có thể làm nóng thiết bị lên 45–50 độ trong vòng 10–15 phút, dẫn đến giảm tốc và giảm tốc độ khung hình. Trên thiết bị không có làm mát chủ động (máy tính bảng, điện thoại), điều này đặc biệt đáng chú ý. Mức tiêu thụ điện năng CPU khi giải mã phần mềm có thể đạt 3–5 W so với 0,3–0,5 W khi giải mã phần cứng cùng một luồng.
Giải mã phần cứng là lựa chọn mặc định cho bất kỳ trình phát phương tiện sản xuất nào. Nó cung cấp 60 khung hình/giây ổn định cho video 4K với mức tiêu thụ điện năng tối thiểu. VideoToolbox trên iOS và MediaCodec trên Android cung cấp API gốc cho giải mã phần cứng tự động chọn khối xử lý tối ưu tùy theo codec và độ phân giải.
API nền tảng quản lý bộ đệm khung hình (surface pool trên Android, CVPixelBufferPool trên iOS), đồng bộ hóa màn hình và tối ưu hóa bộ nhớ. Nhà phát triển chỉ cần mở bộ giải mã với các tham số yêu cầu và nhận khung hình đã sẵn sàng. Giải mã phần cứng hỗ trợ quy trình đầu cuối với độ trễ tối thiểu: từ khi nhận luồng bit đến hiển thị trên màn hình mất 5–15 ms so với 30–80 ms ở giải mã phần mềm.
Hãy xem xét triển khai thực tế của giải mã trên cả hai nền tảng di động. Trên iOS, giải mã phần cứng được thực hiện qua VideoToolbox và giải mã phần mềm qua FFmpeg. Trên Android, MediaCodec được sử dụng cho giải mã phần cứng.
@interface VideoDecoder ()
@property (nonatomic) VTDecompressionSessionRef session;
@end
@implementation VideoDecoder
- (void)setupDecoder {
CMVideoFormatDescriptionRef formatDesc;
CMVideoCodecType codecType = kCMVideoCodecType_H264;
OSStatus status = CMVideoFormatDescriptionCreate(
NULL, codecType, 1920, 1080, NULL, &formatDesc
);
VTDecompressionOutputCallbackRecord callback;
callback.decompressionOutputCallback = &decodingCallback;
VTDecompressionSessionCreate(NULL, formatDesc, NULL,
NULL, &callback, &_session);
}
- (void)decodeFrame: (uint8_t*)nalData length:(size_t)size {
CMBlockBufferRef blockBuffer;
CMBlockBufferCreateWithMemoryBlock(NULL, nalData,
size, NULL, NULL, 0, size, 0, &blockBuffer);
CMSampleBufferRef sampleBuffer;
CMSampleBufferCreate(NULL, blockBuffer, true, NULL,
NULL, NULL, 1, 0, NULL, 0, NULL, &sampleBuffer);
VTDecompressionSessionDecodeFrame(_session,
sampleBuffer, 0, NULL, 0);
}
@end
Mã này minh họa việc khởi tạo bộ giải mã H.264 phần cứng trên iOS. VTDecompressionSessionCreate tạo phiên giải mã và VTCreate gọi callback khi có khung hình sẵn sàng. Phiên tự động sử dụng khối phần cứng nếu có sẵn cho codec được chỉ định. Để lấy khung hình đã giải mã ở định dạng CVPixelBuffer, một callback được sử dụng để truyền từng khung hình sẵn sàng với độ trễ tối thiểu.
MediaCodec decoder = MediaCodec.createDecoderByType("video/avc");
MediaFormat format = MediaFormat.createVideoFormat(
"video/avc", 1920, 1080
);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
decoder.configure(format, surface, null, 0);
decoder.start();
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
int inputIndex = decoder.dequeueInputBuffer(10000);
if (inputIndex >= 0) {
ByteBuffer buffer = inputBuffers[inputIndex];
buffer.clear();
buffer.put(nalData);
decoder.queueInputBuffer(inputIndex, 0, nalData.length, pts, 0);
}
Trên Android, MediaCodec sử dụng Surface để xuất ra thay vì bộ đệm pixel, giảm thiểu việc sao chép dữ liệu giữa GPU và CPU. Bộ giải mã tự động chọn khối phần cứng (thành phần OMX) dựa trên loại codec. Đối với H.264, OMX.google.h264.decoder được sử dụng, có thể là phần cứng hoặc phần mềm tùy thuộc vào triển khai của nhà sản xuất.
Lựa chọn chiến lược giải mã phụ thuộc vào đối tượng mục tiêu của ứng dụng, định dạng được hỗ trợ và yêu cầu hiệu suất. Giải pháp tối ưu thường bao gồm cách tiếp cận kết hợp: giải mã phần cứng cho các định dạng chính (H.264, H.265) với dự phòng phần mềm cho codec hiếm.
Nếu ứng dụng hướng đến khả năng tương thích tối đa — hãy sử dụng H.264, được đảm bảo giải mã bằng phần cứng trên mọi thiết bị. Đối với dịch vụ phát trực tuyến video, H.265 với hỗ trợ phần cứng trên thiết bị sau năm 2016 là phù hợp. AV1 là lựa chọn cho các dịch vụ mà việc tiết kiệm băng thông quan trọng: YouTube, Netflix và các nền tảng lớn khác đang tích cực áp dụng AV1 để giảm chi phí CDN trong khi vẫn duy trì chất lượng.
Một tham số quan trọng là kích thước bộ đệm của bộ giải mã. Bộ giải mã phần cứng có bộ đệm cố định (thường 4–16 khung hình). Khi phát luồng có tốc độ bit cao, bộ đệm có thể bị tràn, dẫn đến mất khung hình. MediaCodec cung cấp phương thức getOutputFrameRate để xác định hiệu suất thực tế của bộ giải mã trên thiết bị cụ thể, trong khi VideoToolbox cho phép kiểm soát ưu tiên thời gian thực qua kVTDecodeFrame_EnableAsynchronousDecompression.
Giảm tốc nhiệt là một yếu tố khác. Ngay cả giải mã phần cứng cũng có thể làm nóng thiết bị khi phát video 4K HDR kéo dài. Nên theo dõi nhiệt độ qua ProcessInfo trên iOS và BatteryManager trên Android, giảm chất lượng hoặc độ phân giải luồng khi quá nhiệt. Điều này đặc biệt quan trọng đối với trò chơi và ứng dụng phát trực tuyến có phiên xem dài.
Câu hỏi thường gặp
Mã hóa chuyển đổi dữ liệu không nén thành định dạng nén, trong khi giải mã khôi phục dữ liệu gốc từ luồng nén. Hai quá trình này là ngược chiều nhau và sử dụng cùng thuật toán: DCT, lượng tử hóa, bù chuyển động. Bộ mã hóa thực hiện biến đổi thuận, bộ giải mã thực hiện biến đổi ngược.
Để tương thích tối đa — H.264, vì nó được giải mã phần cứng trên 100% thiết bị hiện đại. Để nén tốt hơn — H.265 hoặc AV1. Lựa chọn phụ thuộc vào đối tượng: nếu 80% người dùng có thiết bị từ 2021+, H.265 sẽ cung cấp chất lượng tốt hơn với tốc độ bit thấp hơn. AV1 phù hợp cho thiết bị cao cấp có hỗ trợ phần cứng từ 2023+.
Bộ giải mã phần cứng là một vi mạch chuyên dụng (ASIC) được thiết kế riêng cho giải mã. Không giống CPU thực hiện giải mã bằng các lệnh tuần tự, khối phần cứng xử lý macroblock song song. Mức tiêu thụ điện năng của bộ giải mã phần cứng thấp hơn 5–10 lần vì chip hoạt động ở tần số thấp hơn và không có các giai đoạn pipeline không cần thiết.
Cấu hình (profile) xác định bộ thuật toán nén được sử dụng bởi bộ mã hóa: Baseline, Main, High. Mức (level) đặt các tham số tối đa của luồng: độ phân giải, tốc độ bit, kích thước bộ đệm. Đối với thiết bị di động, cấu hình High và mức 4.1–5.2 được khuyến nghị — đủ cho video 1080p–4K với giải mã phần cứng.
Trên Android, sử dụng MediaCodecList để lấy danh sách codec khả dụng và kiểm tra codec nào là phần cứng. Trên iOS, kiểm tra hỗ trợ qua CMVideoFormatDescription với codec được chỉ định — nếu VTDecompressionSessionCreate thành công, codec được hỗ trợ. Đối với AV1 trên Android, kiểm tra sự tồn tại của codec OMX.google.aomc.decoder hoặc phiên bản phần cứng của nó.
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