Báo cáo vấn đề:
MFT phần cứng của Intel không tôn trọng cài đặt GOP, dẫn đến tiêu thụ băng thông nhiều hơn trong các ứng dụng thời gian thực. Mã tương tự hoạt động tốt trên MFT phần cứng Nvidia.
Lý lịch:
Tôi đang cố mã hóa các mẫu NV12 được ghi lại thông qua API DesktopD repeatation thành luồng video bằng bộ mã hóa phần cứng MediaFoundation H264 trên máy Windows10, truyền phát và kết xuất tương tự trong thời gian thực qua mạng LAN.
Ban đầu, tôi đã phải đối mặt với quá nhiều bộ đệm tại bộ mã hóa vì bộ mã hóa được đệm tối đa 25 khung hình (kích thước GOP) trước khi đưa ra một mẫu đầu ra. Sau một số nghiên cứu, tôi đã tìm ra rằng việc thiết lập CODECAPI_AVLowLatencyMode sẽ giảm độ trễ với chi phí của một chút chất lượng và băng thông.
Đặt loại thuộc tính CODECAPI_AVLowLatencyMode để cải thiện hiệu suất một chút, nhưng không theo yêu cầu thời gian thực. Có vẻ như bây giờ bộ mã hóa vẫn đệm tối thiểu 15 khung hình trước khi tạo ra các mẫu (Giới thiệu độ trễ khoảng 2 giây ở đầu ra). Và hành vi này chỉ đáng chú ý khi tốc độ khung hình thấp được cấu hình. Ở 60FPS, đầu ra gần như là thời gian thực mà không có độ trễ đáng chú ý.
Trên thực tế, bộ đệm chỉ có thể nhận thấy bằng mắt người khi tốc độ khung hình được đặt dưới 30FPS. Và, độ trễ tăng tỷ lệ nghịch với cấu hình FPS, ở mức 25FPS, độ trễ là trong vài trăm mili giây và tăng lên 3 giây khi FPS được cấu hình thành 10 (Tốc độ không đổi). Tôi đoán, cài đặt FPS hơn 30 (Say 60FPS) thực sự khiến bộ đệm mã hóa tràn đủ nhanh để tạo ra các mẫu với độ trễ không đáng chú ý.
Gần đây, tôi đã thử thuộc tính CODECAPI_AVEncCommonRealTime ( https://docs.microsoft.com/en-us/windows/win32/directshow/avenccommonrealtime-property ) để kiểm tra xem nó có cải thiện hiệu suất khi giảm tốc độ khung hình đầu vào không , nhưng cuộc gọi đó không thành công với lỗi "tham số không chính xác" .
Thí nghiệm của tôi:
Để duy trì tốc độ khung hình không đổi và cũng để buộc bộ mã hóa tạo ra các đầu ra thời gian thực, tôi đang cung cấp cùng một mẫu (mẫu đã lưu trước đó) cho bộ mã hóa với tốc độ không đổi 30FPS / 60FPS. Tôi đang làm điều này bằng cách chỉ chụp tối đa 10FPS (hoặc ở bất kỳ FPS nào được yêu cầu) và giả mạo 30 / 60FPS bằng cách cho ăn ba lần mẫu tương tự hoặc chính xác theo tỷ lệ dựa trên tỷ lệ EMULATED_FRAME_RATE / ACTUAL_FRAME_RATE (Ví dụ: 30/10, 60/15 , 60/20) để lấp đầy khoảng trống chính xác theo các khoảng không đổi. Ví dụ: khi không có thay đổi nào xảy ra trong 10 giây, tôi sẽ cho ăn bộ mã hóa với cùng một mẫu 30 * 10 lần (30FPS). Tôi đã học về cách tiếp cận này từ một số dự án Github mã nguồn mở, cũng từ các mẫu mã thử nghiệm của crom, tôi cũng được thông báo ( Chủ yếu trên SO, và cũng trên các diễn đàn khác) rằng đây là cách duy nhất để đẩy bộ mã hóa cho đầu ra thời gian thực, và không có cách nào xung quanh nó.
Cách tiếp cận được đề cập ở trên tạo ra đầu ra gần thời gian thực nhưng tiêu thụ nhiều dữ liệu hơn tôi mong đợi mặc dù tôi chỉ cung cấp mẫu đã lưu trước đó cho bộ mã hóa.
Tốc độ bit đầu ra dường như duy trì ở mức từ 350KBps đến 500KBps trên Intel MFT và thay đổi trong khoảng từ 80KBps đến 400KBps trên NVidia MFT (với cấu hình bitrate 30FPS và 500KB), bất kể nội dung màn hình thay đổi ở mức 30FPS hay 0FPS (không hoạt động). Bộ mã hóa phần cứng NVidia dường như có phần tốt hơn trong trường hợp này.
Trong thực tế, trong thời gian nhàn rỗi trên màn hình, bộ mã hóa đã tạo ra nhiều dữ liệu hơn mỗi giây so với tốc độ được đề cập ở trên. Tôi đã có thể cắt giảm mức tiêu thụ dữ liệu trên các thiết bị NVidia thông qua cài đặt kích thước GOP lớn hơn (kích thước GOP hiện tại được định cấu hình là 16K). Tuy nhiên, mức tiêu thụ dữ liệu thời gian nhàn rỗi trên màn hình vẫn ở mức khoảng 300KBps trên phần cứng đồ họa Intel 620 và 50KBps đến 80KBps trên NVidia GTX 1070 (cấu hình: tốc độ bit 500KB và 30FPS) không thể chấp nhận được. Tôi đoán, MFT phần cứng của Intel hoàn toàn không tôn trọng cài đặt GOP hoặc sự cải thiện là không đáng chú ý.
Tôi cũng có thể giảm mức tiêu thụ dữ liệu thời gian nhàn rỗi xuống ~ 130KBps và ~ 40KBps trên phần cứng của Intel và Nvidia bằng cách đặt bitrate rất thấp nhưng điều này vẫn không được chấp nhận, điều này cũng làm giảm chất lượng video.
Có cách nào để định cấu hình bộ mã hóa để tạo đầu ra dưới ~ 10KBps khi không có thay đổi nào xảy ra giữa các mẫu đầu vào không? Tôi thực sự đã nhắm đến đầu ra ~ 0KB khi không có thay đổi xảy ra nhưng ~ 10KBps có thể chấp nhận được.
Cập nhật:
Tôi có thể giảm mức tiêu thụ dữ liệu thời gian nhàn rỗi trên NVidia MFT bằng cách điều chỉnh một số tham số, xuống dưới ~ 20KBps với cấu hình bitrate 400KB và dưới ~ 10KBps với cấu hình bitrate 100KB . Điều này là thuyết phục. Nhưng cùng một mã với các cấu hình bộ mã hóa giống nhau tạo ra dữ liệu gấp 20 đến 40 lần trên các máy của Intel. Intel (Intel Graphics 620) chắc chắn không tôn vinh cài đặt GOP. Tôi thậm chí đã thử thay đổi GOP giữa 256 thành INT_MAX dường như không có gì thay đổi trên đầu ra MFT của phần cứng Intel.
Cập nhật 2:
Sau khi chơi xung quanh với các thuộc tính của bộ mã hóa (tôi chỉ định cấu hình CODECAPI_AVEncCommonRateControlMode với eAVEncCommonRateControlMode_UnconstrainedVBR thay vì eAVEncCommonRateControlMode_CBR), bây giờ tôi có thể thấy rằng chỉ có một lần duy nhất chỉ có một lần duy nhất. , sau đó nó trở lại cùng một câu chuyện. Tôi đoán sau vài giây, bộ mã hóa sẽ mất tham chiếu đến khung hình chính mà nó so sánh các mẫu và dường như nó không phục hồi sau thời điểm đó. Hành vi là như nhau cho dù GOP là 16/128/256/512/1024 hay INT_MAX.
Cấu hình bộ mã hóa:
Tham khảo: http://alax.info/blog/1586
const int EMULATED_FRAME_RATE = 30;//
const int TARGET_FPS = 10;
const int FPS_DENOMINATOR = 1;
const unsigned long long time_between_capture = 1000 / TARGET_FPS;
const unsigned long long nEmulatedWaitTime = 1000 / EMULATED_FRAME_RATE;
const unsigned long long TARGET_AVERAGE_BIT_RATE = 4000000; // Adjusting this affects the quality of the H264 bit stream.
const LONGLONG VIDEO_FRAME_DURATION = 10ll * 1000ll * 1000ll / ((long long)EMULATED_FRAME_RATE); // frame duration in 100ns units
const UINT32 KEY_FRAME_SPACING = 16384;
const UINT32 GOP_SIZE = 16384;
const UINT32 BPICTURECOUNT = 2;
VARIANT var = { 0 };
//no failure on both Nvidia & Intel, but Intel seems to be not behaving as expected
var.vt = VT_UI4;
var.lVal = GOP_SIZE;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncMPVGOPSize, &var), "Failed to set GOP size");
var.vt = VT_BOOL;
var.ulVal = VARIANT_TRUE;
// fails with "parameter incorrect" error.
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRealTime, &var), "Failed to set realtime mode");
var = { 0 };
var.vt = VT_BOOL;
var.ulVal = VARIANT_TRUE;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVLowLatencyMode, &var), "Failed to set low latency mode");
var = { 0 };
var.vt = VT_BOOL;
var.ulVal = VARIANT_TRUE;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonLowLatency, &var), "Failed to set low latency mode");
var = { 0 };
var.vt = VT_UI4;
var.lVal = 2; // setting B-picture count to 0 to avoid latency and buffering at both encoder and decoder
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncMPVDefaultBPictureCount, &var), "Failed to set B-Picture count");
var = { 0 };
var.vt = VT_UI4;
var.lVal = 100; //0 - 100 (100 for best quality, 0 for low delay)
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonQualityVsSpeed, &var), "Failed to set Quality-speed ratio");
var = { 0 };
var.vt = VT_UI4;
var.lVal = 20;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonQuality, &var), "Failed to set picture quality");
var = { 0 };
var.vt = VT_UI4;
var.lVal = eAVEncCommonRateControlMode_CBR; // This too fails on some hardware
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var), "Failed to set rate control");
var = { 0 };
var.vt = VT_UI4;
var.lVal = 4000000;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var), "Failed to set Adaptive mode");
var = { 0 };
var.vt = VT_UI4;
var.lVal = eAVEncAdaptiveMode_FrameRate;
CHECK_HR(mpCodecAPI->SetValue(&CODECAPI_AVEncAdaptiveMode, &var), "Failed to set Adaptive mode");
Tôi đã thử truy xuất phạm vi tham số được hỗ trợ cho kích thước GOP với mã sau đây, nhưng nó chỉ trả về lỗi E_NOTIMPL.
VARIANT ValueMin = { 0 };
VARIANT ValueMax = { 0 };
VARIANT SteppingDelt = { 0 };
HRESULT hr = S_OK;
if (!mpCodecAPI) {
CHECK_HR(_pTransform->QueryInterface(IID_PPV_ARGS(&mpCodecAPI)), "Failed to get codec api");
}
hr = mpCodecAPI->GetParameterRange(&CODECAPI_AVEncMPVGOPSize, &ValueMin, &ValueMax, &SteppingDelt);
CHECK_HR(hr, "Failed to get GOP range");
VariantClear(&ValueMin);
VariantClear(&ValueMax);
VariantClear(&SteppingDelt);
Tui bỏ lỡ điều gì vậy? Có bất kỳ tính chất nào khác mà tôi có thể thử nghiệm để có được hiệu suất thời gian thực trong khi tiêu thụ càng ít băng thông càng tốt khi không có thay đổi nội dung màn hình không?