├── D3D11Utils.cpp ├── D3D11Utils.h ├── D3D9Utils.cpp ├── D3D9Utils.h ├── LICENSE ├── MFAudioGlue.cpp ├── MFCompat.h ├── MFGlue.cpp ├── MFGlue.h ├── MFTAudioDecoder.cpp ├── MFTCodec.cpp ├── MFTCodec.h ├── MFTVideoDecoder.cpp ├── MFUUIDs.cpp ├── MFVideoGlue.cpp ├── MSUtils.cpp ├── MSUtils.h └── README.md /D3D11Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | */ 4 | #include "D3D11Utils.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "base/fmt.h" 11 | #pragma comment(lib, "dxguid.lib") // WKPDID_D3DDebugObjectName 12 | // gpu select: https://github.com/walbourn/directx-vs-templates/commit/e6406ee9afaf2719ff29b68c63c7adbcac57c02a 13 | using namespace std; 14 | 15 | namespace DXGI { 16 | 17 | // TODO: enum + reflection 18 | // https://gamedev.stackexchange.com/questions/31625/get-video-chipset-manufacturer-in-direct3d 19 | static const struct { 20 | UINT id; 21 | const char* name; 22 | } vendor[] = { 23 | {0x1414, "MicroSoft"}, 24 | {0x1002, "AMD"}, 25 | {0x1022, "AMD"}, 26 | {0x10DE, "NVIDIA"}, 27 | {0x1106, "VIA"}, 28 | {0x163C, "INTEL"}, 29 | {0x8086, "INTEL"}, 30 | {0x8087, "INTEL"}, 31 | {0x5333, "S3"}, 32 | {0x4D4F4351, "QUALCOMM"}, 33 | {0x344c5250, "Parallels"}, 34 | // "ParallelDesktop", "VMWare", "VirtualBox" 35 | }; 36 | 37 | const char* VendorName(UINT id) 38 | { 39 | for (const auto& v : vendor) { 40 | if (v.id == id) 41 | return v.name; 42 | } 43 | return "Unknown"; 44 | } 45 | 46 | static int VendorIndex(ComPtr dxgi, const char* vendor) 47 | { 48 | if (!dxgi || !vendor) 49 | return 0; 50 | ComPtr adapter; 51 | HRESULT hr = S_OK; 52 | for (int i = 0; hr != DXGI_ERROR_NOT_FOUND; ++i) { 53 | hr = dxgi->EnumAdapters(i, &adapter); // dxgi2? 54 | if (FAILED(hr) && hr != DXGI_ERROR_NOT_FOUND) 55 | return -1; 56 | DXGI_ADAPTER_DESC desc; 57 | MS_ENSURE(adapter->GetDesc(&desc), 0); 58 | string s(DXGI::VendorName(desc.VendorId)); 59 | transform(s.begin(), s.end(), s.begin(), [](char c){ return (char)tolower(c);}); 60 | if (s.find(vendor) != string::npos) 61 | return i; 62 | s.resize(256); 63 | snprintf(&s[0], s.size(), "%ls", desc.Description); 64 | transform(s.begin(), s.end(), s.begin(), [](char c){ return (char)tolower(c);}); 65 | if (s.find(vendor) != string::npos) 66 | return i; 67 | } 68 | return -1; 69 | } 70 | } // namespace DXGI 71 | 72 | namespace D3D11 { 73 | using namespace std; 74 | D3D_FEATURE_LEVEL to_feature_level(float value) 75 | { 76 | if (value < 9.1f) 77 | return D3D_FEATURE_LEVEL_12_1; 78 | if (value >= 12.1f) 79 | return D3D_FEATURE_LEVEL_12_1; 80 | else if (value >= 12.0f) 81 | return D3D_FEATURE_LEVEL_12_0; 82 | else if (value >= 11.1f) 83 | return D3D_FEATURE_LEVEL_11_1; 84 | else if (value >= 11.0f) 85 | return D3D_FEATURE_LEVEL_11_0; 86 | else if (value >= 10.1f) 87 | return D3D_FEATURE_LEVEL_10_1; 88 | else if (value >= 10.0f) 89 | return D3D_FEATURE_LEVEL_10_0; 90 | else if (value >= 9.3f) 91 | return D3D_FEATURE_LEVEL_9_3; 92 | else if (value >= 9.2f) 93 | return D3D_FEATURE_LEVEL_9_2; 94 | else if (value >= 9.1f) 95 | return D3D_FEATURE_LEVEL_9_1; 96 | return D3D_FEATURE_LEVEL_11_1; 97 | } 98 | 99 | D3D_FEATURE_LEVEL to_feature_level(const char* name) 100 | { 101 | float fl = 12.1f; // TODO: check os version. win10: 12.x, win8: 11.1 102 | if (name) 103 | fl = std::stof(name); 104 | return to_feature_level(fl); 105 | } 106 | 107 | static void debug_to_log(ID3D11InfoQueue* iq) 108 | { 109 | auto nb_msgs = iq->GetNumStoredMessages(); 110 | for (UINT64 i = 0; i < nb_msgs; ++i) { 111 | SIZE_T len = 0; 112 | MS_ENSURE(iq->GetMessage(0, NULL, &len)); 113 | D3D11_MESSAGE* msg = (D3D11_MESSAGE*)malloc(len); 114 | if (FAILED(iq->GetMessage(0, msg, &len))) { 115 | free(msg); 116 | return; 117 | } 118 | clog << fmt::to_string("ID %d(Severity %d): %.*s", msg->ID, msg->Severity, (int)msg->DescriptionByteLength, msg->pDescription) << endl; 119 | free(msg); 120 | } 121 | iq->ClearStoredMessages(); 122 | } 123 | 124 | void debug_report(ID3D11Device* dev, const char* prefix) 125 | { 126 | if (!dev) 127 | return; 128 | ComPtr dbg; 129 | if (FAILED(dev->QueryInterface(IID_PPV_ARGS(&dbg)))) 130 | return; 131 | ComPtr iq; 132 | if (FAILED(dev->QueryInterface(IID_PPV_ARGS(&iq)))) 133 | return; 134 | // Using ID3D11Debug::ReportLiveDeviceObjects with D3D11_RLDO_DETAIL will help drill into object lifetimes. Objects with Refcount=0 and IntRef=0 will be eventually destroyed through typical Immediate Context usage. However, if the application requires these objects to be destroyed sooner, ClearState followed by Flush on the Immediate Context will realize their destruction. 135 | { 136 | ComPtr ctx; 137 | dev->GetImmediateContext(&ctx); 138 | ctx->ClearState(); 139 | ctx->Flush(); 140 | } 141 | const auto extra = prefix ? prefix : " ?"; 142 | clog << fmt::to_string("------------debug report detail begin. device %p. %s------------", dev, extra) << endl; 143 | dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); //D3D11_RLDO_IGNORE_INTERNAL 144 | debug_to_log(iq.Get()); 145 | clog << fmt::to_string("------------debug report summary begin. device %p. %s------------", dev, extra) << endl; 146 | dbg->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY); 147 | debug_to_log(iq.Get()); 148 | clog << fmt::to_string("------------debug report end. device %p. %s------------", dev, extra) << endl; 149 | } 150 | 151 | void DumpInfo(ComPtr dev, const char* prefix) 152 | { 153 | clog << fmt::to_string("%s Feature level: %#X, create flags: %#X", (prefix ? prefix : ""), dev->GetFeatureLevel(), dev->GetCreationFlags()) << endl; 154 | ComPtr dxgiDev; 155 | MS_ENSURE(dev.As(&dxgiDev)); 156 | ComPtr adapter; 157 | MS_ENSURE(dxgiDev->GetAdapter(&adapter)); 158 | DXGI_ADAPTER_DESC desc; 159 | MS_ENSURE(adapter->GetDesc(&desc)); 160 | char description[256]{}; 161 | snprintf(description, sizeof(description), "%ls", desc.Description); 162 | clog << fmt::to_string("d3d11: %p, dxgi adapter vendor %x, device %x, revision %x, %s", dev.Get(), desc.VendorId, desc.DeviceId, desc.Revision, description) << endl; 163 | } 164 | 165 | ComPtr CreateDXGI() // TODO: int version = 2. 1.0/1.1 can not be mixed 166 | { 167 | #if (MS_API_DESKTOP+0) 168 | dll_t h{nullptr, &FreeLibrary}; 169 | HMODULE dxgi_dll = GetModuleHandleA("dxgi.dll"); 170 | if (!dxgi_dll) { 171 | h.reset(LoadLibraryA("dxgi.dll")); 172 | if (!h) 173 | return nullptr; 174 | dxgi_dll = GetModuleHandleA("dxgi.dll"); 175 | if (!dxgi_dll) 176 | return nullptr; 177 | } 178 | typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); 179 | auto CreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY)GetProcAddress(dxgi_dll, "CreateDXGIFactory"); 180 | if (!CreateDXGIFactory) 181 | return nullptr; 182 | #else 183 | auto CreateDXGIFactory = &CreateDXGIFactory1; // no CreateDXGIFactory, only CreateDXGIFactory1/2 184 | #endif 185 | ComPtr dxgi2; 186 | if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&dxgi2)))) 187 | return dxgi2; 188 | ComPtr dxgi1; 189 | if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&dxgi1)))) 190 | return dxgi1; 191 | ComPtr dxgi; 192 | if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&dxgi)))) 193 | return dxgi; 194 | return nullptr; 195 | } 196 | 197 | // Starting with Windows 8, an adapter called the "Microsoft Basic Render Driver" is always present. This adapter has a VendorId of 0x1414 and a DeviceID of 0x8c 198 | ComPtr CreateDevice(ComPtr dxgi, int adapterIndex, D3D_FEATURE_LEVEL fl, UINT flags) 199 | { 200 | // always link to d3d11 to ensure d3d11.dll is loaded even if Manager is not used 201 | ComPtr adapter; 202 | if (dxgi && adapterIndex >= 0) { 203 | MS_WARN(dxgi->EnumAdapters(adapterIndex, &adapter)); // dxgi2? 204 | DXGI_ADAPTER_DESC desc; 205 | MS_ENSURE(adapter->GetDesc(&desc), nullptr); 206 | char description[256]{}; 207 | snprintf(description, sizeof(description), "%ls", desc.Description); 208 | clog << fmt::to_string("dxgi adapter %d: vendor %x, device %x, revision %x, %s", adapterIndex, desc.VendorId, desc.DeviceId, desc.Revision, description) << endl; 209 | } else { 210 | std::clog << "d3d11 use default adapter" << std::endl; 211 | } 212 | const D3D_FEATURE_LEVEL fls[] = { 213 | fl, 214 | D3D_FEATURE_LEVEL_12_1, 215 | D3D_FEATURE_LEVEL_12_0, 216 | D3D_FEATURE_LEVEL_11_1, 217 | D3D_FEATURE_LEVEL_11_0, 218 | D3D_FEATURE_LEVEL_10_1, 219 | D3D_FEATURE_LEVEL_10_0, 220 | D3D_FEATURE_LEVEL_9_3, // win7, win10/8 mobile/phone 221 | D3D_FEATURE_LEVEL_9_2, 222 | D3D_FEATURE_LEVEL_9_1, // low power devices 223 | }; 224 | ComPtr dev; 225 | // d3d11.1: MUST explicitly provide an array includes D3D_FEATURE_LEVEL_11_1. If Direct3D 11.1 runtime is not available, immediately fails with E_INVALIDARG. 226 | HRESULT hr = S_OK; 227 | size_t fl_index = 0; 228 | for (int i = 1; i < std::extent::value; ++i) { 229 | if (fls[i] == fl) { 230 | fl_index = i; 231 | break; 232 | } 233 | } 234 | // adapter non-null, type D3D_DRIVER_TYPE_HARDWARE: E_INVALIDARG 235 | MS_WARN((hr = D3D11CreateDevice(adapter.Get(), adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE 236 | , nullptr, flags, &fls[fl_index], (UINT)(std::extent::value - fl_index) 237 | , D3D11_SDK_VERSION, &dev, nullptr, nullptr))); 238 | if (hr == E_INVALIDARG) // if fls contains 11_1 but not supported by runtime. null fls will try 11_0~9_1 239 | MS_ENSURE(D3D11CreateDevice(adapter.Get(), adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE 240 | , nullptr, flags, nullptr, 0 //&fls[2], std::extent::value - 2 241 | , D3D11_SDK_VERSION, &dev, nullptr, nullptr), nullptr); 242 | ComPtr dev1; 243 | MS_WARN(dev.As(&dev1)); 244 | clog << fmt::to_string("d3d11.%d device feature level: %#x, requested: %#x.", dev1.Get() ? 1 : 0, dev->GetFeatureLevel(), fl) << endl; 245 | // if null adapter, print adapter info 246 | ComPtr mt; 247 | if (SUCCEEDED(dev.As(&mt))) 248 | mt->SetMultithreadProtected(TRUE); 249 | return dev; 250 | } 251 | 252 | ComPtr CreateDevice(const char* vendor, int adapterIndex, D3D_FEATURE_LEVEL fl, UINT flags) 253 | { 254 | auto dxgi = D3D11::CreateDXGI(); 255 | if (vendor) 256 | adapterIndex = DXGI::VendorIndex(dxgi, vendor); 257 | return CreateDevice(D3D11::CreateDXGI(), adapterIndex, fl, flags); 258 | } 259 | 260 | static bool is_trace_enabled() 261 | { 262 | char v[4]{}; 263 | if (GetEnvironmentVariableA("D3D11_TRACE", v, sizeof(v)) > 0) 264 | return !!std::stoi(v); 265 | return false; 266 | } 267 | 268 | void trace(ComPtr obj, const char* name) 269 | { 270 | if (!obj) 271 | return; 272 | if (name) 273 | SetDebugName(obj, name); 274 | static bool enabled = is_trace_enabled(); 275 | if (!enabled) 276 | return; 277 | #if (_MSC_VER + 0) 278 | class __declspec(uuid("50581513-C9A0-454A-B78F-3A3156D06AC4")) DXTracer final : public RuntimeClass, IUnknown> { 279 | public: 280 | // IInspectable.GetRuntimeClassName? WKPDID_D3DDebugObjectName? 281 | DXTracer(const char* name) : name_(name) { clog << fmt::to_string("%s %p (%s)", __func__, this, name_) << endl; } 282 | ~DXTracer() override { clog << fmt::to_string("%s %p (%s)", __func__, this, name_) << endl; } 283 | private: 284 | const char* name_ = "?"; 285 | }; 286 | 287 | UINT size = sizeof(DXTracer*); 288 | ComPtr dt; 289 | if (ComPtr x; SUCCEEDED(obj.As(&x)) && SUCCEEDED(x->GetPrivateData(__uuidof(DXTracer), &size, dt.ReleaseAndGetAddressOf()))) 290 | return; 291 | else if (ComPtr x; SUCCEEDED(obj.As(&x)) && SUCCEEDED(x->GetPrivateData(__uuidof(DXTracer), &size, dt.ReleaseAndGetAddressOf()))) 292 | return; 293 | else if (ComPtr x; SUCCEEDED(obj.As(&x)) && SUCCEEDED(x->GetPrivateData(__uuidof(DXTracer), &size, dt.ReleaseAndGetAddressOf()))) 294 | return; 295 | if (dt) 296 | return; 297 | dt = Make(name ? name : "?"); 298 | if (ComPtr x; SUCCEEDED(obj.As(&x))) 299 | MS_ENSURE(x->SetPrivateDataInterface(__uuidof(DXTracer), dt.Get())); 300 | else if (ComPtr x; SUCCEEDED(obj.As(&x))) 301 | MS_ENSURE(x->SetPrivateDataInterface(__uuidof(DXTracer), dt.Get())); 302 | else if (ComPtr x; SUCCEEDED(obj.As(&x))) 303 | MS_ENSURE(x->SetPrivateDataInterface(__uuidof(DXTracer), dt.Get())); 304 | //SetPrivateDataInterface(__uuidof(xxx), xxx) // xxx ref +1, so manually Release() after set. GetPrivateData() 305 | #endif 306 | } 307 | 308 | void SetDebugName(ComPtr obj, const char* name) 309 | { 310 | if (!obj) 311 | return; 312 | if (ComPtr x; SUCCEEDED(obj.As(&x))) 313 | MS_ENSURE(x->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)strlen(name), name)); 314 | else if (ComPtr x; SUCCEEDED(obj.As(&x))) 315 | MS_ENSURE(x->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)strlen(name), name)); 316 | else if (ComPtr x; SUCCEEDED(obj.As(&x))) 317 | MS_ENSURE(x->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)strlen(name), name)); 318 | } 319 | 320 | bool Manager::init(const char* vendor, int adapterIndex, D3D_FEATURE_LEVEL fl, UINT flags) 321 | { 322 | #if (MS_API_DESKTOP+0) 323 | d3d11_dll_.reset(LoadLibraryA("d3d11.dll")); 324 | #endif 325 | dev_ = D3D11::CreateDevice(vendor, adapterIndex, fl, D3D11_CREATE_DEVICE_VIDEO_SUPPORT|flags); 326 | if (!dev_) 327 | return false; 328 | trace(dev_, "MFDXDevice"); 329 | debug_report(dev_.Get()); 330 | return true; 331 | } 332 | 333 | Manager::~Manager() 334 | { 335 | mgr_.Reset(); 336 | debug_report(dev_.Get(), __func__); 337 | } 338 | 339 | ComPtr Manager::create() 340 | { 341 | if (!dev_) 342 | return nullptr; 343 | UINT token = 0; 344 | #if (MS_API_DESKTOP+0) 345 | mfplat_dll_.reset(LoadLibraryA("mfplat.dll")); 346 | if (!mfplat_dll_) 347 | return nullptr; 348 | typedef HRESULT (STDAPICALLTYPE *MFCreateDXGIDeviceManager_pfn)(UINT*, IMFDXGIDeviceManager**); 349 | auto MFCreateDXGIDeviceManager = (MFCreateDXGIDeviceManager_pfn)GetProcAddress(mfplat_dll_.get(), "MFCreateDXGIDeviceManager"); 350 | if (!MFCreateDXGIDeviceManager) { 351 | std::clog << "MFCreateDXGIDeviceManager is not found in mfplat.dll. try mshtmlmedia.dll" << std::endl; 352 | // https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/media/gpu/dxva_video_decode_accelerator_win.cc?autodive=0%2F%2F%2F%2F 353 | mfplat_dll_.reset(LoadLibraryA("mshtmlmedia.dll")); 354 | if (!mfplat_dll_) { 355 | std::clog << "Failed to load mshtmlmedia.dll" << std::endl; 356 | return nullptr; 357 | } 358 | MFCreateDXGIDeviceManager = (MFCreateDXGIDeviceManager_pfn)GetProcAddress(mfplat_dll_.get(), "MFCreateDXGIDeviceManager"); 359 | if (!MFCreateDXGIDeviceManager) { 360 | std::clog << "MFCreateDXGIDeviceManager is not found" << std::endl; 361 | return nullptr; 362 | } 363 | } 364 | #endif 365 | MS_ENSURE(MFCreateDXGIDeviceManager(&token, &mgr_), nullptr); 366 | mgr_->ResetDevice(dev_.Get(), token); // MUST create with D3D11_CREATE_DEVICE_VIDEO_SUPPORT 367 | // ResetDevice(nullptr, token) in dtor? 368 | return mgr_; 369 | } 370 | } // namespace D3D11 371 | -------------------------------------------------------------------------------- /D3D11Utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | */ 4 | #pragma once 5 | # pragma push_macro("_WIN32_WINNT") 6 | # if _WIN32_WINNT < 0x0601 // for IDXGIFactory2 7 | # undef _WIN32_WINNT 8 | # define _WIN32_WINNT 0x0601 9 | # endif 10 | #include "base/ms/MSUtils.h" 11 | #include 12 | #include 13 | #include // MFCreateDXGIDeviceManager 14 | #include 15 | # pragma pop_macro("_WIN32_WINNT") 16 | 17 | namespace D3D11 { 18 | 19 | void DumpInfo(ComPtr dev, const char* prefix = ""); 20 | 21 | ComPtr CreateDXGI(); 22 | // result feature level will be <= requested value 23 | ComPtr CreateDevice(ComPtr dxgi, int adapterIndex = 0, D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_12_1, UINT flags = 0); 24 | // vendor != null: try to match vendor 25 | // vendor == null && adapterIndex == -1: default 26 | ComPtr CreateDevice(const char* vendor = nullptr, int adapterIndex = 0, D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_12_1, UINT flags = 0); 27 | D3D_FEATURE_LEVEL to_feature_level(float value = 0); 28 | D3D_FEATURE_LEVEL to_feature_level(const char* name = nullptr); 29 | void debug_report(ID3D11Device* dev, const char* prefix = nullptr); 30 | 31 | // IDXGIObject, ID3D11Device, ID3D11DeviceChild 32 | void SetDebugName(ComPtr obj, const char* name); 33 | 34 | void trace(ComPtr obj, const char* name = nullptr); 35 | 36 | class Manager 37 | { 38 | public: 39 | ~Manager(); 40 | // vendor != null: try to match vendor 41 | // vendor == null && adapterIndex == -1: default 42 | bool init(const char* vendor = nullptr, int adapterIndex = 0, D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_12_1, UINT flags = 0); 43 | ComPtr create(); // win8+ 44 | private: 45 | dll_t d3d11_dll_{nullptr, &FreeLibrary}; 46 | dll_t mfplat_dll_{nullptr, &FreeLibrary}; 47 | ComPtr dev_; 48 | ComPtr mgr_; 49 | }; 50 | } // namespace D3D11 51 | -------------------------------------------------------------------------------- /D3D9Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #include "D3D9Utils.h" 11 | #include "base/fmt.h" 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | #if (MS_API_DESKTOP+0) 17 | namespace DXGI { 18 | const char* VendorName(UINT id); 19 | } 20 | 21 | namespace D3D9 { 22 | 23 | static int VendorIndex(ComPtr id3d9, const char* vendor) 24 | { 25 | if (!id3d9 || !vendor) 26 | return D3DADAPTER_DEFAULT; 27 | D3DADAPTER_IDENTIFIER9 ai{}; 28 | const auto n = id3d9->GetAdapterCount(); // why always 1? 29 | for (UINT i = 0; i < 10; ++i) { 30 | MS_ENSURE(id3d9->GetAdapterIdentifier(i, 0, &ai), D3DADAPTER_DEFAULT); 31 | string s(DXGI::VendorName(ai.VendorId)); 32 | transform(s.begin(), s.end(), s.begin(), [](char c){ return (char)tolower(c);}); 33 | if (s.find(vendor) != string::npos) 34 | return i; 35 | s = ai.Description; 36 | transform(s.begin(), s.end(), s.begin(), [](char c){ return (char)tolower(c);}); 37 | if (s.find(vendor) != string::npos) 38 | return i; 39 | } 40 | return D3DADAPTER_DEFAULT; 41 | } 42 | 43 | ComPtr Create() 44 | { 45 | HMODULE dll = GetModuleHandleA("d3d9.dll"); 46 | if (!dll) { 47 | std::clog << "d3d9.dll is not loaded" << std::endl; 48 | return nullptr; 49 | } 50 | ComPtr id3d9; 51 | typedef HRESULT (WINAPI *Create9ExFunc)(UINT SDKVersion, IDirect3D9Ex **ppD3D); //IDirect3D9Ex: void is ok 52 | auto create9ex = (Create9ExFunc)GetProcAddress(dll, "Direct3DCreate9Ex"); 53 | if (create9ex) { 54 | ComPtr id3d9ex; 55 | MS_WARN(create9ex(D3D_SDK_VERSION, &id3d9ex)); 56 | id3d9 = id3d9ex; 57 | } 58 | if (id3d9) 59 | return id3d9; 60 | IDirect3D9 * WINAPI Direct3DCreate9(UINT SDKVersion); 61 | typedef IDirect3D9* (WINAPI *Create9Func)(UINT SDKVersion); 62 | Create9Func create9 = (Create9Func)GetProcAddress(dll, "Direct3DCreate9"); 63 | if (!create9) 64 | return nullptr; 65 | id3d9 = create9(D3D_SDK_VERSION); 66 | if (id3d9) 67 | return id3d9; 68 | std::clog << "Failed to create d3d9" << std::endl; 69 | return nullptr; 70 | } 71 | 72 | // getenv("DXADAPTOR") here or in caller? different adapters works together? 73 | ComPtr CreateDevice(ComPtr id3d9, int adapter) 74 | { 75 | D3DADAPTER_IDENTIFIER9 ai{}; 76 | if (adapter < 0) 77 | adapter = D3DADAPTER_DEFAULT; 78 | MS_ENSURE(id3d9->GetAdapterIdentifier(adapter, 0, &ai), nullptr); // adapter < GetAdapterCount() 79 | clog << fmt::to_string("d3d9 adapter: %d, vendor: %x, driver %s, device: %s(%x), revision %x, %s", adapter, ai.VendorId, ai.Driver, ai.DeviceName, ai.DeviceId, ai.Revision, ai.Description) << endl; 80 | ComPtr dev; 81 | D3DPRESENT_PARAMETERS pp{}; 82 | pp.Flags = D3DPRESENTFLAG_VIDEO; 83 | pp.Windowed = TRUE; 84 | pp.hDeviceWindow = ::GetShellWindow(); //NULL; 85 | pp.SwapEffect = D3DSWAPEFFECT_DISCARD; 86 | //pp.MultiSampleType = D3DMULTISAMPLE_NONE; 87 | //pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; 88 | pp.BackBufferCount = 1; //0; /* FIXME what to put here */ 89 | pp.BackBufferFormat = D3DFMT_UNKNOWN; //D3DFMT_X8R8G8B8; /* FIXME what to put here */ 90 | pp.BackBufferWidth = 1; //0; 91 | pp.BackBufferHeight = 1; //0; 92 | //pp.EnableAutoDepthStencil = FALSE; 93 | 94 | // D3DCREATE_MULTITHREADED is required by gl interop. https://www.opengl.org/registry/specs/NV/DX_interop.txt 95 | // D3DCREATE_SOFTWARE_VERTEXPROCESSING in other dxva decoders. D3DCREATE_HARDWARE_VERTEXPROCESSING is required by cuda in cuD3D9CtxCreate() 96 | DWORD flags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING; 97 | ComPtr id3d9ex; 98 | if (SUCCEEDED(id3d9.As(&id3d9ex))) { 99 | ComPtr devex; 100 | MS_WARN(id3d9ex->CreateDeviceEx(adapter, D3DDEVTYPE_HAL, GetShellWindow(),// GetDesktopWindow(), //GetShellWindow()? 101 | flags, &pp, nullptr, &devex)); 102 | dev = devex; 103 | } 104 | if (dev) { 105 | std::clog << "IDirect3DDevice9Ex is created" << std::endl; 106 | return dev; 107 | } 108 | MS_ENSURE(id3d9->CreateDevice(adapter, D3DDEVTYPE_HAL, GetShellWindow(),// GetDesktopWindow(), //GetShellWindow()? 109 | flags, &pp, &dev), nullptr); 110 | return dev; 111 | } 112 | 113 | bool Manager::init(const char* vendor, int adapter) 114 | { 115 | if (!id3d9_) { 116 | d3d9_dll_.reset(LoadLibraryA("d3d9.dll")); 117 | if (!d3d9_dll_) 118 | return false; 119 | id3d9_ = D3D9::Create(); 120 | } 121 | if (!id3d9_) 122 | return false; 123 | adapter = VendorIndex(id3d9_, vendor); 124 | dev_ = D3D9::CreateDevice(id3d9_, adapter); 125 | if (!dev_) 126 | return false; 127 | return true; 128 | } 129 | 130 | ComPtr Manager::create() 131 | { 132 | if (!dev_) 133 | return nullptr; 134 | dxva2_dll_.reset(LoadLibraryA("dxva2.dll")); 135 | if (!dxva2_dll_) 136 | return nullptr; 137 | typedef HRESULT (WINAPI *DXVA2CreateDirect3DDeviceManager9_f)(UINT *pResetToken, IDirect3DDeviceManager9 **); 138 | auto DXVA2CreateDirect3DDeviceManager9 = (DXVA2CreateDirect3DDeviceManager9_f)GetProcAddress(dxva2_dll_.get(), "DXVA2CreateDirect3DDeviceManager9"); 139 | UINT token = 0; 140 | MS_ENSURE(DXVA2CreateDirect3DDeviceManager9(&token, &mgr_), nullptr); 141 | mgr_->ResetDevice(dev_.Get(), token); 142 | return mgr_; 143 | } 144 | } // namespace D3D9 145 | #endif // (MS_API_DESKTOP+0) -------------------------------------------------------------------------------- /D3D9Utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | #include "base/ms/MSUtils.h" 12 | #if (MS_API_DESKTOP+0) 13 | #include 14 | #include // ensure IDirect3DDeviceManager9 is defined 15 | #include 16 | namespace D3D9 { 17 | ComPtr Create(); 18 | // TODO: CreateVideoDevice() 19 | ComPtr CreateDevice(ComPtr id3d9, int adapter = D3DADAPTER_DEFAULT); 20 | 21 | class Manager 22 | { 23 | public: 24 | bool init(const char* vendor = nullptr, int adapter = D3DADAPTER_DEFAULT); 25 | ComPtr create(); 26 | private: 27 | dll_t d3d9_dll_{nullptr, &FreeLibrary}; 28 | dll_t dxva2_dll_{nullptr, &FreeLibrary}; 29 | ComPtr id3d9_; // create first, destroy last 30 | ComPtr dev_; 31 | ComPtr mgr_; 32 | }; 33 | } // namespace D3D9 34 | #endif // (MS_API_DESKTOP+0) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /MFAudioGlue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | //#ifdef _MSC_VER 11 | # pragma push_macro("_WIN32_WINNT") 12 | # if _WIN32_WINNT < 0x0602 // MF_MT_AAC_PAYLOAD_TYPE 13 | # undef _WIN32_WINNT 14 | # define _WIN32_WINNT 0x0602 15 | # endif 16 | #include "base/ms/MFGlue.h" 17 | #include "base/ByteArrayBuffer.h" 18 | #include "mdk/MediaInfo.h" 19 | #include "mdk/AudioFrame.h" 20 | #include 21 | #if __has_include() // msvc 22 | # include 23 | #else // mingw 24 | # include 25 | #endif 26 | //#ifdef _MSC_VER 27 | # pragma pop_macro("_WIN32_WINNT") 28 | 29 | MDK_NS_BEGIN 30 | namespace MF { 31 | 32 | bool to(AudioFormat::ChannelMap& cm, UINT32 cl) 33 | { 34 | cm = uint64_t(cl); 35 | return true; 36 | } 37 | 38 | bool to(AudioFormat& af, const IMFAttributes* ca) 39 | { 40 | auto a = const_cast(ca); 41 | UINT32 v = 0; 42 | if (FAILED(a->GetUINT32(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, &v)) // may be not set (if no padding data) 43 | && FAILED(a->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &v))) 44 | return false; 45 | GUID subtype; 46 | MS_ENSURE(a->GetGUID(MF_MT_SUBTYPE, &subtype), false); 47 | bool is_flt = false; 48 | bool is_uint = false; 49 | if (subtype == MFAudioFormat_PCM) { 50 | is_uint = v == 8; 51 | } else if (subtype == MFAudioFormat_Float) { 52 | is_flt = true; 53 | } else { // spdif pass through? not supported yet 54 | return false; 55 | } 56 | af.setSampleFormat(AudioFormat::make(v/8, is_flt, is_uint, false)); 57 | 58 | if (SUCCEEDED(a->GetUINT32(MF_MT_AUDIO_CHANNEL_MASK, &v))) { 59 | AudioFormat::ChannelMap cm; 60 | if (!to(cm, v)) 61 | return false; 62 | af.setChannelMap(cm); 63 | } else { 64 | MS_ENSURE(a->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &v), false); 65 | af.setChannels(v); 66 | } 67 | 68 | MS_ENSURE(a->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &v), false); 69 | af.setSampleRate(v); 70 | return true; 71 | } 72 | 73 | 74 | bool to(AudioFrame& frame, ComPtr sample, bool copy) 75 | { 76 | LONGLONG t = 0; 77 | if (SUCCEEDED(sample->GetSampleTime(&t))) 78 | frame.setTimestamp(from_mf_time(t)); 79 | DWORD nb_bufs = 0; 80 | MS_ENSURE(sample->GetBufferCount(&nb_bufs), false); 81 | const bool contiguous = frame.format().planeCount() > (int)nb_bufs; 82 | 83 | for (DWORD i = 0; i < nb_bufs; ++i) { 84 | ComPtr buf; 85 | MS_ENSURE(sample->GetBufferByIndex(i, &buf), false); 86 | if (copy) { 87 | BYTE* data = nullptr; 88 | DWORD len = 0; 89 | MS_ENSURE(buf->Lock(&data, nullptr, &len), false); 90 | if (contiguous) { 91 | const uint8_t* da[] = {data, nullptr, nullptr}; 92 | //frame.setBuffers(da); 93 | } else { 94 | frame.addBuffer(std::make_shared(len, data)); 95 | } 96 | buf->Unlock(); 97 | } else { 98 | frame.addBuffer(to(buf)); 99 | } 100 | } 101 | DWORD bytes = 0; 102 | MS_ENSURE(sample->GetTotalLength(&bytes), false); 103 | frame.setSamplesPerChannel(frame.format().framesForBytes(bytes)); 104 | return true; 105 | } 106 | 107 | bool from(const AudioFormat& af, IMFAttributes* a) 108 | { 109 | return false; 110 | } 111 | 112 | 113 | bool from(const AudioCodecParameters& par, IMFAttributes* a) 114 | { 115 | MS_ENSURE(a->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), false); 116 | MS_ENSURE(a->SetGUID(MF_MT_SUBTYPE, *codec_for(par.codec, MediaType::Audio)), false); 117 | 118 | if (par.bit_rate > 0) 119 | MS_ENSURE(a->SetUINT32(MF_MT_AVG_BITRATE, (UINT32)par.bit_rate), false); 120 | 121 | bool use_extra = !par.extra.empty(); // and if no bsf 122 | if (par.codec == "aac") { // https://docs.microsoft.com/en-us/windows/desktop/medfound/mf-mt-user-data-attribute 123 | // https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types 124 | // The first 12 bytes of MF_MT_USER_DATA: HEAACWAVEINFO.wPayloadType,wAudioProfileLevelIndication,wStructType 125 | ByteArray extra(int(12 + par.extra.size()), 0); // +2 for last 2 bytes? 126 | memcpy(extra.data() + 12, par.extra.data(), par.extra.size()); 127 | const UINT32 payload_type = par.extra.empty() ? 1 : 0; 128 | extra[0] = payload_type; // for HEAACWAVEINFO.wPayloadType 129 | // https://docs.microsoft.com/en-us/windows/desktop/medfound/mf-mt-aac-payload-type 130 | MS_ENSURE(a->SetBlob(MF_MT_USER_DATA, extra.data(), extra.size()), false); 131 | MS_ENSURE(a->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, payload_type), false); 132 | } else if (use_extra) { 133 | // check existing MF_MT_USER_DATA? 134 | MS_ENSURE(a->SetBlob(MF_MT_USER_DATA, par.extra.data(), (UINT32)par.extra.size()), false); 135 | } 136 | 137 | a->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, par.sample_rate); 138 | a->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, par.channels); 139 | // WAVEFORMATEX stuff; might be required by some codecs. 140 | if (par.block_align > 0) 141 | a->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, par.block_align); 142 | if (par.bit_rate > 0) 143 | a->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, (UINT32)(par.bit_rate / 8)); 144 | if (par.bits_per_coded_sample) 145 | a->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, par.bits_per_coded_sample); 146 | 147 | a->SetUINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1); // ?? 148 | return true; 149 | } 150 | } // namespace MF 151 | MDK_NS_END -------------------------------------------------------------------------------- /MFCompat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | #include 12 | #if !(_MSC_VER + 0) 13 | //INITGUID 14 | //# pragma push_macro("DEFINE_GUID") 15 | #undef DEFINE_GUID 16 | #define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } 17 | 18 | // WMA1. Apparently there is no official GUID symbol for this. 19 | DEFINE_GUID(MFAudioFormat_MSAUDIO1, 0x00000160, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); 20 | 21 | // mmreg.h 22 | #ifndef WAVE_FORMAT_FLAC 23 | #define WAVE_FORMAT_FLAC 0xF1AC /* flac.sourceforge.net */ 24 | #endif 25 | //////// mfapi.h BEGIN 26 | //#if (WINVER >= _WIN32_WINNT_WINTHRESHOLD) 27 | DEFINE_MEDIATYPE_GUID( MFAudioFormat_FLAC, WAVE_FORMAT_FLAC ); 28 | DEFINE_MEDIATYPE_GUID( MFAudioFormat_ALAC, WAVE_FORMAT_ALAC ); 29 | DEFINE_MEDIATYPE_GUID( MFAudioFormat_Opus, WAVE_FORMAT_OPUS ); 30 | //#endif 31 | // These audio types are not derived from an existing wFormatTag 32 | DEFINE_GUID(MFAudioFormat_Dolby_AC3, // == MEDIASUBTYPE_DOLBY_AC3 defined in ksuuids.h 33 | 0xe06d802c, 0xdb46, 0x11cf, 0xb4, 0xd1, 0x00, 0x80, 0x05f, 0x6c, 0xbb, 0xea); 34 | DEFINE_GUID(MFAudioFormat_Dolby_DDPlus, // == MEDIASUBTYPE_DOLBY_DDPLUS defined in wmcodecdsp.h 35 | 0xa7fb87af, 0x2d02, 0x42fb, 0xa4, 0xd4, 0x5, 0xcd, 0x93, 0x84, 0x3b, 0xdd); 36 | DEFINE_GUID(MFAudioFormat_Vorbis, // {8D2FD10B-5841-4a6b-8905-588FEC1ADED9} 37 | 0x8D2FD10B, 0x5841, 0x4a6b, 0x89, 0x05, 0x58, 0x8F, 0xEC, 0x1A, 0xDE, 0xD9); 38 | 39 | DEFINE_MEDIATYPE_GUID(MFVideoFormat_MP42, 0x3234504D); 40 | DEFINE_MEDIATYPE_GUID(MFVideoFormat_L8, 50); 41 | DEFINE_MEDIATYPE_GUID(MFVideoFormat_L16, 81); 42 | 43 | DEFINE_GUID(MF_MT_VIDEO_ROTATION, 0xc380465d, 0x2271, 0x428c, 0x9b, 0x83, 0xec, 0xea, 0x3b, 0x4a, 0x85, 0xc1); 44 | // desktop|game 45 | DEFINE_GUID(MF_MT_CUSTOM_VIDEO_PRIMARIES, 0x47537213, 0x8cfb, 0x4722, 0xaa, 0x34, 0xfb, 0xc9, 0xe2, 0x4d, 0x77, 0xb8); 46 | //#if (WINVER >= _WIN32_WINNT_WIN10) 47 | DEFINE_GUID(MF_MT_MAX_LUMINANCE_LEVEL, 0x50253128, 0xc110, 0x4de4, 0x98, 0xae, 0x46, 0xa3, 0x24, 0xfa, 0xe6, 0xda); 48 | DEFINE_GUID(MF_MT_MAX_FRAME_AVERAGE_LUMINANCE_LEVEL, 0x58d4bf57, 0x6f52, 0x4733, 0xa1, 0x95, 0xa9, 0xe2, 0x9e, 0xcf, 0x9e, 0x27); 49 | DEFINE_GUID(MF_MT_MAX_MASTERING_LUMINANCE, 0xd6c6b997, 0x272f, 0x4ca1, 0x8d, 0x0, 0x80, 0x42, 0x11, 0x1a, 0xf, 0xf6); 50 | DEFINE_GUID(MF_MT_MIN_MASTERING_LUMINANCE, 0x839a4460, 0x4e7e, 0x4b4f, 0xae, 0x79, 0xcc, 0x8, 0x90, 0x5c, 0x7b, 0x27); 51 | //#endif // (WINVER > _WIN32_WINNT_WIN10) 52 | //////// mfapi.h END 53 | 54 | //////// mftransform.h BEGIN 55 | DEFINE_GUID(MFT_ENUM_HARDWARE_VENDOR_ID_Attribute, 0x3aecb0cc, 0x35b, 0x4bcc, 0x81, 0x85, 0x2b, 0x8d, 0x55, 0x1e, 0xf3, 0xaf); 56 | DEFINE_GUID(MFT_DECODER_EXPOSE_OUTPUT_TYPES_IN_NATIVE_ORDER, 0xef80833f, 0xf8fa, 0x44d9, 0x80, 0xd8, 0x41, 0xed, 0x62, 0x32, 0x67, 0xc); 57 | DEFINE_GUID(MFT_DECODER_QUALITY_MANAGEMENT_CUSTOM_CONTROL, 0xa24e30d7, 0xde25, 0x4558, 0xbb, 0xfb, 0x71, 0x7, 0xa, 0x2d, 0x33, 0x2e); 58 | DEFINE_GUID(MFT_DECODER_QUALITY_MANAGEMENT_RECOVERY_WITHOUT_ARTIFACTS, 0xd8980deb, 0xa48, 0x425f, 0x86, 0x23, 0x61, 0x1d, 0xb4, 0x1d, 0x38, 0x10); 59 | 60 | //////// mftransform.h END 61 | 62 | 63 | //////// mfidl.h BEGIN 64 | DEFINE_GUID(CLSID_MSH264DecoderMFT, 0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45, 0x28, 0x31, 0xA8, 0x7D, 0x9D); 65 | DEFINE_GUID(CLSID_MSH264EncoderMFT, 0x6ca50344, 0x051a, 0x4ded, 0x97, 0x79, 0xa4, 0x33, 0x05, 0x16, 0x5e, 0x35); 66 | DEFINE_GUID(CLSID_MSDDPlusDecMFT, 0x177C0AFE, 0x900B, 0x48d4, 0x9E, 0x4C, 0x57, 0xAD, 0xD2, 0x50, 0xB3, 0xD4); 67 | DEFINE_GUID(CLSID_MP3DecMediaObject, 0xbbeea841, 0x0a63, 0x4f52, 0xa7, 0xab, 0xa9, 0xb3, 0xa8, 0x4e, 0xd3, 0x8a); 68 | DEFINE_GUID(CLSID_MSAACDecMFT, 0x32d186a7, 0x218f, 0x4c75, 0x88, 0x76, 0xdd, 0x77, 0x27, 0x3a, 0x89, 0x99); 69 | DEFINE_GUID(CLSID_MSH265DecoderMFT, 0x420A51A3, 0xD605, 0x430C, 0xB4, 0xFC, 0x45, 0x27, 0x4F, 0xA6, 0xC5, 0x62); 70 | DEFINE_GUID(CLSID_WMVDecoderMFT, 0x82d353df, 0x90bd, 0x4382, 0x8b, 0xc2, 0x3f, 0x61, 0x92, 0xb7, 0x6e, 0x34); 71 | DEFINE_GUID(CLSID_WMADecMediaObject, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a); 72 | DEFINE_GUID(CLSID_MSMPEGAudDecMFT, 0x70707B39, 0xB2CA, 0x4015, 0xAB, 0xEA, 0xF8, 0x44, 0x7D, 0x22, 0xD8, 0x8B); 73 | DEFINE_GUID(CLSID_MSMPEGDecoderMFT, 0x2D709E52, 0x123F, 0x49b5, 0x9C, 0xBC, 0x9A, 0xF5, 0xCD, 0xE2, 0x8F, 0xB9); 74 | DEFINE_GUID(CLSID_AudioResamplerMediaObject, 0xf447b69e, 0x1884, 0x4a7e, 0x80, 0x55, 0x34, 0x6f, 0x74, 0xd6, 0xed, 0xb3); 75 | DEFINE_GUID(CLSID_MSVPxDecoder, 0xE3AAF548, 0xC9A4, 0x4C6E, 0x23, 0x4D, 0x5A, 0xDA, 0x37, 0x4B, 0x00, 0x00); 76 | DEFINE_GUID(CLSID_MSOpusDecoder, 0x63e17c10, 0x2d43, 0x4c42, 0x8f, 0xe3, 0x8d, 0x8b, 0x63, 0xe4, 0x6a, 0x6a); 77 | DEFINE_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82); 78 | 79 | //#define MIDL_INTERFACE(x) struct DECLSPEC_UUID(x) DECLSPEC_NOVTABLE 80 | 81 | #ifndef __CRT_UUID_DECL 82 | // clang: attribute declaration must precede definition [-Wignored-attributes] (previous definition is here DECLARE_INTERFACE_(IDirectSoundNotify, IUnknown)) 83 | struct __declspec(uuid("{245BF8E9-0755-40f7-88A5-AE0F18D55E17}")) IMFTrackedSample; 84 | #endif 85 | // https://stackoverflow.com/questions/23977244/how-can-i-define-an-uuid-for-a-class-and-use-uuidof-in-the-same-way-for-g 86 | // define missing guid of IMFTrackedSample, required by __uuidof() 87 | #ifdef __CRT_UUID_DECL // for mingw, in _mingw.h 88 | struct IMFTrackedSample; 89 | __CRT_UUID_DECL(IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88, 0xa5, 0xae, 0x0F, 0x18, 0xD5, 0x5E, 0x17); 90 | #endif // assume msvc like 91 | MIDL_INTERFACE("245BF8E9-0755-40f7-88A5-AE0F18D55E17") 92 | IMFTrackedSample : public IUnknown 93 | { 94 | public: 95 | virtual HRESULT STDMETHODCALLTYPE SetAllocator( 96 | /* [annotation][in] */ 97 | _In_ IMFAsyncCallback *pSampleAllocator, 98 | /* [unique][in] */ IUnknown *pUnkState) = 0; 99 | 100 | }; 101 | 102 | // TODO: IMFShutdown 103 | //////// mfidl.h END 104 | 105 | //# pragma pop_macro("DEFINE_GUID") 106 | #endif 107 | // uuids not defined in sdk: https://www.magnumdb.com/search?q=value%3A%223341%22 108 | // find uuid name by value: dumpbin -all mfuuid.lib, RAW DATA field is uuid value in littel endian 109 | #define DEFINE_GUID_STATIC(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } 110 | // TODO: _uuid literal 111 | DEFINE_GUID_STATIC(MF_MT_D3D_DECODE_PROFILE_GUID, 0x657c3e17, 0x3341, 0x41a7, 0x9a, 0xe6, 0x37, 0xa9, 0xd6, 0x99, 0x85, 0x1f); 112 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_PACKAGED_WINDOWS_SIGNED, 0x3c0fbe52, 0xd034, 0x4115, 0x99, 0x5d, 0x95, 0xb3, 0x56, 0xb9, 0x85, 0x5c); 113 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_ABSOLUTE_DLLPATH, 0x7347c815, 0x79fc, 0x4ad9, 0x87, 0x7d, 0xac, 0xdf, 0x5f, 0x46, 0x68, 0x5e); 114 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_PACKAGE_FULL_NAME, 0x957193ad, 0x9029, 0x4835, 0xa2, 0xf2, 0x3e, 0xc9, 0xae, 0x9b, 0xb6, 0xc8); 115 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_PACKAGE_FAMILY_NAME, 0x9d8b61a8, 0x6bc8, 0x4bff, 0xb3, 0x1f, 0x3a, 0x31, 0x06, 0x0a, 0xfa, 0x3d); 116 | DEFINE_GUID_STATIC(MF_TELEMETRY_OBJECT_INSTANCE_ATTRIBUTE, 0xbb49bc51, 0x1810, 0x4c3a, 0xa9, 0xcf, 0xd5, 0x9c, 0x4e, 0x5b, 0x96, 0x22); // not defined in mfuuid.lib 117 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_ACTIVATABLE_CLASS_ID, 0xde106d30, 0x42fb, 0x4767, 0x80, 0x8d, 0x0f, 0xcc, 0x68, 0x11, 0xb0, 0xb9); 118 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_PACKAGE_REG_NEEDED, 0xf9542f80, 0xd069, 0x4efe, 0xb3, 0x0d, 0x34, 0x55, 0x36, 0xf7, 0x6a, 0xaa); 119 | DEFINE_GUID_STATIC(MF_MEDIA_EXTENSION_WEB_PLATFORM_ALLOWED, 0xf9a1ef38, 0xf61e, 0x42e6, 0x87, 0xb3, 0x30, 0x94, 0x38, 0xf9, 0xac, 0x67); 120 | DEFINE_GUID_STATIC(MF_INPROCDLL_LIFETIME_MANAGER, 0x592a2a5a, 0xe797, 0x491a, 0x97, 0x38, 0xc0, 0x00, 0x7b, 0xe2, 0x8c, 0x52); // not defined in mfuuid.lib 121 | //KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS, 0x0000000a, 0x0cea, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 122 | #if (WDK_NTDDI_VERSION < NTDDI_WIN10_RS3) 123 | DEFINE_MEDIATYPE_GUID(MFVideoFormat_AV1, FCC('AV01')); 124 | #endif 125 | // MFVideoFormat_DVH1, 'dvh1' -------------------------------------------------------------------------------- /MFGlue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018~2021 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #include "MFGlue.h" 11 | #include "mdk/Buffer.h" 12 | #include "mdk/MediaInfo.h" 13 | #include "mdk/Packet.h" 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "base/log.h" 21 | 22 | MDK_NS_BEGIN 23 | namespace MF { 24 | /* 25 | IMFMediaBuffer, IMFSample, IMFMediaType: 26 | This interface is available on the following platforms if the Windows Media Format 11 SDK redistributable components are installed: 27 | Windows XP SP2+, Windows XP Media Center Edition 2005 with KB900325 (Windows XP Media Center Edition 2005) and KB925766 (October 2006 Update Rollup for Windows XP Media Center Edition) installed. 28 | 29 | TODO: dynamic load new dlls, or delay load? 30 | */ 31 | class MFBuffer final : public Buffer { 32 | int size_; 33 | ptrdiff_t offset_; 34 | // FIXME: ptr after unlock is invalid. so return an object can be implicitly converted to unit8_t* and unlock in it's dtor and use as auto? 35 | // or add lock/unlock in Buffer? 36 | mutable uint8_t* locked_ = nullptr; 37 | mutable ComPtr mfbuf_; 38 | public: 39 | MFBuffer(ComPtr mfbuf, ptrdiff_t offset = 0, int size = -1) 40 | : size_(size) 41 | , offset_(offset) 42 | , mfbuf_(mfbuf) 43 | {} 44 | ~MFBuffer() { 45 | if (locked_) 46 | MS_WARN(mfbuf_->Unlock()); 47 | } 48 | 49 | const uint8_t* constData() const override { 50 | if (!locked_) 51 | MS_ENSURE(mfbuf_->Lock(&locked_, nullptr, nullptr), nullptr); //D3DERR_INVALIDCALL: if d3d surface is not lockable 52 | return locked_ + offset_; 53 | } 54 | size_t size() const override { 55 | if (size_ > 0) 56 | return size_; 57 | DWORD len = 0; 58 | MS_ENSURE(mfbuf_->GetMaxLength(&len), 0); 59 | return len - offset_; 60 | } 61 | }; 62 | 63 | BufferRef to(ComPtr b, ptrdiff_t offset, int size) 64 | { 65 | if (!b) 66 | return nullptr; 67 | return std::make_shared(b, offset, size); 68 | } 69 | 70 | class MFBuffer2D final : public Buffer2D { 71 | int size_; 72 | mutable LONG stride_ = 0; 73 | ptrdiff_t offset_; 74 | // FIXME: ptr after unlock is invalid. so return an object can be implicitly converted to unit8_t* and unlock in it's dtor and use as auto? 75 | // or add lock/unlock in Buffer? 76 | mutable uint8_t* locked_ = nullptr; 77 | mutable ComPtr mfbuf_; 78 | public: 79 | MFBuffer2D(ComPtr mfbuf, ptrdiff_t offset = 0, int size = -1) 80 | : size_(size) 81 | , offset_(offset) 82 | , mfbuf_(mfbuf) 83 | {} 84 | ~MFBuffer2D() { 85 | if (locked_) 86 | MS_WARN(mfbuf_->Unlock2D()); 87 | } 88 | 89 | const uint8_t* constData() const override { 90 | if (!ensureLock()) 91 | return nullptr; 92 | return locked_ + offset_; 93 | } 94 | size_t size() const override { 95 | if (size_ > 0) 96 | return size_; 97 | ComPtr b; 98 | MS_ENSURE(mfbuf_.As(&b), 0); 99 | DWORD len = 0; 100 | MS_ENSURE(b->GetMaxLength(&len), 0); 101 | return len - offset_; 102 | } 103 | size_t stride() const override { 104 | if (!ensureLock()) 105 | return 0; 106 | return stride_; // FIXME: stride<0: bottom up 107 | } 108 | private: 109 | bool ensureLock() const { 110 | if (locked_) 111 | return true; 112 | ComPtr b; // FIXME: win8+. faster? 113 | if (SUCCEEDED(mfbuf_.As(&b))) // TODO: ppbBufferStart instead of ppbScanline0 to work with plPitch<0 114 | MS_ENSURE(b->Lock2DSize(MF2DBuffer_LockFlags_Read, &locked_, &stride_, nullptr, nullptr), false); //D3DERR_INVALIDCALL: if d3d surface is not lockable 115 | else 116 | MS_ENSURE(mfbuf_->Lock2D(&locked_, &stride_), false); //D3DERR_INVALIDCALL: if d3d surface is not lockable 117 | return true; 118 | } 119 | }; 120 | 121 | Buffer2DRef to(ComPtr b, ptrdiff_t offset, int size) 122 | { 123 | if (!b) 124 | return nullptr; 125 | return std::make_shared(b, offset, size); 126 | } 127 | 128 | 129 | #if (_MSC_VER + 0) // RuntimeClass is missing in mingw 130 | class MFMediaBufferView : public RuntimeClass, IMFMediaBuffer> // IUnknown is implemented by RuntimeClass 131 | { 132 | BufferRef buf_; 133 | public: 134 | MFMediaBufferView(BufferRef b) : buf_(b) {} 135 | HRESULT STDMETHODCALLTYPE Lock(BYTE **ppbBuffer, _Out_opt_ DWORD *pcbMaxLength, _Out_opt_ DWORD *pcbCurrentLength) override { 136 | *ppbBuffer = buf_->data(); 137 | if (pcbMaxLength) 138 | *pcbMaxLength = (DWORD)buf_->size(); 139 | if (pcbCurrentLength) 140 | *pcbCurrentLength = (DWORD)buf_->size(); 141 | return S_OK; 142 | } 143 | 144 | HRESULT STDMETHODCALLTYPE Unlock() override {return S_OK;} 145 | 146 | HRESULT STDMETHODCALLTYPE GetCurrentLength(_Out_ DWORD *pcbCurrentLength) override { 147 | *pcbCurrentLength = (DWORD)buf_->size(); 148 | return S_OK; 149 | } 150 | 151 | HRESULT STDMETHODCALLTYPE SetCurrentLength(DWORD cbCurrentLength) override {return S_OK;} 152 | 153 | HRESULT STDMETHODCALLTYPE GetMaxLength(_Out_ DWORD *pcbMaxLength) override { 154 | *pcbMaxLength = (DWORD)buf_->size(); 155 | return S_OK; 156 | } 157 | }; 158 | #endif // (_MSC_VER + 0) 159 | 160 | ComPtr from(BufferRef buf, int align) 161 | { 162 | ComPtr b; 163 | const auto a = std::max(align, 16); 164 | #if (_MSC_VER + 0) // RuntimeClass is missing in mingw 165 | if (((intptr_t)buf->constData() & (a-1)) == 0) 166 | return Make(buf); 167 | #endif 168 | MS_ENSURE(MFCreateAlignedMemoryBuffer((DWORD)buf->size(), a - 1, &b), nullptr); 169 | BYTE* ptr = nullptr; 170 | MS_ENSURE(b->Lock(&ptr, nullptr, nullptr), nullptr); 171 | memcpy(ptr, buf->constData(), buf->size()); 172 | MS_ENSURE(b->SetCurrentLength((DWORD)buf->size()), nullptr); // necessary? 173 | b->Unlock(); 174 | return b; 175 | } 176 | 177 | ComPtr from(Buffer2DRef buf) 178 | { 179 | ComPtr b; 180 | // MFCreate2DMediaBuffer: win8 181 | return b; 182 | } 183 | 184 | 185 | // TODO: IMFAttributes 186 | //IMFSample::SetSampleTime method: 100-nanosecond 187 | ComPtr from(const Packet& pkt, int align) 188 | { 189 | if (!pkt) 190 | return nullptr; 191 | ComPtr p; 192 | MS_ENSURE(MFCreateSample(&p), nullptr); 193 | MS_ENSURE(p->AddBuffer(from(pkt.buffer, align).Get()), nullptr); // take the ownership. E_INVALIDARG: AddBuffer(nullptr) 194 | p->SetSampleTime(to_mf_time(pkt.pts)); 195 | if (pkt.duration > 0) 196 | p->SetSampleDuration(to_mf_time(pkt.duration)); // MF_E_NO_SAMPLE_DURATION in GetSampleDuration() if not called 197 | if (pkt.hasKeyFrame) { 198 | ComPtr attr; 199 | if (SUCCEEDED(p.As(&attr))) 200 | attr->SetUINT32(MFSampleExtension_CleanPoint, 1); 201 | } 202 | return p; 203 | } 204 | 205 | void to(Packet& pkt, ComPtr mfpkt) 206 | { 207 | ComPtr b; // sample is compressed, no need to use ConvertToContiguousBuffer() 208 | MS_ENSURE(mfpkt->GetBufferByIndex(0, &b)); 209 | pkt.buffer = to(b); 210 | LONGLONG t = 0; 211 | if (SUCCEEDED(mfpkt->GetSampleTime(&t))) 212 | pkt.pts = from_mf_time(t); 213 | if (SUCCEEDED(mfpkt->GetSampleDuration(&t))) 214 | pkt.duration = from_mf_time(t); 215 | } 216 | 217 | const CLSID* codec_for(const std::string& name, MediaType type) 218 | { 219 | using codec_id_map = std::unordered_map; 220 | // https://gix.github.io/media-types 221 | // TODO: MediaInfo codec fourcc(index_sequence)? enum forcc::value, static_assert([4]==0) 222 | static const codec_id_map acodec_id{ 223 | {"ac3", &MFAudioFormat_Dolby_AC3}, 224 | {"eac3", &MFAudioFormat_Dolby_DDPlus}, 225 | {"aac", &MFAudioFormat_AAC}, 226 | {"mp3", &MFAudioFormat_MP3}, 227 | {"mp2", &MFAudioFormat_MPEG}, 228 | {"mp1", &MFAudioFormat_MPEG}, 229 | {"wmavoice", &MFAudioFormat_MSP1}, 230 | //{"wmav1", &MFAudioFormat_MSAUDIO1}, 231 | {"wmav2", &MFAudioFormat_WMAudioV8}, 232 | {"wmapro", &MFAudioFormat_WMAudioV9}, 233 | {"wmalossless", &MFAudioFormat_WMAudio_Lossless}, 234 | //{"flac", &MFAudioFormat_FLAC}, //win10+ 235 | //{"opus", &MFAudioFormat_Opus}, //win10+ 236 | // flac, alac, opus, amrnb/wb/wp, dts, msp1, qcelp 237 | }; 238 | static const codec_id_map vcodec_id{ 239 | {"h264", &MFVideoFormat_H264}, 240 | {"hevc", &MFVideoFormat_HEVC}, // MFVideoFormat_H265 'H265', MFVideoFormat_HEVC_ES , 'HEVS' 241 | {"vp8", &MFVideoFormat_VP80}, // 'MPG1' 242 | {"vp9", &MFVideoFormat_VP90}, // 'MPG1' 243 | {"mjpeg", &MFVideoFormat_MJPG}, 244 | {"mpeg2", &MFVideoFormat_MPEG2}, 245 | {"mpeg4", &MFVideoFormat_MP4V}, 246 | //{"msmpeg4v1", &MFVideoFormat_MP42}, // symbol not defined? 247 | //{"msmpeg4v2", &MFVideoFormat_MP42}, 248 | {"msmpeg4v3", &MFVideoFormat_MP43}, 249 | {"wmv1", &MFVideoFormat_WMV1}, 250 | {"wmv2", &MFVideoFormat_WMV2}, 251 | {"wmv3", &MFVideoFormat_WMV3}, 252 | {"vc1", &MFVideoFormat_WVC1}, 253 | {"av1", &MFVideoFormat_AV1}, 254 | }; 255 | static const codec_id_map codec_ids[] = {vcodec_id, acodec_id}; 256 | const auto& ids = codec_ids[std::underlying_type::type(type)]; 257 | const auto it = ids.find(name); 258 | if (it == ids.cend()) 259 | return nullptr; 260 | return it->second; 261 | } 262 | // IMFMediaBuffer 263 | // IMF2DBuffer: MFCreateDXSurfaceBuffer(IID_IDirect3DSurface9) https://docs.microsoft.com/en-us/windows/desktop/medfound/directx-surface-buffer 264 | // IMFDXGIBuffer+IMF2DBuffer(2)+IMFMediaBuffer : MFCreateDXGISurfaceBuffer(IID_ID3D11Texture2D) 265 | 266 | void dump(IMFAttributes* a) 267 | { 268 | UINT32 count = 0; 269 | MS_ENSURE(a->GetCount(&count)); 270 | std::stringstream ss; 271 | ss << count << " attributes: "; 272 | for (UINT32 i = 0; i < count; ++i) { 273 | GUID key; 274 | char detail[80] = {0}; 275 | MS_ENSURE(a->GetItemByIndex(i, &key, nullptr)); 276 | if (key == MF_MT_AUDIO_CHANNEL_MASK) { 277 | UINT32 v; 278 | MS_ENSURE(a->GetUINT32(key, &v)); 279 | std::snprintf(detail, sizeof(detail), " (0x%x)", (unsigned)v); 280 | } else if (key == MF_MT_FRAME_SIZE) { 281 | UINT32 w, h; 282 | MS_ENSURE(MFGetAttributeSize(a, MF_MT_FRAME_SIZE, &w, &h)); 283 | std::snprintf(detail, sizeof(detail), " (%dx%d)", (int)w, (int)h); 284 | } else if (key == MF_MT_PIXEL_ASPECT_RATIO || key == MF_MT_FRAME_RATE) { 285 | UINT32 num, den; 286 | MS_ENSURE(MFGetAttributeRatio(a, key, &num, &den)); 287 | std::snprintf(detail, sizeof(detail), " (%d:%d)", (int)num, (int)den); 288 | } 289 | ss << to_name(key) << "="; 290 | MF_ATTRIBUTE_TYPE type; 291 | MS_ENSURE(a->GetItemType(key, &type)); 292 | switch (type) { 293 | case MF_ATTRIBUTE_UINT32: { 294 | UINT32 v; 295 | MS_ENSURE(a->GetUINT32(key, &v)); 296 | ss << v; 297 | break; 298 | case MF_ATTRIBUTE_UINT64: { 299 | UINT64 v; 300 | MS_ENSURE(a->GetUINT64(key, &v)); 301 | ss << v; 302 | break; 303 | } 304 | case MF_ATTRIBUTE_DOUBLE: { 305 | DOUBLE v; 306 | MS_ENSURE(a->GetDouble(key, &v)); 307 | ss << v; 308 | break; 309 | } 310 | case MF_ATTRIBUTE_STRING: { 311 | wchar_t wv[512]; // being lazy here 312 | MS_ENSURE(a->GetString(key, wv, sizeof(wv), nullptr)); 313 | char cwv[512]{}; 314 | std::snprintf(cwv, sizeof(cwv), "%ls", wv); 315 | ss << cwv; 316 | break; 317 | } 318 | case MF_ATTRIBUTE_GUID: { 319 | GUID v; 320 | MS_ENSURE(a->GetGUID(key, &v)); 321 | ss << to_name(v); 322 | break; 323 | } 324 | case MF_ATTRIBUTE_BLOB: { 325 | UINT32 sz; 326 | MS_ENSURE(a->GetBlobSize(key, &sz)); 327 | ss << "(" << sz << ")"; 328 | std::vector buffer(sz, 0); 329 | MS_ENSURE(a->GetBlob(key, &buffer[0], (UINT32)buffer.size(), &sz)); 330 | ss << std::hex; 331 | for (const auto x : buffer) 332 | ss << " " << (int)x; 333 | ss << std::dec; 334 | break; 335 | } 336 | case MF_ATTRIBUTE_IUNKNOWN: 337 | default: 338 | ss << ""; 339 | break; 340 | } 341 | } 342 | ss << detail << ", "; 343 | } 344 | std::clog << ss.str() << std::endl; 345 | } 346 | } // namespace MF 347 | MDK_NS_END -------------------------------------------------------------------------------- /MFGlue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | #include "mdk/global.h" 12 | #include 13 | #include 14 | #include "MSUtils.h" 15 | #include "MFCompat.h" 16 | MDK_NS_BEGIN 17 | 18 | class Buffer; 19 | class Buffer2D; 20 | class Packet; 21 | class AudioFormat; 22 | class AudioFrame; 23 | class VideoFormat; 24 | class VideoFrame; 25 | struct VideoCodecParameters; 26 | struct AudioCodecParameters; 27 | enum class PixelFormat; 28 | class ColorSpace; 29 | struct HDRMetadata; 30 | 31 | namespace MF { 32 | // create wrapper buffer from IMF in limited offset and size, like MFCreateMediaBufferWrapper 33 | // offset is of type ptrdiff_t instead of int to avoid ambiguous overload 34 | // size <=0: use the whole size - offset 35 | std::shared_ptr to(ComPtr b, ptrdiff_t offset = 0, int size = -1); 36 | std::shared_ptr to(ComPtr b, ptrdiff_t offset = 0, int size = -1); 37 | 38 | // assum from/to source and target are not the same internal type(mem, mf, av buffer) 39 | // TODO: no mem allocation if alignment matches 40 | ComPtr from(std::shared_ptr buf, int align = 0); 41 | ComPtr from(std::shared_ptr buf); 42 | 43 | static inline LONGLONG to_mf_time(double s) 44 | { 45 | return LONGLONG(s*100000000.0); // 100ns 46 | } 47 | 48 | static inline double from_mf_time(LONGLONG ns_100) 49 | { 50 | return ns_100/100000000.0; 51 | } 52 | 53 | ComPtr from(const Packet& pkt, int align = 0); 54 | void to(Packet& pkt, ComPtr mfpkt); 55 | 56 | const CLSID* codec_for(const std::string& name, MediaType type); 57 | 58 | //AudioCodecParameters 59 | bool from(const AudioCodecParameters& par, IMFAttributes* a); 60 | 61 | bool to(AudioFormat& fmt, const IMFAttributes* a); 62 | bool from(const AudioFormat& fmt, IMFAttributes* a); 63 | 64 | bool to(AudioFrame& frame, ComPtr sample, bool copy = false); 65 | bool from(const AudioFrame& frame, ComPtr sample); 66 | 67 | bool from(const VideoCodecParameters& par, IMFAttributes* a); 68 | 69 | bool to(ColorSpace& cs, const IMFAttributes* a); 70 | bool from(const ColorSpace& cs, IMFAttributes* a); 71 | 72 | bool to(HDRMetadata& hdr, const IMFAttributes* a); 73 | bool from(const HDRMetadata& hdr, IMFAttributes* a); 74 | 75 | bool to(VideoFormat& fmt, const IMFAttributes* a); 76 | bool from(const VideoFormat& fmt, IMFAttributes* a); 77 | 78 | // frame format and size are not touched(no corresponding attributes in sample) 79 | // \param copy 80 | // 0: no copy if possible, i.e. hold d3d surface for d3d buffer, or add ref to sample buffers and lock to access for software decoder sample buffers(no d3d). 81 | // 1: lock sample buffer for d3d, and also copy data to frame planes for software decoder sample buffers 82 | // 2: lock sample buffer copy data to frame planes for d3d 83 | bool to(VideoFrame& frame, ComPtr sample, int strideX = 0, int strideY = 0, int copy = 0); 84 | bool from(const VideoFrame& frame, ComPtr sample); 85 | /* 86 | bool to(AudioFrame& frame, ComPtr mfmt); 87 | AVFrame* from(const AudioFrame& frame); 88 | bool to(VideoFormat& vf, AVPixelFormat fmt); 89 | AVPixelFormat from(const VideoFormat& vf); 90 | bool to(VideoFrame& frame, const AVFrame* avframe); 91 | AVFrame* from(const VideoFrame& frame); 92 | */ 93 | std::string to_string(const GUID& id); // uuid string, e.g. 8D2FD10B-5841-4a6b-8905-588FEC1ADED9 94 | std::string to_name(const GUID& id); 95 | void dump(IMFAttributes* a); 96 | } // namespace MF 97 | MDK_NS_END -------------------------------------------------------------------------------- /MFTAudioDecoder.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2018-2022 WangBin 4 | * This file is part of MDK MFT plugin 5 | * Source code: https://github.com/wang-bin/mdk-mft 6 | * 7 | * This Source Code Form is subject to the terms of the Mozilla Public 8 | * License, v. 2.0. If a copy of the MPL was not distributed with this 9 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 10 | */ 11 | #include "mdk/AudioDecoder.h" 12 | #include "mdk/MediaInfo.h" 13 | #include "mdk/Packet.h" 14 | #include "mdk/AudioFrame.h" 15 | #include "base/ms/MFTCodec.h" 16 | #include 17 | #if __has_include() // msvc 18 | # include 19 | #else // mingw 20 | # include 21 | #endif 22 | #include 23 | // properties: copy=0(0, 1, 2) 24 | 25 | MDK_NS_BEGIN 26 | using namespace std; 27 | class MFTAudioDecoder final : public AudioDecoder, protected MFTCodec 28 | { 29 | public: 30 | const char* name() const override {return "MFT";} 31 | bool open() override; 32 | bool close() override; 33 | bool flush() override { 34 | bool ret = flushCodec(); 35 | onFlush(); 36 | return ret; 37 | } 38 | bool decode(const Packet& pkt) override { return decodePacket(pkt); } 39 | private: 40 | void onPropertyChanged(const std::string& key, const std::string& value) override { 41 | if (key == "copy") 42 | copy_ = std::stoi(value); 43 | else if (key == "pool") 44 | useSamplePool(std::stoi(value)); 45 | } 46 | 47 | virtual bool setInputTypeAttributes(IMFAttributes* attr) override; 48 | virtual bool setOutputTypeAttributes(IMFAttributes* attr) override; 49 | virtual int getInputTypeScore(IMFAttributes* attr) override; 50 | virtual int getOutputTypeScore(IMFAttributes* attr) override; 51 | bool onOutputTypeChanged(DWORD streamId, ComPtr type) override; // width/height, pixel format, yuv mat, color primary/transfer func/chroma sitting/range, par 52 | bool onOutput(ComPtr sample) override; 53 | 54 | const CLSID* codec_id_ = nullptr; 55 | AudioFormat outfmt_; 56 | bool copy_ = false; 57 | }; 58 | 59 | bool MFTAudioDecoder::open() 60 | { 61 | copy_ = std::stoi(property("copy", "0")); 62 | 63 | const auto& par = parameters(); 64 | codec_id_ = MF::codec_for(par.codec, MediaType::Audio); 65 | if (!codec_id_) { 66 | std::clog << "codec is not supported: " << par.codec << std::endl; 67 | return false; 68 | } 69 | if (!openCodec(MediaType::Audio, *codec_id_, this)) 70 | return false; 71 | std::clog << this << "MFT decoder is ready" << std::endl; 72 | onOpen(); 73 | return true; 74 | } 75 | 76 | bool MFTAudioDecoder::close() 77 | { 78 | bool ret = closeCodec(); 79 | onClose(); 80 | return ret; 81 | } 82 | 83 | bool MFTAudioDecoder::setInputTypeAttributes(IMFAttributes* a) 84 | { 85 | return MF::from(parameters(), a); 86 | } 87 | 88 | bool MFTAudioDecoder::setOutputTypeAttributes(IMFAttributes* attr) 89 | { 90 | return true; 91 | } 92 | 93 | int MFTAudioDecoder::getInputTypeScore(IMFAttributes* attr) 94 | { 95 | GUID id; 96 | MS_ENSURE(attr->GetGUID(MF_MT_SUBTYPE, &id), -1); 97 | if (id != *codec_id_) // TODO: always same id because mft is activated from same codec id? aac can be aac or adts 98 | return -1; 99 | return 1; 100 | } 101 | 102 | int MFTAudioDecoder::getOutputTypeScore(IMFAttributes* attr) 103 | { 104 | GUID subtype; 105 | MS_ENSURE(attr->GetGUID(MF_MT_SUBTYPE, &subtype), -1); 106 | AudioFormat fmt; 107 | if (!MF::to(fmt, attr)) // TODO: closest channels, depth as option/property? e.g. dolby 108 | return -1; 109 | return 0; 110 | } 111 | 112 | bool MFTAudioDecoder::onOutputTypeChanged(DWORD streamId, ComPtr type) 113 | { 114 | ComPtr a; 115 | MS_ENSURE(type.As(&a), false); 116 | AudioFormat outfmt; 117 | if (!MF::to(outfmt, a.Get())) 118 | return false; 119 | std::clog << "output format: " << outfmt << std::endl; 120 | outfmt_ = outfmt; 121 | return true; 122 | } 123 | 124 | bool MFTAudioDecoder::onOutput(ComPtr sample) 125 | { 126 | AudioFrame frame(outfmt_); 127 | if (!MF::to(frame, sample, copy_)) 128 | return false; 129 | frameDecoded(frame); 130 | return true; 131 | } 132 | 133 | void register_audio_decoders_mf() { 134 | AudioDecoder::registerOnce("MFT", []{return new MFTAudioDecoder();}); 135 | } 136 | namespace { // DCE 137 | static const struct register_at_load_time_if_no_dce { 138 | inline register_at_load_time_if_no_dce() { register_audio_decoders_mf();} 139 | } s; 140 | } 141 | MDK_NS_END 142 | -------------------------------------------------------------------------------- /MFTCodec.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | // https://docs.microsoft.com/zh-cn/windows/uwp/audio-video-camera/supported-codecs 11 | # pragma push_macro("_WIN32_WINNT") 12 | # if _WIN32_WINNT < 0x0602 // _WIN32_WINNT_WIN7 is not defined yet. 0x0602: MFT_ENUM_HARDWARE_VENDOR_ID_Attribute 13 | # undef _WIN32_WINNT 14 | # define _WIN32_WINNT 0x0602 15 | # endif 16 | #include "MFTCodec.h" 17 | #include "base/scope_atexit.h" 18 | #include "base/ByteArrayBuffer.h" 19 | #include "base/fmt.h" 20 | #include "base/mpsc_fifo.h" 21 | #include "mdk/Property.h" 22 | #include 23 | #include 24 | #if __has_include() // msvc 25 | # include 26 | # include // MFT_FRIENDLY_NAME_Attribute 27 | #else // mingw 28 | # include 29 | # include // MFT_FRIENDLY_NAME_Attribute 30 | #endif 31 | # pragma pop_macro("_WIN32_WINNT") 32 | using namespace std; 33 | 34 | // TODO: async. see vlc 35 | // properties: activate=(index 0, 1, ...), pool=1(0, 1), in_type=index(or -1), out_type=index(or -1) 36 | // codec attributes: https://docs.microsoft.com/zh-cn/windows/win32/directshow/codec-api-properties 37 | 38 | MDK_NS_BEGIN 39 | #if (_MSC_VER + 0) // RuntimeClass is missing in mingw 40 | // used by SetAllocator, pool ref must be added in Tracked sample, so make it as IUnknown 41 | class MFTCodec::SamplePool final : public mpsc_fifo>, public RuntimeClass, IMFAsyncCallback> // IUnknown is implemented by RuntimeClass 42 | { 43 | public: 44 | HRESULT STDMETHODCALLTYPE GetParameters(DWORD *pdwFlags, DWORD *pdwQueue) override {return E_NOTIMPL;} 45 | HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult *pAsyncResult) override { 46 | IMFTrackedSample* s = nullptr; 47 | HRESULT hr = S_OK; 48 | MS_ENSURE((hr = pAsyncResult->GetState((IUnknown**)&s)), hr); 49 | push(std::move(s)); 50 | return hr; 51 | } 52 | }; 53 | #endif 54 | MFTCodec::MFTCodec() 55 | { 56 | #if (_MSC_VER + 0) 57 | pool_cb_ = Make(); 58 | #endif 59 | } 60 | 61 | MFTCodec::~MFTCodec() = default; 62 | 63 | bool MFTCodec::openCodec(MediaType mt, const CLSID& codec_id, const Property* prop) 64 | { 65 | useSamplePool(std::stoi(prop->get("pool", "1"))); 66 | activateAt(std::stoi(prop->get("activate", "0"))); 67 | setInputTypeIndex(std::stoi(prop->get("in_type", "-1"))); 68 | setOutputTypeIndex(std::stoi(prop->get("out_type", "-1"))); 69 | 70 | if (!createMFT(mt, codec_id)) 71 | return false; 72 | // TODO: unlock async 73 | 74 | DWORD nb_in = 0, nb_out = 0; 75 | MS_ENSURE(mft_->GetStreamCount(&nb_in, &nb_out), false); 76 | std::clog << "stream cout: in=" << nb_in << ", out=" << nb_out << std::endl; 77 | auto hr = mft_->GetStreamIDs(1, &id_in_, 1, &id_out_); 78 | if (hr == E_NOTIMPL) {// stream number is fixed and 0~n-1 79 | id_in_ = id_out_ = 0; 80 | } else if (FAILED(hr)) { 81 | std::clog << "failed to get stream ids" << std::endl; 82 | return false; 83 | } 84 | if (!setMediaTypes()) 85 | return false; 86 | MS_ENSURE(mft_->GetInputStreamInfo(id_in_, &info_in_), false); 87 | clog << fmt::to_string("input stream info: dwFlags=%u, cbSize=%u, cbAlignment=%u, hnsMaxLatency=%lld, cbMaxLookahead=%u", info_in_.dwFlags, info_in_.cbSize, info_in_.cbAlignment, info_in_.hnsMaxLatency, info_in_.cbMaxLookahead) << endl; 88 | MS_ENSURE(mft_->GetOutputStreamInfo(id_out_, &info_out_), false); 89 | clog << fmt::to_string("output stream info: dwFlags=%u, cbSize=%u, cbAlignment=%u", info_out_.dwFlags, info_out_.cbSize, info_out_.cbAlignment) << endl; 90 | ComPtr type; 91 | MS_ENSURE(mft_->GetOutputCurrentType(id_out_, &type), false); 92 | if (!onOutputTypeChanged(id_out_, type)) 93 | return false; 94 | // TODO: apply extra data here? 95 | MS_ENSURE(mft_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, ULONG_PTR()), false); // optional(After setting all media types, before ProcessInput). allocate resources(in the 1st ProcessInput if not sent). 96 | MS_ENSURE(mft_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, ULONG_PTR()), false); // required by async. start to process inputs 97 | warn_not_tracked_ = true; 98 | return true; 99 | } 100 | 101 | bool MFTCodec::closeCodec() 102 | { 103 | destroyMFT(); 104 | return true; 105 | } 106 | 107 | bool MFTCodec::createMFT(MediaType mt, const CLSID& codec_id) 108 | { 109 | uninit_com_ = CoInitializeEx(nullptr, COINIT_MULTITHREADED) != RPC_E_CHANGED_MODE; // TODO: class 110 | std::clog << "uninit com required for MFT: " << uninit_com_ << std::endl; 111 | MS_ENSURE(MFStartup(MF_VERSION), false); 112 | static const CLSID kMajorType[] = { 113 | MFMediaType_Video, 114 | MFMediaType_Audio, 115 | }; 116 | MFT_REGISTER_TYPE_INFO reg{kMajorType[std::underlying_type_t(mt)], codec_id}; 117 | UINT32 flags = 0;// 118 | // MFT_ENUM_FLAG_HARDWARE | // MUST be async. intel mjpeg decoder 119 | // MFT_ENUM_FLAG_SYNCMFT | 120 | // MFT_ENUM_FLAG_LOCALMFT | 121 | // MFT_ENUM_FLAG_SORTANDFILTER; // TODO: vlc flags. default 0: MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER 122 | // MFT_ENUM_FLAG_HARDWARE implies MFT_ENUM_FLAG_ASYNCMFT. usually with MFT_ENUM_FLAG_TRANSCODE_ONLY, and GetStreamIDs error 123 | IMFActivate **activates = nullptr; 124 | UINT32 nb_activates = 0; 125 | CLSID *pCLSIDs = nullptr; // for MFTEnum() ShutdownObject()); // required by some. no effect if not 130 | activates[i]->Release(); 131 | } 132 | } 133 | CoTaskMemFree(activates); 134 | CoTaskMemFree(pCLSIDs); 135 | }); 136 | const GUID kCategory[] { 137 | MFT_CATEGORY_VIDEO_DECODER, 138 | MFT_CATEGORY_AUDIO_DECODER, 139 | MFT_CATEGORY_VIDEO_ENCODER, 140 | MFT_CATEGORY_AUDIO_ENCODER, 141 | MFT_CATEGORY_VIDEO_EFFECT, 142 | MFT_CATEGORY_AUDIO_EFFECT, 143 | }; 144 | const auto category = kCategory[std::underlying_type_t(mt)]; 145 | // optional KSCATEGORY_DATADECOMPRESSOR for hw 146 | #if !(MS_WINRT+0) 147 | // TODO: MFTGetInfo() to get in/out info 148 | typedef HRESULT (STDAPICALLTYPE *MFTEnumEx_fn)(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*, IMFActivate***, UINT32*); 149 | MFTEnumEx_fn MFTEnumEx = nullptr; 150 | HMODULE mfplat_dll = GetModuleHandleW(L"mfplat.dll"); 151 | if (mfplat_dll) 152 | MFTEnumEx = (MFTEnumEx_fn)GetProcAddress(mfplat_dll, "MFTEnumEx"); 153 | if (!MFTEnumEx) {// vista 154 | MS_ENSURE(MFTEnum(category, flags, ®, nullptr, nullptr, &pCLSIDs, &nb_activates), false); 155 | std::clog << nb_activates << " MFT class ids found." << std::endl; 156 | } else 157 | #endif 158 | { 159 | MS_ENSURE(MFTEnumEx(category, flags, ®, nullptr, &activates, &nb_activates), false); 160 | std::clog << nb_activates << " MFT class activates found" << std::endl; 161 | 162 | } 163 | if (nb_activates == 0) 164 | return false; 165 | for (int i = 0; i < (int)nb_activates; ++i) { 166 | if (i != activate_index_ && activate_index_ >= 0) 167 | continue; 168 | if (i > activate_index_ && activate_index_ >= 0) 169 | break; 170 | if (activates) { 171 | ComPtr aa; 172 | ComPtr act(activates[i]); 173 | MS_ENSURE(act.As(&aa), false); 174 | std::clog << "IMFActivate[" << i << "] attributes:" << std::endl; 175 | MF::dump(aa.Get()); 176 | wchar_t name[512]{}; 177 | if (SUCCEEDED(activates[i]->GetString(MFT_FRIENDLY_NAME_Attribute, name, sizeof(name), nullptr))) // win7 attribute 178 | std::clog << fmt::to_string("Activating IMFActivate: %ls", name) << std::endl; 179 | MS_WARN(activates[i]->ActivateObject(IID_IMFTransform, &mft_)); // __uuidof(IMFTransform), IID_PPV_ARGS(&mft) 180 | } else { 181 | #if !(MS_WINRT+0) 182 | MS_WARN(CoCreateInstance(pCLSIDs[i], nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&mft_))); 183 | #endif 184 | } 185 | if (!mft_.Get()) 186 | { 187 | ComPtr attr; 188 | if (SUCCEEDED(mft_->GetAttributes(&attr))) { 189 | UINT32 bAsync = 0; // MFGetAttributeUINT32 190 | if (SUCCEEDED(attr->GetUINT32(MF_TRANSFORM_ASYNC, &bAsync)) && bAsync) { // only iff MFT_ENUM_FLAG_HARDWARE is explicitly set 191 | std::clog << "Async mft is not supported yet" << std::endl; 192 | continue; 193 | } 194 | } 195 | } 196 | 197 | if (onMFTCreated(mft_)) 198 | break; 199 | mft_.Reset(); 200 | break; 201 | } 202 | // https://docs.microsoft.com/zh-cn/windows/desktop/medfound/supporting-direct3d-11-video-decoding-in-media-foundation#allocating-uncompressed-buffers 203 | if (mft_) { 204 | ComPtr a; 205 | if (SUCCEEDED(mft_->GetAttributes(&a))) { 206 | wchar_t vendor[128]{}; // set vendor id? 207 | if (SUCCEEDED(a->GetString(MFT_ENUM_HARDWARE_VENDOR_ID_Attribute, vendor, sizeof(vendor), nullptr))) // win8+, so warn only 208 | clog << fmt::to_string("hw vendor id: %ls", vendor) << endl; 209 | if (SUCCEEDED(a->GetString(MFT_ENUM_HARDWARE_URL_Attribute, vendor, sizeof(vendor), nullptr))) // win8+, so warn only 210 | clog << fmt::to_string("hw url: %ls", vendor) << endl; 211 | // TODO: what about using eventgenerator anyway 212 | // async requires: IMFMediaEventGenerator, IMFShutdown 213 | //MS_WARN(attr->SetUINT32(MF_TRANSFORM_ASYNC, TRUE)); 214 | //MS_WARN(attr->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE)); 215 | // TODO: MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE must be true for async 216 | std::clog << "Selected MFT attributes:" << std::endl; 217 | MF::dump(a.Get()); 218 | } 219 | } 220 | return !!mft_.Get(); 221 | } 222 | 223 | bool MFTCodec::destroyMFT() 224 | { 225 | #if (_MSC_VER + 0) // missing in mingw 226 | ComPtr shutdown; 227 | if (mft_ && SUCCEEDED(mft_.As(&shutdown))) // async MFT only 228 | MS_WARN(shutdown->Shutdown()); 229 | #endif // (_MSC_VER + 0) 230 | // TODO: affect other mft/com components? shared? 231 | mft_.Reset(); // reset before shutdown. otherwise crash 232 | MS_WARN(MFShutdown()); 233 | if (uninit_com_) 234 | CoUninitialize(); 235 | return true; 236 | } 237 | 238 | using GetAvailableType = std::function; 239 | using GetScore = std::function; // return 0(the same value) to get the 1st one, for MFT_DECODER_EXPOSE_OUTPUT_TYPES_IN_NATIVE_ORDER 240 | // for in/out audio/video dec/enc 241 | ComPtr SelectType(DWORD stream_id, GetAvailableType getAvail, GetScore getScore, int idx, bool* later) 242 | { 243 | *later = false; 244 | ComPtr type; 245 | ComPtr tmp; 246 | int index = -1; 247 | int score = -1; 248 | HRESULT hr = S_OK; 249 | for (int i = 0; ; i++) { 250 | hr = getAvail(stream_id, i, &tmp); 251 | if (hr == MF_E_NO_MORE_TYPES) 252 | return type; 253 | if (FAILED(hr)) 254 | MS_WARN(hr); 255 | if (hr == E_NOTIMPL) // An MFT is not required to implement GetInputAvailableType (simple type) 256 | return nullptr; 257 | if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { 258 | *later = true; 259 | return nullptr; // ? 260 | } 261 | if (FAILED(hr)) // skip MFCreateMediaType? 262 | return nullptr; 263 | ComPtr attr; 264 | MS_ENSURE(tmp.As(&attr), nullptr); 265 | MF::dump(attr.Get()); 266 | int new_score = getScore(attr.Get()); 267 | if (idx >= 0) { 268 | if (idx != i) 269 | continue; 270 | } else { 271 | if (new_score <= score) 272 | continue; 273 | } 274 | score = new_score; 275 | type = tmp; 276 | index = i; 277 | } 278 | std::clog << "selected IMediaType index: " << index << std::endl; 279 | return type; 280 | } 281 | 282 | ComPtr MFTCodec::selectInputType(DWORD stream_id, bool* later) 283 | { 284 | std::clog << __FUNCTION__ << std::endl; 285 | auto type = SelectType(stream_id, [this](DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType){ 286 | return mft_->GetInputAvailableType(dwOutputStreamID, dwTypeIndex, ppType); 287 | }, [this](IMFAttributes* a){ 288 | return getInputTypeScore(a); 289 | }, in_type_idx_, later); // optional 290 | if (*later) { 291 | std::clog << "at least 1 output type must be set first" << std::endl; 292 | return nullptr; 293 | } 294 | if (!type) { 295 | std::clog << "GetInputAvailableType is not implemented or failed, try to create IMediaType manually" << std::endl; 296 | MS_ENSURE(MFCreateMediaType(&type), nullptr); 297 | } 298 | ComPtr a; 299 | MS_ENSURE(type.As(&a), nullptr); 300 | if (!setInputTypeAttributes(a.Get())) 301 | return nullptr; 302 | std::clog << "SetInputType:" << std::endl; 303 | MS_ENSURE(type.As(&a), nullptr); 304 | MF::dump(a.Get()); 305 | DWORD flags = 0; // MFT_SET_TYPE_TEST_ONLY 306 | HRESULT hr = S_OK; 307 | MS_WARN((hr = mft_->SetInputType(stream_id, type.Get(), flags))); // set null to clear 308 | // TODO: will it return MF_E_TRANSFORM_TYPE_NOT_SET if GetInputAvailableType did not? 309 | if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) 310 | *later = true; 311 | if (FAILED(hr)) 312 | return nullptr; 313 | std::clog << "used input type: " << std::endl; 314 | MS_ENSURE(mft_->GetInputCurrentType(stream_id, &type), nullptr); 315 | MS_ENSURE(type.As(&a), nullptr); 316 | MF::dump(a.Get()); 317 | return type; 318 | } 319 | 320 | ComPtr MFTCodec::selectOutputType(DWORD stream_id, bool* later) 321 | { 322 | std::clog << __FUNCTION__ << std::endl; 323 | auto type = SelectType(stream_id, [this](DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType){ 324 | return mft_->GetOutputAvailableType(dwOutputStreamID, dwTypeIndex, ppType); 325 | }, [this](IMFAttributes* a){ 326 | return getOutputTypeScore(a); 327 | }, out_type_idx_, later); // optional 328 | if (*later) { 329 | std::clog << "at least 1 input type must be set first" << std::endl; 330 | return nullptr; 331 | } 332 | if (!type) { 333 | std::clog << "GetOutputAvailableType is not implemented or failed, try to create IMediaType manually" << std::endl; 334 | MS_ENSURE(MFCreateMediaType(&type), nullptr); 335 | } 336 | ComPtr a; 337 | MS_ENSURE(type.As(&a), nullptr); 338 | if (!setOutputTypeAttributes(a.Get())) 339 | return nullptr; 340 | std::clog << "SetOutputType:" << std::endl; 341 | MS_ENSURE(type.As(&a), nullptr); 342 | MF::dump(a.Get()); 343 | DWORD flags = 0; // MFT_SET_TYPE_TEST_ONLY 344 | HRESULT hr = S_OK; 345 | MS_WARN((hr = mft_->SetOutputType(stream_id, type.Get(), flags))); // set null to clear 346 | // TODO: will it return MF_E_TRANSFORM_TYPE_NOT_SET if GetOutputAvailableType did not? 347 | if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) 348 | *later = true; 349 | if (FAILED(hr)) 350 | return nullptr; 351 | std::clog << "used output type: " << std::endl; 352 | MS_ENSURE(mft_->GetOutputCurrentType(stream_id, &type), nullptr); 353 | MS_ENSURE(type.As(&a), nullptr); 354 | clog << (void*)a.Get() << " "; 355 | MF::dump(a.Get()); 356 | return type; 357 | } 358 | 359 | // https://docs.microsoft.com/zh-cn/windows/desktop/medfound/basic-mft-processing-model#set-media-types 360 | bool MFTCodec::setMediaTypes() 361 | { 362 | ComPtr a; 363 | bool in_later = false; 364 | if (!selectInputType(id_in_, &in_later) && !in_later) 365 | return false; 366 | bool out_later = false; 367 | if (!selectOutputType(id_out_, &out_later)) 368 | return false; 369 | if (!in_later) 370 | return true; 371 | if (!selectInputType(id_in_, &in_later)) 372 | return false; 373 | return true; 374 | } 375 | 376 | bool MFTCodec::flushCodec() 377 | { // https://docs.microsoft.com/zh-cn/windows/desktop/medfound/basic-mft-processing-model#flushing-an-mft 378 | MS_ENSURE(mft_->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, ULONG_PTR()), false); 379 | discontinuity_ = true; // TODO: 380 | // TODO: async mft does not send another METransformNeedInput event until it receives an MFT_MESSAGE_NOTIFY_START_OF_STREAM message from the client 381 | // https://docs.microsoft.com/zh-cn/windows/desktop/medfound/mft-message-command-flush 382 | return true; 383 | } 384 | 385 | bool MFTCodec::decodePacket(const Packet& pkt) 386 | { 387 | if (pkt.isEnd()) { 388 | MS_ENSURE(mft_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, ULONG_PTR()), false); // not necessary 389 | // TODO: async must send START_OF_STREAM later to accept inputs. https://docs.microsoft.com/zh-cn/windows/desktop/medfound/mft-message-command-drain 390 | // when, how: https://docs.microsoft.com/zh-cn/windows/desktop/medfound/basic-mft-processing-model#draining-an-mft 391 | MS_ENSURE(mft_->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, ULONG_PTR()), false); 392 | while (processOutput()) {} 393 | return false; 394 | } 395 | size_t size = pkt.buffer->size(); 396 | Packet filtered = pkt; 397 | if (pkt.buffer && pkt.buffer->constData()) 398 | filtered.buffer = filter(pkt.buffer); 399 | ComPtr sample = MF::from(filtered, info_in_.cbAlignment); 400 | if (!sample) 401 | return false; 402 | if (discontinuity_) { 403 | discontinuity_ = false; 404 | ComPtr attr; 405 | if (SUCCEEDED(sample.As(&attr))) 406 | attr->SetUINT32(MFSampleExtension_Discontinuity, 1); 407 | } 408 | auto hr = mft_->ProcessInput(id_in_, sample.Get(), 0); 409 | if (hr == MF_E_NOTACCEPTING) { // MUST be in 1 state: accept more input, produce more output 410 | while (processOutput()) {} 411 | MS_ENSURE(mft_->ProcessInput(id_in_, sample.Get(), 0), false); 412 | } else if (FAILED(hr)) { // MFT can drop it 413 | std::clog << "ProcessInput error: " << hr << std::endl; 414 | discontinuity_ = true; 415 | MS_ENSURE(hr, false); 416 | return true; 417 | } 418 | while (processOutput()) {} // get output ASAP. https://docs.microsoft.com/zh-cn/windows/desktop/medfound/basic-mft-processing-model#process-data 419 | if (!(info_in_.dwFlags & MFT_INPUT_STREAM_DOES_NOT_ADDREF)) {// ProcessInput() may but not always hold a reference count on the input samples 420 | // mft bug? ProcessInput() may release the sample and MFCreateSample may reuse last one(not IMFTrackedSample) even if old sample is not released(keep by user) 421 | } 422 | return !pkt.isEnd(); 423 | } 424 | 425 | ComPtr MFTCodec::getOutSample() // getUncompressedSample() 426 | { 427 | auto set_sample_buffers = [this](IMFSample* sample){ 428 | ComPtr buf; 429 | const auto align = std::max(16, info_out_.cbAlignment); 430 | MS_ENSURE(MFCreateAlignedMemoryBuffer(info_out_.cbSize, align - 1, &buf)); 431 | sample->AddBuffer(buf.Get()); 432 | }; 433 | ComPtr sample; 434 | #if !(MS_WINRT+0) 435 | typedef HRESULT (STDAPICALLTYPE *MFCreateTrackedSample_fn)(IMFTrackedSample**); 436 | static HMODULE mfplat_dll = GetModuleHandleW(L"mfplat.dll"); 437 | static auto MFCreateTrackedSample = (MFCreateTrackedSample_fn)GetProcAddress(mfplat_dll, "MFCreateTrackedSample"); // win8, phone8.1 438 | if (!MFCreateTrackedSample && use_pool_) { 439 | use_pool_ = false; 440 | std::clog << "MFCreateTrackedSample is not found in mfplat.dll. can not use IMFTrackedSample to reduce copy" << std::endl; 441 | } 442 | #endif 443 | if (!use_pool_ || !pool_cb_) { 444 | MS_ENSURE(MFCreateSample(&sample), nullptr); 445 | set_sample_buffers(sample.Get()); 446 | return sample; 447 | } 448 | #if (_MSC_VER + 0) 449 | ComPtr ts; 450 | if (!getPool()->pop(&ts)) { 451 | std::clog << this << " no sample in pool. create one" << std::endl; 452 | MS_ENSURE(MFCreateTrackedSample(&ts), nullptr); 453 | MS_ENSURE(ts.As(&sample), nullptr); 454 | set_sample_buffers(sample.Get()); 455 | } 456 | ts->SetAllocator(pool_cb_.Get(), ts.Get()); // callback is cleared after invoke() 457 | MS_ENSURE(ts.As(&sample), nullptr); 458 | #endif // (_MSC_VER + 0) 459 | return sample; 460 | } 461 | 462 | bool MFTCodec::processOutput() 463 | { 464 | ComPtr sample; 465 | const bool kProvidesSample = !!(info_out_.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES|MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)); 466 | if (!kProvidesSample) // sw dec. if mft can provides samples but we want to use our samples, we must create correct sample type to be used by mft(e.g. d3d surface sample) 467 | sample = getOutSample(); 468 | MFT_OUTPUT_DATA_BUFFER out{}; 469 | out.dwStreamID = id_out_; 470 | out.pSample = sample.Get(); // nullptr: allocated by MFT if no flag MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER and can provide samples 471 | out.dwStatus = 0; // set by MFT 472 | out.pEvents = nullptr; // set by MFT 473 | DWORD status = 0; 474 | auto hr = mft_->ProcessOutput(0, 1, &out, &status); 475 | if (out.pEvents) // in-band(sync mft) events from ProcessEvent() 476 | out.pEvents->Release(); 477 | // MFCreateVideoSampleFromSurface. additional ref is added to safe reuse the sample. https://msdn.microsoft.com/en-us/library/windows/desktop/ms697026(v=vs.85).aspx 478 | // https://docs.microsoft.com/zh-cn/windows/desktop/medfound/supporting-dxva-2-0-in-media-foundation#decoding 479 | // https://docs.microsoft.com/zh-cn/windows/desktop/medfound/supporting-direct3d-11-video-decoding-in-media-foundation#decoding 480 | ComPtr tracked; // d3d11 or dxva2 provided by mft. or provided by our pool. Video samples created by the MFCreateVideoSampleFromSurface function expose this interface: https://docs.microsoft.com/en-us/windows/win32/medfound/video-samples 481 | // if not provided by mft and need more input, out.pSample is null 482 | //clog << "kProvidesSample: " << kProvidesSample << ", sample: " << (void*)sample.Get() << ", out.pSample: " << (void*)out.pSample << endl << flush; 483 | if (out.pSample && ( 484 | SUCCEEDED(out.pSample->QueryInterface(IID_PPV_ARGS(&tracked))) 485 | || !sample.Get() // can not assume d3d11/dxva is a IMFTrackedSample(win10 2021 HEVCVideoExtensions, intel/nvidia), but output sample must be tracked internally, so Attach to it. sample is always null here for dxva2/d3d11 486 | )) { 487 | if (warn_not_tracked_ && !tracked) 488 | clog << "Not a IMFTrackedSample" << endl; 489 | warn_not_tracked_ = false; 490 | sample.Attach(out.pSample); // provided by mft or pool. DO NOT Release() pSample here. Otherwise TrackedSample callback is called and sample is recycled. 491 | #ifdef __MINGW32__ // mingw adds ref in attach() https://sourceforge.net/p/mingw-w64/discussion/723797/thread/616a8df0ee . TODO: version check if fixed in mingw 492 | sample->Release(); 493 | #endif 494 | } 495 | if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) 496 | return false; 497 | // dwStatus is not bit flags. https://docs.microsoft.com/en-us/windows/desktop/api/mftransform/nf-mftransform-imftransform-processoutput#stream-changes 498 | if (out.dwStatus & MFT_OUTPUT_DATA_BUFFER_STREAM_END) { // stream is deleted, not eof. stream info flag must have MFT_OUTPUT_STREAM_REMOVABLE 499 | std::clog << "MFT_OUTPUT_DATA_BUFFER_STREAM_END" << std::endl; 500 | } else if (out.dwStatus & MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS) { 501 | std::clog << "MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS" << std::endl; 502 | } else if (out.dwStatus & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) { 503 | std::clog << "MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE" << std::endl; 504 | } else if (out.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) { 505 | std::clog << "MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE" << std::endl; 506 | } 507 | if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { // TODO: status == MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS? 508 | std::clog << "MF_E_TRANSFORM_STREAM_CHANGE" << std::endl; 509 | sample.Reset(); // recycle if tracked 510 | #if (_MSC_VER + 0) 511 | getPool()->clear(); // different buffer parameters. FIXME: how to clear all samples outside the pool? double pool and swap? 512 | #endif 513 | // TODO: GetStreamIDs() again? https://docs.microsoft.com/zh-cn/windows/desktop/api/mftransform/ne-mftransform-_mft_process_output_status 514 | bool later = false; 515 | if (!selectOutputType(id_out_, &later)) 516 | return false; 517 | MS_ENSURE(mft_->GetOutputStreamInfo(id_out_, &info_out_), false); 518 | clog << fmt::to_string("output stream info: dwFlags=%u, cbSize=%u, cbAlignment=%u", info_out_.dwFlags, info_out_.cbSize, info_out_.cbAlignment) << endl; 519 | ComPtr type; 520 | MS_ENSURE(mft_->GetOutputCurrentType(id_out_, &type), false); 521 | if (!onOutputTypeChanged(id_out_, type)) // TODO: dump attribute 522 | return false; 523 | return true; 524 | } 525 | if (FAILED(hr)) { 526 | MS_WARN(hr); 527 | if (use_pool_) { // h264? hevc works 528 | std::clog << "FIXME: ProcessOutput error and may be caused by output sample pool. Disable to workaround for now." << std::endl; 529 | use_pool_ = false; 530 | } 531 | return false; 532 | } 533 | if (!sample) { 534 | clog << "null output sample" << endl; 535 | return true; 536 | } 537 | return onOutput(sample); 538 | } 539 | MDK_NS_END -------------------------------------------------------------------------------- /MFTCodec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | #pragma once 11 | #include "mdk/Packet.h" 12 | #include "MFGlue.h" 13 | #include 14 | 15 | MDK_NS_BEGIN 16 | class MDK_NOVTBL MFTCodec 17 | { 18 | protected: 19 | MFTCodec(); 20 | virtual ~MFTCodec(); 21 | // enable (unbounded) sample pool. Use a pool to reduce sample and buffer allocation frequency because a buffer(video) can be in large size. 22 | void useSamplePool(bool value = true) { use_pool_ = value; } // setPoolSamples(int size = -1) 23 | void activateAt(int value) { activate_index_ = value; } 24 | bool openCodec(MediaType type, const CLSID& codec_id, const Property* prop); 25 | bool closeCodec(); 26 | bool flushCodec(); 27 | bool decodePacket(const Packet& pkt); 28 | void setInputTypeIndex(int index = -1) { 29 | in_type_idx_ = index; 30 | } 31 | void setOutputTypeIndex(int index = -1) { 32 | out_type_idx_ = index; 33 | } 34 | private: 35 | bool createMFT(MediaType type, const CLSID& codec_id); 36 | virtual bool onMFTCreated(ComPtr /*mft*/) {return true;} 37 | bool destroyMFT(); 38 | bool setMediaTypes(); 39 | // bitstream/packet filter 40 | // return nullptr if filter in place, otherwise allocated data is returned and size is modified. no need to free the data 41 | virtual BufferRef filter(BufferRef in) {return in;} 42 | virtual bool setInputTypeAttributes(IMFAttributes*) {return true;} 43 | virtual bool setOutputTypeAttributes(IMFAttributes*) {return true;} 44 | virtual int getInputTypeScore(IMFAttributes*) {return -1;} 45 | virtual int getOutputTypeScore(IMFAttributes*) {return -1;} 46 | virtual bool onOutputTypeChanged(DWORD streamId, ComPtr type) = 0; 47 | virtual bool onOutput(ComPtr sample) = 0; 48 | ComPtr getOutSample(); // get an output sample from pool, or create directly. 49 | // processInput(data, size); 50 | bool processOutput(); 51 | virtual ComPtr selectInputType(DWORD stream_id, bool* later); 52 | virtual ComPtr selectOutputType(DWORD stream_id, bool* later); 53 | protected: 54 | ComPtr mft_; 55 | private: 56 | class SamplePool; 57 | SamplePool* getPool() {return reinterpret_cast(pool_cb_.Get());} 58 | 59 | bool uninit_com_ = false; 60 | bool use_pool_ = true; 61 | bool discontinuity_ = false; 62 | bool warn_not_tracked_ = true; 63 | int activate_index_ = -1; 64 | DWORD id_in_ = 0; 65 | DWORD id_out_ = 0; 66 | MFT_INPUT_STREAM_INFO info_in_; 67 | MFT_OUTPUT_STREAM_INFO info_out_; 68 | int in_type_idx_ = -1; 69 | int out_type_idx_ = -1; 70 | 71 | using SamplePoolRef = std::shared_ptr; 72 | ComPtr pool_cb_; // double-pool for stream(parameter) change to clear samples outside the pool? 73 | }; 74 | MDK_NS_END 75 | -------------------------------------------------------------------------------- /MFTVideoDecoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | // https://searchfox.org/mozilla-central/source/dom/media/platforms/wmf 11 | /* 12 | MF_TRANSFORM_ASYNC_UNLOCK: MUST set for async (set by the whole MF pipeline) 13 | MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE: (Get)always true for async 14 | 15 | vista: sync, like mediacodec 16 | win7+: async (event). METransformNeedInput...: desktop only 17 | 18 | uwp: https://www.eternalcoding.com/?p=413 19 | 20 | https://docs.microsoft.com/zh-cn/uwp/win32-and-com/win32-apis 21 | https://docs.microsoft.com/zh-cn/uwp/win32-and-com/alternatives-to-windows-apis-uwp 22 | https://docs.microsoft.com/zh-cn/previous-versions/windows/mt592899%28v%3dwin.10%29 23 | https://docs.microsoft.com/en-us/uwp/api/windows.media 24 | 25 | https://docs.microsoft.com/zh-cn/windows/desktop/medfound/uncompressed-audio-media-types 26 | https://docs.microsoft.com/zh-cn/windows/desktop/medfound/uncompressed-video-media-types 27 | 28 | BUG: 29 | - (intel HD 520) hevc main10 d3d11 decoding blocks(via store extension), works again after seek. (ffmpeg d3d11/dxva works fine). gpu render core is used instead of gpu decoder. 30 | 31 | Compare with FFmpeg D3D11/DXVA: 32 | - (intel HD 520) MFT supports gpu decoding for hevc but ffmpeg d3d11 does not, instead it use render core(hybrid mode?) 33 | - (intel HD 520) ffmpeg d3d11 hevc wrong output size and color. because of hybrid decoding? 34 | - (intel HD 520) MFT may failed to set dxva device manager for hevc main10, but ffmpeg dxva works? 35 | */ 36 | //#ifdef _MSC_VER 37 | # pragma push_macro("_WIN32_WINNT") 38 | # if _WIN32_WINNT < 0x0602 // for d3d11 etc. _WIN32_WINNT_WIN8 is not defined yet 39 | # undef _WIN32_WINNT 40 | # define _WIN32_WINNT 0x0602 41 | # endif 42 | #include "mdk/VideoDecoder.h" 43 | #include "mdk/MediaInfo.h" 44 | #include "mdk/Packet.h" 45 | #include "mdk/VideoFrame.h" 46 | #include "NAL.h" 47 | #include "base/ByteArrayBuffer.h" 48 | #include "base/fmt.h" 49 | #include "base/ms/MFTCodec.h" 50 | #include "video/d3d/D3D9Utils.h" 51 | #include "video/d3d/D3D11Utils.h" 52 | #include 53 | #if __has_include() // msvc 54 | # include 55 | #else // mingw 56 | # include 57 | #endif 58 | #include 59 | #include 60 | //#ifdef _MSC_VER 61 | # pragma pop_macro("_WIN32_WINNT") 62 | 63 | /* 64 | properties: 65 | d3d=0(0, 9, 11), copy=0(0, 1, 2), adapter=0, low_latency=0(0,1), ignore_profile=0(0,1), ignore_level=0(0,1) 66 | shader_resource=0(0,1),shared=1, nthandle=0, kmt=0 67 | feature_level=12.1(9.1,9.2,1.3,10.0,10.1,11.0,11.1,12.0,12.1), blacklist=mpeg4 68 | software decoder properties: 69 | priority=-2(lowest), -1(below normal), 0(normal), 1(above normal), 2(highest) 70 | threads=0(default, number of concurrent threads supported), N 71 | fast=0: normal, 1: Optimal Loop Filter, 2: Disable Loop Filter, ..., 32: fastest 72 | power=0: Optimize for battery life, 50: balanced, 100: Optimize for video quality. 0~100 73 | deinterlace=0: no, 1: progressive, 2: bob, 3: smart bob 74 | TODO: property device=global 75 | */ 76 | MDK_NS_BEGIN 77 | using namespace std; 78 | class MFTVideoDecoder final : public VideoDecoder, protected MFTCodec 79 | { 80 | public: 81 | const char* name() const override {return "MFT";} 82 | bool open() override; 83 | bool close() override; 84 | bool flush() override { 85 | bool ret = flushCodec(); 86 | onFlush(); 87 | return ret; 88 | } 89 | int decode(const Packet& pkt) override { return decodePacket(pkt); } 90 | private: 91 | void onPropertyChanged(const std::string& key, const std::string& value) override { 92 | VideoDecoder::onPropertyChanged(key, value); 93 | if (key == "copy") 94 | copy_ = std::stoi(value); 95 | else if (key == "pool") 96 | useSamplePool(std::stoi(value)); 97 | } 98 | 99 | bool onMFTCreated(ComPtr mft) override; 100 | bool testConstraints(ComPtr mft); 101 | BufferRef filter(BufferRef in) override; 102 | 103 | bool setInputTypeAttributes(IMFAttributes* attr) override; 104 | bool setOutputTypeAttributes(IMFAttributes* attr) override; 105 | int getInputTypeScore(IMFAttributes* attr) override; 106 | int getOutputTypeScore(IMFAttributes* attr) override; 107 | bool onOutputTypeChanged(DWORD streamId, ComPtr type) override; // width/height, pixel format, yuv mat, color primary/transfer func/chroma sitting/range, par 108 | bool onOutput(ComPtr sample) override; 109 | 110 | // properties 111 | int copy_ = 0; 112 | int use_d3d_ = 0; 113 | VideoFormat force_fmt_; 114 | 115 | const CLSID* codec_id_ = nullptr; 116 | int nal_size_ = 0; 117 | int csd_size_ = 0; 118 | uint8_t* csd_ = nullptr; 119 | bool csd_sent_ = false; 120 | VideoFrame frame_param_; 121 | UINT32 stride_x_ = 0; 122 | UINT32 stride_y_ = 0; 123 | #if (MS_API_DESKTOP+0) 124 | D3D9::Manager mgr9_; 125 | #endif 126 | D3D11::Manager mgr11_; 127 | NativeVideoBufferPoolRef pool_; 128 | }; 129 | 130 | bool MFTVideoDecoder::open() 131 | { 132 | copy_ = std::stoi(property("copy", "0")); 133 | force_fmt_ = VideoFormat::fromName(property("format", "unknown").data()); 134 | 135 | const auto blacklist = property("blacklist", "mpeg4"); 136 | const auto& par = parameters(); 137 | if (blacklist.find(par.codec) != string::npos) { 138 | clog << par.codec << " is in blacklist" << endl; 139 | return false; 140 | } 141 | codec_id_ = MF::codec_for(par.codec, MediaType::Video); 142 | if (!codec_id_) { 143 | std::clog << "codec is not supported: " << par.codec << std::endl; 144 | return false; 145 | } 146 | // https://docs.microsoft.com/en-us/windows/desktop/medfound/h-264-video-decoder#format-constraints 147 | // TODO: other codecs 148 | if (*codec_id_ == MFVideoFormat_H264) { 149 | if (par.profile > 100 150 | && par.profile != ((1<<9)|66) // constrained baseline 151 | && !std::stoi(property("ignore_profile", "0"))) { // TODO: property to ignore profile and level 152 | std::clog << "H264 profile is not supported by MFT. Max is High(100). set property 'ignore_profile=1' to ignore profile restriction." << std::endl; 153 | return false; 154 | } 155 | if (par.level > 51 && !std::stoi(property("ignore_level", "0"))) { 156 | std::clog << "H264 level is not supported by MFT. Max is 5.1. set property 'ignore_level=1' to ignore profile restriction" << std::endl; 157 | return false; 158 | } 159 | // chroma subsample? 160 | } 161 | nal_size_ = 0; 162 | // http://www.howtobuildsoftware.com/index.php/how-do/9vN/c-windows-ms-media-foundation-mf-doesnt-play-video-from-my-source 163 | if (!par.extra.empty()) { 164 | auto extra = par.extra; 165 | typedef uint8_t* (*to_annexb_t)(const uint8_t* extradata, int extrasize, int* out_size, int* nalu_field_size); 166 | to_annexb_t to_annexb_func = nullptr; 167 | if (strstr(par.codec.data(), "h264")) 168 | to_annexb_func = avcc_to_annexb_extradata; 169 | else if (strstr(par.codec.data(), "hevc") || strstr(par.codec.data(), "h265")) 170 | to_annexb_func = hvcc_to_annexb_extradata; 171 | if (to_annexb_func) { 172 | if (!is_annexb(extra.data(), (int)extra.size())) { 173 | std::clog << "try to convert extra data to annexb" << std::endl; 174 | csd_ = to_annexb_func(extra.data(), (int)extra.size(), &csd_size_, &nal_size_); 175 | } 176 | } 177 | } 178 | if (!openCodec(MediaType::Video, *codec_id_, this)) 179 | return false; 180 | std::clog << "MFT decoder is ready" << std::endl; 181 | onOpen(); 182 | return true; 183 | } 184 | 185 | bool MFTVideoDecoder::close() 186 | { 187 | if (csd_) 188 | free(csd_); 189 | csd_ = nullptr; 190 | csd_size_ = 0; 191 | csd_sent_ = false; 192 | bool ret = closeCodec(); 193 | onClose(); 194 | return ret; 195 | } 196 | 197 | bool MFTVideoDecoder::onMFTCreated(ComPtr mft) 198 | { 199 | if (!testConstraints(mft)) 200 | return false; 201 | use_d3d_ = std::stoi(property("d3d", "0")); 202 | int adapter = std::stoi(property("adapter", "0")); 203 | auto vendor_s = property("vendor"); 204 | auto vendor = vendor_s.empty() ? nullptr : vendor_s.data(); 205 | auto fl = D3D11::to_feature_level(property("feature_level", "0").data()); // -1 and check os version to select highest in to_feature_level? 206 | UINT flags = 0; 207 | if (std::stoi(property("debug", "0"))) 208 | flags |= D3D11_CREATE_DEVICE_DEBUG; 209 | ComPtr a; 210 | MS_ENSURE(mft->GetAttributes(&a), false); 211 | // d3d: https://docs.microsoft.com/en-us/windows/desktop/medfound/direct3d-aware-mfts 212 | UINT32 d3d_aware = 0; 213 | #if (MS_API_DESKTOP+0) 214 | if (use_d3d_ == 9 && SUCCEEDED(a->GetUINT32(MF_SA_D3D_AWARE, &d3d_aware)) && d3d_aware 215 | && mgr9_.init(vendor, adapter)) { 216 | auto mgr = mgr9_.create(); 217 | if (mgr) { 218 | HRESULT hr = S_OK; 219 | MS_WARN((hr = mft->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)mgr.Get()))); 220 | if (SUCCEEDED(hr)) 221 | pool_ = NativeVideoBufferPool::create("D3D9"); 222 | } 223 | } 224 | #endif 225 | if (use_d3d_ == 11) 226 | MS_WARN(a->GetUINT32(MF_SA_D3D11_AWARE, &d3d_aware)); 227 | if (use_d3d_ == 11 && d3d_aware && mgr11_.init(vendor, adapter, fl, flags)) { 228 | auto mgr = mgr11_.create(); 229 | if (mgr) { 230 | HRESULT hr = S_OK; 231 | MS_WARN((hr = mft->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR)mgr.Get()))); 232 | if (SUCCEEDED(hr)) 233 | pool_ = NativeVideoBufferPool::create("D3D11"); 234 | } else { 235 | std::clog << "failed to create IMFDXGIDeviceManager. MFT d3d11 will be disabled." << std::endl; 236 | } 237 | } 238 | 239 | // https://docs.microsoft.com/zh-cn/windows/desktop/DirectShow/codec-api-properties 240 | // or ICodecAPI.SetValue(, &VARIANT{.vt=VT_UI4, .uintVal=1}) 241 | MS_WARN(a->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, 1)); 242 | /* low latency: 243 | faster but lower quality. h264 for win8+ 244 | no b-frame reordering. https://docs.microsoft.com/en-us/windows/win32/medfound/codecapi-avlowlatencymode?redirectedfrom=MSDN#remarks 245 | and many frames can be dropped by renderer because of bad pts 246 | if disabled: 247 | - stuck to decode some videos after seek 248 | - can't decode frames near EOS 249 | - too large latency on some devices, so video becomes stutter 250 | seems disabling low latency is required iff codec is hevc(depending on mft plugin?) 251 | */ 252 | const auto& par = parameters(); 253 | bool low_latency = true; 254 | if (strstr(par.codec.data(), "hevc") || strstr(par.codec.data(), "h265")) 255 | low_latency = false; 256 | if (std::stoi(property("low_latency", std::to_string(low_latency)))) // or MF_LOW_LATENCY for win8+ 257 | MS_WARN(a->SetUINT32(CODECAPI_AVLowLatencyMode, 1)); // .vt = VT_BOOL, .boolVal = VARIANT_TRUE fails 258 | // https://docs.microsoft.com/en-us/gaming/gdk/_content/gc/system/overviews/mediafoundation-decode#software-decode-1 259 | // options for sw decoder. defined in um/codecapi.h 260 | auto val = std::stoi(property("threads", "0")); 261 | if (val == 0) 262 | val = thread::hardware_concurrency(); 263 | MS_WARN(a->SetUINT32(CODECAPI_AVDecNumWorkerThreads, val)); 264 | // TODO: apply on the fly? 265 | val = std::stoi(property("priority", "0")); // -2(lowest), -1(below normal), 0(normal), 1(above normal), 2(highest) 266 | if (val != 0) // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority 267 | MS_WARN(a->SetUINT32(CODECAPI_AVPriorityControl, val)); 268 | val = std::stoi(property("fast", "0")); // 0: normal, 1: Optimal Loop Filter, 2: Disable Loop Filter, ..., 32: fastest 269 | if (val != 0) // https://docs.microsoft.com/en-us/windows/win32/directshow/avdecvideofastdecodemode 270 | MS_WARN(a->SetUINT32(CODECAPI_AVDecVideoFastDecodeMode, val)); // eAVFastDecodeMode 271 | // software power saving level in MPEG4 Part 2, VC1 and H264, 0~100 272 | val = std::stoi(property("power", "-1")); // 0 - Optimize for battery life, 50 - balanced, 100 - Optimize for video quality 273 | if (val >= 0) 274 | MS_WARN(a->SetUINT32(CODECAPI_AVDecVideoSWPowerLevel, val)); 275 | val = std::stoi(property("deinterlace", "0")); // 0: no, 1: progressive, 2: bob, 3: smart bob 276 | if (val != 0) 277 | MS_WARN(a->SetUINT32(CODECAPI_AVDecVideoSoftwareDeinterlaceMode, val)); 278 | // CODECAPI_AVDecVideoThreadAffinityMask, same as Win32 SetThreadAffinityMask 279 | // CODECAPI_AVDecDisableVideoPostProcessing (0,1): deblocking/deringing 280 | // CODECAPI_AVDecVideoThumbnailGenerationMode: can decode I frame onlyx 281 | return true; 282 | } 283 | 284 | bool MFTVideoDecoder::testConstraints(ComPtr mft) 285 | { 286 | // ICodecAPI is optional. same attributes 287 | ComPtr a; 288 | MS_ENSURE(mft->GetAttributes(&a), false); 289 | UINT32 max_w = 0, max_h = 0; 290 | if (SUCCEEDED(a->GetUINT32(CODECAPI_AVDecVideoMaxCodedWidth, &max_w)) // TODO: we can set max value? 291 | && SUCCEEDED(a->GetUINT32(CODECAPI_AVDecVideoMaxCodedHeight, &max_h))) { 292 | std::clog << "max supported size: " << max_w << "x" << max_h << std::endl; 293 | // FIXME: par.width/height is not coded value 294 | if ((max_w > 0 && (int)max_w < parameters().width) || (max_h > 0 && (int)max_h <= parameters().height)) { 295 | std::clog << "unsupported frame size" << std::endl; 296 | return false; 297 | } 298 | } 299 | return true; 300 | } 301 | 302 | BufferRef MFTVideoDecoder::filter(BufferRef in) 303 | { 304 | if (nal_size_ <= 0 || !in) 305 | return in; 306 | // TODO: PacketFilter. convert in a new buffer and keep input packet untouched, so switching to a new decoder can decode previously cached packets(from the last key frame packet) correctly 307 | const int size = in->size(); 308 | ByteArray out(in->constData(), size); 309 | to_annexb_packet(out.data(), size, nal_size_); 310 | if (csd_sent_) 311 | return make_shared(out); 312 | csd_sent_ = true; 313 | ByteArray csd_pkt(csd_size_ + size); 314 | memcpy(csd_pkt.data(), csd_, csd_size_); 315 | memcpy(csd_pkt.data() + csd_size_, out.constData(), size); 316 | out = csd_pkt; 317 | return make_shared(out); 318 | } 319 | 320 | bool MFTVideoDecoder::setInputTypeAttributes(IMFAttributes* a) 321 | { 322 | if (csd_ && false) { // TODO: no effect? 323 | // TODO: vlc set MF_MT_USER_DATA if not exist (MF_E_ATTRIBUTENOTFOUND)? 324 | UINT32 blob_size = 0; 325 | if (a->GetBlobSize(MF_MT_USER_DATA, &blob_size) == MF_E_ATTRIBUTENOTFOUND) 326 | MS_ENSURE(a->SetBlob(MF_MT_USER_DATA, csd_, csd_size_), false); 327 | } 328 | return MF::from(parameters(), a); 329 | } 330 | 331 | bool MFTVideoDecoder::setOutputTypeAttributes(IMFAttributes* attr) 332 | { 333 | return true; 334 | } 335 | 336 | int MFTVideoDecoder::getInputTypeScore(IMFAttributes* attr) 337 | { 338 | GUID id; 339 | MS_ENSURE(attr->GetGUID(MF_MT_SUBTYPE, &id), -1); 340 | if (id != *codec_id_) // TODO: always same id because mft is activated from same codec id? 341 | return -1; 342 | //if (codec_tag > 0 && FOURCCMap(id).GetFOURCC() == codec_tag) // TODO: if not constructed from a valid fcc, return value is random 343 | // return 2; 344 | // no fourcc.h? 345 | const auto tag = parameters().codec_tag; 346 | if (tag > 0 && to_fourcc(id) == tag) 347 | return 2; 348 | return 1; 349 | } 350 | 351 | int MFTVideoDecoder::getOutputTypeScore(IMFAttributes* attr) 352 | { 353 | // TODO: property "format" to select yuy2, l8 etc. 354 | GUID subtype; 355 | MS_ENSURE(attr->GetGUID(MF_MT_SUBTYPE, &subtype), -1); 356 | VideoFormat vf; 357 | if (!MF::to(vf, attr)) 358 | return -1; 359 | if (force_fmt_ && force_fmt_ == vf) 360 | return 2; 361 | // if not the same as input format(depth), MF_E_TRANSFORM_STREAM_CHANGE will occur and have to select again(in a dead loop). e.g. input vp9 is 8bit, but p010 is selected, mft can refuse to use it. 362 | return vf.bitsPerChannel() == VideoFormat(parameters().format).bitsPerChannel(); 363 | } 364 | 365 | bool MFTVideoDecoder::onOutputTypeChanged(DWORD streamId, ComPtr type) 366 | { 367 | ComPtr a; 368 | MS_ENSURE(type.As(&a), false); 369 | std::clog << __func__ << ": "; 370 | MF::dump(a.Get()); // TODO: move to MFTCodec.cpp 371 | VideoFormat outfmt; 372 | if (!MF::to(outfmt, a.Get())) 373 | return false; 374 | std::clog << "output format: " << outfmt << std::endl; 375 | // MFGetAttributeSize: vista+ 376 | UINT64 dim = 0; 377 | MS_ENSURE(a->GetUINT64(MF_MT_FRAME_SIZE, &dim), false); 378 | UINT32 w = 0, h = 0; 379 | Unpack2UINT32AsUINT64(dim, &w, &h); 380 | stride_x_ = outfmt.bytesPerLine(w, 0); 381 | stride_y_ = h; 382 | std::clog << "output size: " << w << "x" << h << ", stride: " << stride_x_ << "x" << stride_y_ << std::endl; 383 | MFVideoArea area{}; // desktop only? 384 | if (SUCCEEDED(a->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&area, sizeof(area), nullptr))) { 385 | std::clog << "video area: (" << area.OffsetX.value << ", " << area.OffsetY.value << "), " << area.Area.cx << "x" << area.Area.cy << std::endl; 386 | w = area.Area.cx; 387 | h = area.Area.cy; 388 | } 389 | ColorSpace cs; 390 | MF::to(cs, a.Get()); 391 | HDRMetadata hdr{}; 392 | MF::to(hdr, a.Get()); // frame level(hevc ts, mkv)? 393 | frame_param_ = VideoFrame(w, h, outfmt); 394 | frame_param_.setColorSpace(cs, true); 395 | frame_param_.setMasteringMetadata(hdr.mastering, false); 396 | frame_param_.setContentLightMetadata(hdr.content_light, false); 397 | // TODO: interlace MF_MT_INTERLACE_MODE=>MFVideoInterlaceMode 398 | // TODO: MF_MT_PIXEL_ASPECT_RATIO, MF_MT_YUV_MATRIX, MF_MT_VIDEO_PRIMARIES, MF_MT_TRANSFER_FUNCTION, MF_MT_VIDEO_CHROMA_SITING, MF_MT_VIDEO_NOMINAL_RANGE 399 | // MFVideoPrimaries_BT709, MFVideoPrimaries_BT2020 400 | // MF_MT_TRANSFER_FUNCTION: MFVideoTransFunc_2020(_const) 401 | if (use_d3d_ == 11 && pool_) { 402 | MS_ENSURE(mft_->GetOutputStreamAttributes(streamId, &a), false); 403 | // win8 attributes 404 | const auto nthandle = std::stoi(property("nthandle", "0")); 405 | const auto kmt = std::stoi(property("kmt", "0")); 406 | auto shared = nthandle || kmt || std::stoi(property("shared", "1")); 407 | /* 408 | result MF_SA_D3D11_SHARED MF_SA_D3D11_SHARED_WITHOUT_MUTEX 409 | 0 410 | kmt 0 1 411 | kmt 1 1 412 | kmt 1 0 413 | 0 0 0 414 | FIXME: kmt error ProcessOutput: (80070057), ProcessInput c00d36b5 415 | */ 416 | if (shared) { 417 | MS_ENSURE(a->SetUINT32(MF_SA_D3D11_SHARED , shared), false); // shared via keyed-mutex 418 | MS_ENSURE(a->SetUINT32(MF_SA_D3D11_SHARED_WITHOUT_MUTEX , !kmt), false); // shared via legacy mechanism // chrome media ccd2ba30c9d51983bb7676eda9851c372ace718d 419 | } 420 | // the decoded texture may has no SHADER_RESOUCE flag even if set in MFT(av1 decoder) 421 | auto sr = std::stoi(property("shader_resource", "0")); 422 | if (sr > 0) // hints for surfaces. applies iff MF_SA_D3D11_AWARE is TRUE 423 | MS_ENSURE(a->SetUINT32(MF_SA_D3D11_BINDFLAGS, D3D11_BIND_SHADER_RESOURCE/*|D3D11_BIND_DECODER*/), false); // optional? 424 | // MF_SA_D3D11_USAGE 425 | //MS_ENSURE(a->SetUINT32(MF_SA_BUFFERS_PER_SAMPLE , 1), false); // 3d video 426 | } 427 | return true; 428 | } 429 | 430 | bool MFTVideoDecoder::onOutput(ComPtr sample) 431 | { 432 | class PoolBuffer : public NativeVideoBuffer 433 | { 434 | NativeVideoBufferPoolRef pool_; 435 | public: 436 | PoolBuffer(NativeVideoBufferPoolRef pool) : pool_(pool) {} 437 | void* map(Type type, MapParameter*) override { return pool_.get();} 438 | }; 439 | VideoFrame frame(frame_param_.width(), frame_param_.height(), frame_param_.format()); 440 | if (pool_) 441 | frame.setNativeBuffer(std::make_shared(pool_)); 442 | if (!MF::to(frame, sample, (int)stride_x_, (int)stride_y_, copy_)) 443 | return false; 444 | ColorSpace cs; 445 | frame_param_.colorSpace(&cs, false); 446 | frame.setColorSpace(cs, true); 447 | HDRMetadata hdr{}; 448 | frame_param_.masteringMetadata(&hdr.mastering); 449 | frame_param_.contentLightMetadata(&hdr.content_light); 450 | frame.setMasteringMetadata(hdr.mastering, false); 451 | frame.setContentLightMetadata(hdr.content_light, false); 452 | frameDecoded(frame); 453 | return true; 454 | } 455 | 456 | void register_video_decoders_mf() { 457 | VideoDecoder::registerOnce("MFT", []{return new MFTVideoDecoder();}); 458 | } 459 | namespace { // DCE 460 | static const struct register_at_load_time_if_no_dce { 461 | inline register_at_load_time_if_no_dce() { register_video_decoders_mf();} 462 | } s; 463 | } 464 | MDK_NS_END 465 | -------------------------------------------------------------------------------- /MFUUIDs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | // https://docs.microsoft.com/zh-cn/windows/uwp/audio-video-camera/supported-codecs 11 | # pragma push_macro("_WIN32_WINNT") 12 | # if _WIN32_WINNT < 0x0A00 // _WIN32_WINNT_WIN10 is not defined yet 13 | # undef _WIN32_WINNT 14 | # define _WIN32_WINNT 0x0A00 15 | # endif 16 | #include "MFGlue.h" 17 | #include 18 | #include 19 | #if __has_include() // msvc 20 | # include 21 | # include // MFT_FRIENDLY_NAME_Attribute 22 | #else // mingw 23 | # include 24 | # include // MFT_FRIENDLY_NAME_Attribute 25 | #endif 26 | // #include //UuidToString, UuidHash 27 | # pragma pop_macro("_WIN32_WINNT") 28 | 29 | namespace std 30 | { 31 | template<> 32 | struct hash 33 | { 34 | std::size_t operator()(const UUID& value) const noexcept { 35 | //unsigned short UuidHash(UUID *Uuid, RPC_STATUS *Status); 36 | return std::hash{}(MDK_NS::MF::to_string(value)); // to_stringview? 37 | } 38 | }; 39 | } 40 | 41 | MDK_NS_BEGIN 42 | namespace MF { 43 | 44 | // d3d11.h 45 | // 657c3e17-3341-41a7-9ae6-37a9d699851f: D3D11_DECODER_PROFILE_xxxx 46 | 47 | // StringFromCLSID: wstring, upper case, with {} around. RpcStringFreeA is not declared for uwp? 48 | std::string to_string(const GUID& id) 49 | { 50 | char v[64]{}; 51 | const auto fcc = to_fourcc(id); 52 | if (fcc) { 53 | for (int i = 0; i < 4; ++i) { 54 | auto x = (fcc>>(8*i)) & 0xff; 55 | if (!x) 56 | goto print_uuid; 57 | v[i+1] = x; 58 | } 59 | v[0] = v[5] = '\''; 60 | return v; 61 | } 62 | print_uuid: 63 | snprintf(&v[0], sizeof(v), "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}" 64 | , (unsigned)id.Data1, id.Data2, id.Data3 65 | , id.Data4[0], id.Data4[1], id.Data4[2], id.Data4[3], id.Data4[4], id.Data4[5], id.Data4[6], id.Data4[7]); 66 | return v; 67 | } 68 | 69 | #define GUID_KV(var) {var, # var} 70 | 71 | static const std::unordered_map kIdNameMap{ 72 | GUID_KV(MFT_FRIENDLY_NAME_Attribute), 73 | GUID_KV(MF_TRANSFORM_CATEGORY_Attribute), 74 | GUID_KV(MF_TRANSFORM_FLAGS_Attribute), 75 | GUID_KV(MF_TRANSFORM_ASYNC), 76 | GUID_KV(MF_TRANSFORM_ASYNC_UNLOCK), 77 | GUID_KV(MFT_INPUT_TYPES_Attributes), 78 | GUID_KV(MFT_OUTPUT_TYPES_Attributes), 79 | GUID_KV(MFT_FIELDOFUSE_UNLOCK_Attribute), 80 | GUID_KV(MFT_CODEC_MERIT_Attribute), 81 | GUID_KV(MFT_CATEGORY_AUDIO_DECODER), 82 | GUID_KV(MFT_CATEGORY_AUDIO_EFFECT), 83 | GUID_KV(MFT_CATEGORY_AUDIO_ENCODER), 84 | GUID_KV(MFT_CATEGORY_VIDEO_DECODER), 85 | GUID_KV(MFT_CATEGORY_VIDEO_EFFECT), 86 | GUID_KV(MFT_CATEGORY_VIDEO_ENCODER), 87 | GUID_KV(MFT_CATEGORY_VIDEO_PROCESSOR), 88 | GUID_KV(MFT_TRANSFORM_CLSID_Attribute), 89 | GUID_KV(MFT_ENUM_HARDWARE_URL_Attribute), 90 | GUID_KV(MFT_ENUM_HARDWARE_VENDOR_ID_Attribute), 91 | GUID_KV(MFT_CONNECTED_STREAM_ATTRIBUTE), 92 | GUID_KV(MFT_CONNECTED_TO_HW_STREAM), 93 | GUID_KV(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE), 94 | GUID_KV(MF_SA_D3D_AWARE), 95 | GUID_KV(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT), 96 | GUID_KV(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE), 97 | GUID_KV(MF_SA_D3D11_BINDFLAGS), 98 | GUID_KV(MF_SA_D3D11_USAGE), 99 | GUID_KV(MF_SA_D3D11_AWARE), 100 | GUID_KV(MF_SA_D3D11_SHARED), 101 | GUID_KV(MF_SA_D3D11_SHARED_WITHOUT_MUTEX), 102 | GUID_KV(MF_MT_SUBTYPE), 103 | GUID_KV(MF_MT_MAJOR_TYPE), 104 | GUID_KV(MF_MT_AUDIO_SAMPLES_PER_SECOND), 105 | GUID_KV(MF_MT_AUDIO_NUM_CHANNELS), 106 | GUID_KV(MF_MT_AUDIO_CHANNEL_MASK), 107 | GUID_KV(MF_MT_FRAME_SIZE), 108 | GUID_KV(MF_MT_INTERLACE_MODE), 109 | GUID_KV(MF_MT_USER_DATA), 110 | GUID_KV(MF_MT_PIXEL_ASPECT_RATIO), 111 | GUID_KV(MF_MT_MAX_LUMINANCE_LEVEL), 112 | GUID_KV(MF_MT_MAX_FRAME_AVERAGE_LUMINANCE_LEVEL), 113 | GUID_KV(MF_MT_MAX_MASTERING_LUMINANCE), 114 | GUID_KV(MF_MT_MIN_MASTERING_LUMINANCE), 115 | // MFT_TRANSFORM_CLSID_Attribute, mfidl.h, win10 116 | GUID_KV(CLSID_MSH264DecoderMFT), 117 | GUID_KV(CLSID_MSH264EncoderMFT), 118 | GUID_KV(CLSID_MSDDPlusDecMFT), 119 | GUID_KV(CLSID_MP3DecMediaObject), 120 | GUID_KV(CLSID_MSAACDecMFT), 121 | GUID_KV(CLSID_MSH265DecoderMFT), 122 | GUID_KV(CLSID_WMVDecoderMFT), 123 | GUID_KV(CLSID_WMADecMediaObject), 124 | GUID_KV(CLSID_MSMPEGAudDecMFT), 125 | GUID_KV(CLSID_MSMPEGDecoderMFT), 126 | GUID_KV(CLSID_AudioResamplerMediaObject), 127 | GUID_KV(CLSID_MSVPxDecoder), 128 | GUID_KV(CLSID_MSOpusDecoder), 129 | GUID_KV(CLSID_VideoProcessorMFT), 130 | GUID_KV(MFMediaType_Audio), 131 | GUID_KV(MFMediaType_Video), 132 | GUID_KV(MFAudioFormat_PCM), 133 | GUID_KV(MFAudioFormat_Float), 134 | GUID_KV(MFVideoFormat_H264), 135 | GUID_KV(MFVideoFormat_H264_ES), 136 | GUID_KV(MFVideoFormat_H265), 137 | GUID_KV(MFVideoFormat_HEVC), 138 | GUID_KV(MFVideoFormat_HEVC_ES), 139 | GUID_KV(MFVideoFormat_MPEG2), 140 | GUID_KV(MFVideoFormat_MP43), 141 | GUID_KV(MFVideoFormat_MP4V), 142 | GUID_KV(MFVideoFormat_VP80), 143 | GUID_KV(MFVideoFormat_VP90), 144 | GUID_KV(MFVideoFormat_WMV1), 145 | GUID_KV(MFVideoFormat_WMV2), 146 | GUID_KV(MFVideoFormat_WMV3), 147 | GUID_KV(MFVideoFormat_WVC1), 148 | GUID_KV(MFVideoFormat_AV1), 149 | GUID_KV(MFVideoFormat_L8), 150 | GUID_KV(MFVideoFormat_L16), 151 | GUID_KV(MFAudioFormat_Dolby_AC3), 152 | GUID_KV(MFAudioFormat_Dolby_DDPlus), 153 | GUID_KV(MFAudioFormat_AAC), 154 | GUID_KV(MFAudioFormat_ADTS), // MPEG ADTS AAC Audio 155 | GUID_KV(MFAudioFormat_MP3), 156 | GUID_KV(MFAudioFormat_MSP1), 157 | //GUID_KV(MFAudioFormat_MSAUDIO1), 158 | GUID_KV(MFAudioFormat_FLAC), 159 | GUID_KV(MFAudioFormat_Opus), 160 | GUID_KV(MFAudioFormat_Vorbis), 161 | GUID_KV(MFAudioFormat_WMAudioV8), 162 | GUID_KV(MFAudioFormat_WMAudioV9), 163 | GUID_KV(MFAudioFormat_WMAudio_Lossless), 164 | GUID_KV(MF_MT_ALL_SAMPLES_INDEPENDENT), 165 | GUID_KV(MF_MT_COMPRESSED), 166 | GUID_KV(MF_MT_FIXED_SIZE_SAMPLES), 167 | GUID_KV(MF_MT_SAMPLE_SIZE), 168 | GUID_KV(MF_MT_WRAPPED_TYPE), 169 | GUID_KV(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION), 170 | GUID_KV(MF_MT_AAC_PAYLOAD_TYPE), 171 | GUID_KV(MF_MT_AUDIO_AVG_BYTES_PER_SECOND), 172 | GUID_KV(MF_MT_AUDIO_BITS_PER_SAMPLE), 173 | GUID_KV(MF_MT_AUDIO_BLOCK_ALIGNMENT), 174 | GUID_KV(MF_MT_AUDIO_CHANNEL_MASK), 175 | GUID_KV(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND), 176 | GUID_KV(MF_MT_AUDIO_FOLDDOWN_MATRIX), 177 | GUID_KV(MF_MT_AUDIO_NUM_CHANNELS), 178 | GUID_KV(MF_MT_AUDIO_PREFER_WAVEFORMATEX), 179 | GUID_KV(MF_MT_AUDIO_SAMPLES_PER_BLOCK), 180 | GUID_KV(MF_MT_AUDIO_SAMPLES_PER_SECOND), 181 | GUID_KV(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE), 182 | GUID_KV(MF_MT_AUDIO_WMADRC_AVGREF), 183 | GUID_KV(MF_MT_AUDIO_WMADRC_AVGTARGET), 184 | GUID_KV(MF_MT_AUDIO_WMADRC_PEAKREF), 185 | GUID_KV(MF_MT_AUDIO_WMADRC_PEAKTARGET), 186 | GUID_KV(MF_MT_AVG_BIT_ERROR_RATE), 187 | GUID_KV(MF_MT_AVG_BITRATE), 188 | GUID_KV(MF_MT_DEFAULT_STRIDE), 189 | GUID_KV(MF_MT_DRM_FLAGS), 190 | GUID_KV(MF_MT_FRAME_RATE), 191 | GUID_KV(MF_MT_FRAME_RATE_RANGE_MAX), 192 | GUID_KV(MF_MT_FRAME_RATE_RANGE_MIN), 193 | GUID_KV(MF_MT_FRAME_SIZE), 194 | GUID_KV(MF_MT_GEOMETRIC_APERTURE), 195 | GUID_KV(MF_MT_INTERLACE_MODE), 196 | GUID_KV(MF_MT_MAX_KEYFRAME_SPACING), 197 | GUID_KV(MF_MT_MINIMUM_DISPLAY_APERTURE), 198 | GUID_KV(MF_MT_MPEG_SEQUENCE_HEADER), 199 | GUID_KV(MF_MT_MPEG_START_TIME_CODE), 200 | GUID_KV(MF_MT_MPEG2_FLAGS), 201 | GUID_KV(MF_MT_MPEG2_LEVEL), 202 | GUID_KV(MF_MT_MPEG2_PROFILE), 203 | GUID_KV(MF_MT_PAD_CONTROL_FLAGS), 204 | GUID_KV(MF_MT_PALETTE), 205 | GUID_KV(MF_MT_PAN_SCAN_APERTURE), 206 | GUID_KV(MF_MT_PAN_SCAN_ENABLED), 207 | GUID_KV(MF_MT_PIXEL_ASPECT_RATIO), 208 | GUID_KV(MF_MT_SOURCE_CONTENT_HINT), 209 | GUID_KV(MF_MT_TRANSFER_FUNCTION), 210 | GUID_KV(MF_MT_VIDEO_CHROMA_SITING), 211 | GUID_KV(MF_MT_VIDEO_LIGHTING), 212 | GUID_KV(MF_MT_VIDEO_NOMINAL_RANGE), 213 | GUID_KV(MF_MT_VIDEO_PRIMARIES), 214 | GUID_KV(MF_MT_VIDEO_ROTATION), 215 | GUID_KV(MF_MT_YUV_MATRIX), 216 | GUID_KV(MFVideoFormat_Base), 217 | GUID_KV(MFVideoFormat_YUY2), 218 | GUID_KV(MFVideoFormat_YVYU), 219 | GUID_KV(MFVideoFormat_UYVY), 220 | GUID_KV(MFVideoFormat_NV12), 221 | GUID_KV(MFVideoFormat_YV12), 222 | GUID_KV(MFVideoFormat_I420), 223 | GUID_KV(MFVideoFormat_IYUV), 224 | GUID_KV(MFVideoFormat_P210), 225 | GUID_KV(MFVideoFormat_P216), 226 | GUID_KV(MFVideoFormat_P010), 227 | GUID_KV(MFVideoFormat_P016), 228 | GUID_KV(MFVideoFormat_RGB32), 229 | GUID_KV(MFVideoFormat_ARGB32), 230 | GUID_KV(MFVideoFormat_RGB24), 231 | GUID_KV(CODECAPI_AVDecVideoThumbnailGenerationMode), 232 | GUID_KV(CODECAPI_AVDecVideoDropPicWithMissingRef), 233 | GUID_KV(CODECAPI_AVDecVideoSoftwareDeinterlaceMode), 234 | GUID_KV(CODECAPI_AVDecVideoFastDecodeMode), 235 | GUID_KV(CODECAPI_AVLowLatencyMode), 236 | GUID_KV(CODECAPI_AVDecVideoH264ErrorConcealment), 237 | GUID_KV(CODECAPI_AVDecVideoMPEG2ErrorConcealment), 238 | GUID_KV(CODECAPI_AVDecVideoCodecType), 239 | GUID_KV(CODECAPI_AVDecVideoDXVAMode), 240 | GUID_KV(CODECAPI_AVDecVideoDXVABusEncryption), 241 | GUID_KV(CODECAPI_AVDecVideoSWPowerLevel), 242 | GUID_KV(CODECAPI_AVDecVideoMaxCodedWidth), 243 | GUID_KV(CODECAPI_AVDecVideoMaxCodedHeight), 244 | GUID_KV(CODECAPI_AVDecNumWorkerThreads), 245 | GUID_KV(CODECAPI_AVDecSoftwareDynamicFormatChange), 246 | GUID_KV(CODECAPI_AVDecDisableVideoPostProcessing), 247 | GUID_KV(CODECAPI_AVDecVideoAcceleration_H264), 248 | GUID_KV(CODECAPI_AVDecVideoAcceleration_MPEG2), 249 | GUID_KV(CODECAPI_AVDecVideoAcceleration_VC1), 250 | GUID_KV(CODECAPI_AVDecAudioDualMono), 251 | GUID_KV(CODECAPI_AVDecAudioDualMonoReproMode), 252 | GUID_KV(CODECAPI_AVDecCommonMeanBitRate), 253 | GUID_KV(CODECAPI_AVDDSurroundMode), 254 | GUID_KV(CODECAPI_AVDecDDOperationalMode), 255 | GUID_KV(CODECAPI_AVDecDDMatrixDecodingMode), 256 | GUID_KV(CODECAPI_AVDecDDDynamicRangeScaleHigh), 257 | GUID_KV(CODECAPI_AVDecDDDynamicRangeScaleLow), 258 | GUID_KV(CODECAPI_AVDecDDStereoDownMixMode), 259 | GUID_KV(MFT_DECODER_EXPOSE_OUTPUT_TYPES_IN_NATIVE_ORDER), 260 | GUID_KV(MFT_DECODER_QUALITY_MANAGEMENT_CUSTOM_CONTROL), 261 | GUID_KV(MFSampleExtension_CleanPoint), 262 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES) 263 | GUID_KV(MF_MT_ORIGINAL_4CC), 264 | GUID_KV(MF_MT_CUSTOM_VIDEO_PRIMARIES), 265 | #endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES) */ 266 | 267 | GUID_KV(MF_MT_D3D_DECODE_PROFILE_GUID), 268 | GUID_KV(MF_MEDIA_EXTENSION_PACKAGED_WINDOWS_SIGNED), 269 | GUID_KV(MF_MEDIA_EXTENSION_ABSOLUTE_DLLPATH), 270 | GUID_KV(MF_MEDIA_EXTENSION_PACKAGE_FULL_NAME), 271 | GUID_KV(MF_MEDIA_EXTENSION_PACKAGE_FAMILY_NAME), 272 | GUID_KV(MF_TELEMETRY_OBJECT_INSTANCE_ATTRIBUTE), 273 | GUID_KV(MF_MEDIA_EXTENSION_ACTIVATABLE_CLASS_ID), 274 | GUID_KV(MF_MEDIA_EXTENSION_PACKAGE_REG_NEEDED), 275 | GUID_KV(MF_MEDIA_EXTENSION_WEB_PLATFORM_ALLOWED), 276 | GUID_KV(MF_INPROCDLL_LIFETIME_MANAGER), 277 | }; 278 | 279 | std::string to_name(const GUID& id) 280 | { 281 | const auto i = kIdNameMap.find(id); 282 | if (i == kIdNameMap.cend()) 283 | return to_string(id); 284 | return i->second; 285 | } 286 | } // namespace MF 287 | MDK_NS_END -------------------------------------------------------------------------------- /MFVideoGlue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | * This file is part of MDK MFT plugin 4 | * Source code: https://github.com/wang-bin/mdk-mft 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | # pragma push_macro("_WIN32_WINNT") 11 | # if _WIN32_WINNT < 0x0A00 // 0A00: hdr attributes. 0601: for IMFTrackedSample. doc says it's available for vista, but win sdk requires win7 12 | # undef _WIN32_WINNT 13 | # define _WIN32_WINNT 0x0A00 14 | # endif 15 | #include "base/ms/MFGlue.h" 16 | #include "base/ByteArrayBuffer.h" 17 | #include "mdk/MediaInfo.h" 18 | #include "mdk/VideoFrame.h" 19 | #if (MS_API_DESKTOP+0) 20 | #include 21 | #endif 22 | #include 23 | #include 24 | #if __has_include() // msvc 25 | # include 26 | #else // mingw 27 | # include 28 | #endif 29 | # pragma pop_macro("_WIN32_WINNT") 30 | 31 | MDK_NS_BEGIN 32 | namespace MF { 33 | 34 | static const struct { 35 | UINT32 mf; 36 | ColorSpace::Primary primaries; 37 | } mf_primaries[] = { 38 | {MFVideoPrimaries_BT709, ColorSpace::Primary::BT709}, 39 | {MFVideoPrimaries_BT470_2_SysM, ColorSpace::Primary::BT470M}, 40 | {MFVideoPrimaries_BT470_2_SysBG, ColorSpace::Primary::BT470BG}, 41 | {MFVideoPrimaries_SMPTE170M, ColorSpace::Primary::SMPTE170M}, 42 | {MFVideoPrimaries_SMPTE240M, ColorSpace::Primary::SMPTE240M}, 43 | {MFVideoPrimaries_SMPTE_C, ColorSpace::Primary::FILM}, 44 | {MFVideoPrimaries_BT2020, ColorSpace::Primary::BT2020}, 45 | {MFVideoPrimaries_XYZ, ColorSpace::Primary::SMPTEST428_1}, 46 | {MFVideoPrimaries_EBU3213, ColorSpace::Primary::EBU_3213_E}, 47 | }; 48 | 49 | static const struct { 50 | UINT32 mf; 51 | ColorSpace::Transfer trc; 52 | } mf_trcs[] = { 53 | {MFVideoTransFunc_10, ColorSpace::Transfer::LINEAR}, 54 | {MFVideoTransFunc_22, ColorSpace::Transfer::GAMMA22}, 55 | {MFVideoTransFunc_28, ColorSpace::Transfer::GAMMA28}, 56 | {MFVideoTransFunc_709, ColorSpace::Transfer::BT709}, 57 | {MFVideoTransFunc_240M, ColorSpace::Transfer::SMPTE240M}, 58 | {MFVideoTransFunc_sRGB, ColorSpace::Transfer::IEC61966_2_1}, 59 | {MFVideoTransFunc_Log_100, ColorSpace::Transfer::LOG}, 60 | {MFVideoTransFunc_Log_316, ColorSpace::Transfer::LOG_SQRT}, 61 | {MFVideoTransFunc_2084, ColorSpace::Transfer::PQ}, // PQ, BT2100 62 | {MFVideoTransFunc_HLG, ColorSpace::Transfer::ARIB_STD_B67}, 63 | //{MFVideoTransFunc_2020_const,} // ColorSpace::Matrix::BT2020_CL 64 | //{MFVideoTransFunc_2020, BT2020_10,BT2020_12} // ColorSpace::Matrix::BT2020_NCL 65 | }; 66 | 67 | static const struct { 68 | UINT32 mf; 69 | ColorSpace::Matrix mat; 70 | } mf_mats[] = { 71 | {MFVideoTransferMatrix_BT709, ColorSpace::Matrix::BT709}, 72 | {MFVideoTransferMatrix_BT601, ColorSpace::Matrix::BT470BG}, 73 | {MFVideoTransferMatrix_SMPTE240M, ColorSpace::Matrix::SMPTE240M}, 74 | //{MFVideoTransferMatrix_BT2020_10, ColorSpace::Matrix::}, // Transfer::BT2020_10 75 | //{MFVideoTransferMatrix_BT2020_12, ColorSpace::Matrix::}, // Transfer::BT2020_12 76 | }; 77 | 78 | ColorSpace::Range to_range(UINT32 v) 79 | { 80 | switch (v) { 81 | case MFNominalRange_Unknown: return ColorSpace::Range::INVALID; 82 | case MFNominalRange_Normal: return ColorSpace::Range::Full; 83 | case MFNominalRange_Wide: return ColorSpace::Range::Limited; 84 | default: return ColorSpace::Range::Limited; 85 | } 86 | return ColorSpace::Range::INVALID; 87 | } 88 | 89 | UINT32 from_range(ColorSpace::Range r) 90 | { 91 | switch (r) { 92 | case ColorSpace::Range::Full: return MFNominalRange_Normal; 93 | case ColorSpace::Range::Limited: return MFNominalRange_Wide; 94 | default: return MFNominalRange_Unknown; 95 | } 96 | return MFNominalRange_Unknown; 97 | } 98 | 99 | bool to(ColorSpace& cs, const IMFAttributes* ca) 100 | { 101 | UINT32 v = 0; 102 | auto a = const_cast(ca); 103 | if (SUCCEEDED(a->GetUINT32(MF_MT_VIDEO_PRIMARIES, &v))) { 104 | for (const auto i : mf_primaries) { 105 | if (i.mf == v) { 106 | cs.primaries = i.primaries; 107 | break; 108 | } 109 | } 110 | } 111 | if (SUCCEEDED(a->GetUINT32(MF_MT_TRANSFER_FUNCTION, &v))) { 112 | for (const auto i : mf_trcs) { 113 | if (i.mf == v) { 114 | cs.transfer = i.trc; 115 | break; 116 | } 117 | } 118 | } 119 | if (SUCCEEDED(a->GetUINT32(MF_MT_YUV_MATRIX, &v))) { 120 | for (const auto i : mf_mats) { 121 | if (i.mf == v) { 122 | cs.matrix = i.mat; 123 | break; 124 | } 125 | } 126 | } 127 | if (SUCCEEDED(a->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &v))) 128 | cs.range = to_range(v); 129 | return true; 130 | } 131 | 132 | bool from(const ColorSpace& cs, IMFAttributes* a) 133 | { 134 | for (const auto i : mf_primaries) { 135 | if (i.primaries == cs.primaries) { 136 | MS_WARN(a->SetUINT32(MF_MT_VIDEO_PRIMARIES, i.mf)); 137 | break; 138 | } 139 | } 140 | for (const auto i : mf_trcs) { 141 | if (i.trc == cs.transfer) { 142 | MS_WARN(a->SetUINT32(MF_MT_TRANSFER_FUNCTION, i.mf)); 143 | break; 144 | } 145 | } 146 | for (const auto i : mf_mats) { 147 | if (i.mat == cs.matrix) { 148 | MS_WARN(a->SetUINT32(MF_MT_YUV_MATRIX, i.mf)); 149 | break; 150 | } 151 | } 152 | MS_WARN(a->SetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, from_range(cs.range))); 153 | return true; 154 | } 155 | 156 | bool to(HDRMetadata& hdr, const IMFAttributes* ca) 157 | { 158 | auto a = const_cast(ca); 159 | UINT32 v = 0; 160 | if (SUCCEEDED(a->GetUINT32(MF_MT_MIN_MASTERING_LUMINANCE, &v))) 161 | hdr.mastering.luminance_min = v; 162 | if (SUCCEEDED(a->GetUINT32(MF_MT_MAX_MASTERING_LUMINANCE, &v))) 163 | hdr.mastering.luminance_max = v; 164 | #if !(MS_WINRT) 165 | MT_CUSTOM_VIDEO_PRIMARIES p{}; 166 | if (SUCCEEDED(a->GetBlob(MF_MT_CUSTOM_VIDEO_PRIMARIES, (UINT8*)&p, sizeof(p), nullptr))) { 167 | hdr.mastering.R[0] = p.fRx; 168 | hdr.mastering.R[1] = p.fRy; 169 | hdr.mastering.G[0] = p.fGx; 170 | hdr.mastering.G[1] = p.fGy; 171 | hdr.mastering.B[0] = p.fBx; 172 | hdr.mastering.B[1] = p.fBy; 173 | hdr.mastering.W[0] = p.fWx; 174 | hdr.mastering.W[1] = p.fWy; 175 | } 176 | #endif 177 | // MaxFALL https://docs.microsoft.com/en-us/windows/win32/medfound/mf-mt-max-frame-average-luminance-level 178 | if (SUCCEEDED(a->GetUINT32(MF_MT_MAX_FRAME_AVERAGE_LUMINANCE_LEVEL, &v))) 179 | hdr.content_light.MaxFALL = v; 180 | if (SUCCEEDED(a->GetUINT32(MF_MT_MAX_LUMINANCE_LEVEL, &v))) 181 | hdr.content_light.MaxCLL = v; 182 | return true; 183 | } 184 | 185 | bool from(const HDRMetadata& hdr, IMFAttributes* a) 186 | { 187 | #if !(MS_WINRT) // not defined in header(mfapi.h), but exists at runtime 188 | if (hdr.mastering.W[0] > 0) { 189 | MT_CUSTOM_VIDEO_PRIMARIES p; 190 | p.fRx = hdr.mastering.R[0]; 191 | p.fRy = hdr.mastering.R[1]; 192 | p.fGx = hdr.mastering.G[0]; 193 | p.fGy = hdr.mastering.G[1]; 194 | p.fBx = hdr.mastering.B[0]; 195 | p.fBy = hdr.mastering.B[1]; 196 | p.fWx = hdr.mastering.W[0]; 197 | p.fWy = hdr.mastering.W[1]; 198 | MS_WARN(a->SetBlob(MF_MT_CUSTOM_VIDEO_PRIMARIES, (UINT8*)&p, sizeof(p))); 199 | } 200 | #endif 201 | if (hdr.mastering.luminance_min > 0) 202 | MS_WARN(a->SetUINT32(MF_MT_MIN_MASTERING_LUMINANCE, hdr.mastering.luminance_min)); 203 | if (hdr.mastering.luminance_max > 0) 204 | MS_WARN(a->SetUINT32(MF_MT_MAX_MASTERING_LUMINANCE, hdr.mastering.luminance_max)); 205 | if (hdr.content_light.MaxFALL > 0) 206 | MS_WARN(a->SetUINT32(MF_MT_MAX_FRAME_AVERAGE_LUMINANCE_LEVEL, hdr.content_light.MaxFALL)); 207 | if (hdr.content_light.MaxCLL > 0) 208 | MS_WARN(a->SetUINT32(MF_MT_MAX_LUMINANCE_LEVEL, hdr.content_light.MaxCLL)); 209 | return true; 210 | } 211 | 212 | struct mf_pix_fmt_entry { 213 | const GUID& guid; 214 | PixelFormat pixfmt; 215 | }; 216 | 217 | // https://docs.microsoft.com/en-us/windows/desktop/medfound/video-subtype-guids 218 | static const struct mf_pix_fmt_entry mf_pixfmts[] = { 219 | {MFVideoFormat_IYUV, PixelFormat::YUV420P}, 220 | {MFVideoFormat_I420, PixelFormat::YUV420P}, 221 | {MFVideoFormat_NV12, PixelFormat::NV12}, 222 | {MFVideoFormat_P010, PixelFormat::P010LE}, 223 | {MFVideoFormat_P016, PixelFormat::P016LE}, 224 | {MFVideoFormat_YUY2, PixelFormat::YUYV422}, 225 | {MFVideoFormat_UYVY, PixelFormat::UYVY422}, 226 | //{MFVideoFormat_420O, PixelFormat::YUV420P}, // av1, hevc, vp9 227 | }; 228 | 229 | bool to_pixfmt(PixelFormat* pixfmt, const GUID& guid) 230 | { 231 | for (const auto i : mf_pixfmts) { 232 | if (i.guid == guid) { 233 | *pixfmt = i.pixfmt; 234 | return true; 235 | } 236 | } 237 | return false; 238 | } 239 | 240 | bool from_pixfmt(GUID* guid, PixelFormat pixfmt) 241 | { 242 | for (const auto i : mf_pixfmts) { 243 | if (i.pixfmt == pixfmt) { 244 | *guid = i.guid; 245 | return true; 246 | } 247 | } 248 | return false; 249 | } 250 | 251 | bool to(VideoFormat& fmt, const IMFAttributes* ca) 252 | { 253 | GUID subtype; 254 | auto a = const_cast(ca); 255 | MS_ENSURE(a->GetGUID(MF_MT_SUBTYPE, &subtype), false); 256 | PixelFormat pixfmt; 257 | if (!to_pixfmt(&pixfmt, subtype)) 258 | return false; 259 | fmt = pixfmt; 260 | return true; 261 | } 262 | 263 | bool from(const VideoFormat& fmt, IMFAttributes* a) 264 | { 265 | return false; 266 | } 267 | #undef DEFINE_GUID 268 | #define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } 269 | // FIXME: defined in evr.lib? 270 | DEFINE_GUID(MR_BUFFER_SERVICE, 0xa562248c, 0x9ac6, 0x4ffc, 0x9f, 0xba, 0x3a, 0xf8, 0xf8, 0xad, 0x1a, 0x4d ); 271 | 272 | class MFBuffer2DState { // TODO: hold tracked sample? 273 | ComPtr buf_; 274 | ComPtr buf2d_; 275 | public: 276 | MFBuffer2DState(ComPtr b) : buf_(b) {} 277 | MFBuffer2DState(ComPtr b) : buf2d_(b) {} 278 | MFBuffer2DState(ComPtr b) : buf2d_(b) {} 279 | ~MFBuffer2DState() { 280 | if (buf2d_) 281 | MS_WARN(buf2d_->Unlock2D()); 282 | if (buf_) 283 | MS_WARN(buf_->Unlock()); 284 | } 285 | }; 286 | 287 | class MFPlaneBuffer2D final: public Buffer2D 288 | { 289 | const Byte* const data_; // not writable, so const Byte* 290 | size_t size_; 291 | size_t stride_; 292 | std::shared_ptr state_; 293 | public: 294 | MFPlaneBuffer2D(const uint8_t* data, size_t size, int stride, std::shared_ptr state) 295 | : data_(data), size_(size), stride_(stride), state_(state) 296 | {} 297 | const Byte* constData() const override {return data_;} 298 | size_t size() const override { return size_;} 299 | size_t stride() const override { return stride_;} 300 | }; 301 | 302 | bool setBuffersTo(VideoFrame& frame, ComPtr buf, int stride_x = 0, int stride_y = 0, bool copy = false) 303 | { 304 | BYTE* data = nullptr; 305 | DWORD len = 0; 306 | LONG pitch = 0; 307 | ComPtr buf2d; 308 | ComPtr buf2d2; 309 | std::shared_ptr bs; 310 | 311 | if (SUCCEEDED(buf.As(&buf2d2))) { // usually d3d buffer 312 | bs = std::make_shared(buf2d2); 313 | BYTE* start = nullptr; // required 314 | MS_ENSURE(buf2d2->Lock2DSize(MF2DBuffer_LockFlags_Read, &data, &pitch, &start, &len), false); 315 | } else if (SUCCEEDED(buf.As(&buf2d))) { // usually d3d buffer 316 | bs = std::make_shared(buf2d); 317 | MS_ENSURE(buf2d->Lock2D(&data, &pitch), false); 318 | } else { // slower than 2d if is 2d 319 | bs = std::make_shared(buf); 320 | MS_ENSURE(buf->Lock(&data, nullptr, &len), false); 321 | } 322 | if (pitch <= 0) 323 | pitch = stride_x; 324 | const uint8_t* da[] = {data, nullptr, nullptr}; // assume no padding data if da[i>0] == null 325 | int strides[] = {(int)pitch, (int)pitch, (int)pitch}; 326 | const auto& fmt = frame.format(); 327 | for (int plane = 0; plane < fmt.planeCount(); ++plane) { 328 | if (pitch <= 0) 329 | pitch = frame.effectiveBytesPerLine(plane); 330 | const size_t bytes = pitch*fmt.height(stride_y, plane); 331 | if (copy) 332 | da[plane] = data; 333 | else 334 | frame.addBuffer(std::make_shared(data, bytes, (int)pitch, bs)); // same stride for each plane? 335 | data += bytes; 336 | } 337 | if (copy) 338 | frame.setBuffers(da, strides); 339 | return true; 340 | } 341 | 342 | bool to(VideoFrame& frame, ComPtr sample, int stride_x, int stride_y, int copy) 343 | { 344 | LONGLONG t = 0; 345 | if (SUCCEEDED(sample->GetSampleTime(&t))) 346 | frame.setTimestamp(from_mf_time(t)); 347 | DWORD nb_bufs = 0; 348 | MS_ENSURE(sample->GetBufferCount(&nb_bufs), false); 349 | const auto& fmt = frame.format(); 350 | const bool contiguous = fmt.planeCount() > (int)nb_bufs; 351 | for (DWORD i = 0; i < nb_bufs; ++i) { 352 | ComPtr buf; 353 | MS_ENSURE(sample->GetBufferByIndex(i, &buf), false); 354 | ComPtr dxgibuf; // MFCreateDXGISurfaceBuffer 355 | struct { 356 | ID3D11Texture2D* tex; 357 | ComPtr sp; // auto release 358 | } texinfo; 359 | void* opaque = nullptr; 360 | if (SUCCEEDED(buf.As(&dxgibuf))) { 361 | MS_WARN(dxgibuf->GetResource(IID_PPV_ARGS(&texinfo.sp))); 362 | UINT subidx = 0; 363 | MS_WARN(dxgibuf->GetSubresourceIndex(&subidx)); 364 | texinfo.index = subidx; 365 | texinfo.tex = texinfo.sp.Get(); 366 | opaque = &texinfo; 367 | } 368 | #if (MS_API_DESKTOP+0) 369 | ComPtr d3d9surf; // MFCreateDXSurfaceBuffer 370 | //ComPtr getsv; MS_WARN(buf.As(&getsv)); IMFGetService::GetService 371 | if (!opaque && SUCCEEDED(MFGetService(buf.Get(), MR_BUFFER_SERVICE, IID_PPV_ARGS(&d3d9surf)))) 372 | opaque = d3d9surf.Get(); 373 | #endif 374 | if (opaque) { 375 | if (copy > 0) { 376 | frame.setNativeBuffer(nullptr); 377 | setBuffersTo(frame, buf, stride_x, stride_y, copy > 1); 378 | return true; 379 | } 380 | // d3d: The sample will contain exactly one media buffer 381 | auto nbuf = frame.nativeBuffer(); 382 | if (nbuf) { 383 | auto pool = (NativeVideoBufferPool*)nbuf->map(NativeVideoBuffer::Original, nullptr); 384 | if (pool) { 385 | frame.setNativeBuffer(pool->getBuffer(opaque, [sample](){ 386 | (void)sample; // recyle when no one holds d3d native buffer 387 | })); 388 | return true; 389 | } 390 | } 391 | } 392 | 393 | DWORD len = 0; 394 | MS_ENSURE(buf->GetCurrentLength(&len), false); 395 | if (contiguous) { 396 | setBuffersTo(frame, buf, stride_x, stride_y, copy > 0); 397 | } else { 398 | ComPtr buf2d; 399 | if (SUCCEEDED(buf.As(&buf2d))) { 400 | } 401 | if (buf2d) { 402 | frame.addBuffer(to(buf2d), i); 403 | } else { 404 | // TODO: no copy. Buffer2DRef to(ComPtr b, int stride, int height, ptrdiff_t offset = 0, int size = -1); 405 | BYTE* data = nullptr; 406 | MS_ENSURE(buf->Lock(&data, nullptr, &len), false); 407 | frame.addBuffer(std::make_shared(len/fmt.height(stride_y, i), fmt.height(stride_y, i), data)); 408 | buf->Unlock(); 409 | } 410 | } 411 | } 412 | return true; 413 | } 414 | 415 | bool from(const VideoCodecParameters& par, IMFAttributes* a) 416 | { 417 | MS_ENSURE(a->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), false); 418 | MS_ENSURE(a->SetGUID(MF_MT_SUBTYPE, *codec_for(par.codec, MediaType::Video)), false); 419 | MS_ENSURE(a->SetUINT64(MF_MT_FRAME_SIZE, Pack2UINT32AsUINT64(par.width, par.height)), false); 420 | //MS_ENSURE(MFSetAttributeSize(a.Get(), MF_MT_FRAME_SIZE, par.width, par.height), nullptr); // vista+ 421 | MS_ENSURE(a->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive), false); 422 | //MS_ENSURE(MFSetAttributeRatio(a, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), false); // TODO: 423 | 424 | if (par.bit_rate > 0) 425 | MS_ENSURE(a->SetUINT32(MF_MT_AVG_BITRATE, (UINT32)par.bit_rate), false); 426 | 427 | // TODO: mp4 extra data check? 428 | bool use_extra = !par.extra.empty(); // and if no bsf 429 | if (use_extra) { 430 | if (par.codec.find("mpeg4") != std::string::npos) { 431 | if (par.extra.size() < 3 || par.extra[0] || par.extra[1] || par.extra[2] != 1) 432 | use_extra = false; 433 | } else if (par.codec == "h264" || par.codec == "hevc" || par.codec == "h265") { 434 | if (par.extra[0] == 1) // avcC/hvcC 435 | use_extra = false; 436 | } 437 | UINT32 blob_size = 0; // TODO: vlc set MF_MT_USER_DATA if not exist (MF_E_ATTRIBUTENOTFOUND) 438 | if (a->GetBlobSize(MF_MT_USER_DATA, &blob_size) == MF_E_ATTRIBUTENOTFOUND) 439 | use_extra = false; 440 | } 441 | if (use_extra) 442 | MS_ENSURE(a->SetBlob(MF_MT_USER_DATA, par.extra.data(), (UINT32)par.extra.size()), false); 443 | return true; 444 | } 445 | 446 | } // namespace MF 447 | MDK_NS_END -------------------------------------------------------------------------------- /MSUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 WangBin 3 | */ 4 | #include "MSUtils.h" 5 | 6 | // xxxxxxxx-0000-0010-8000-00AA00389B71. https://docs.microsoft.com/en-us/windows/win32/directshow/fourcc-codes 7 | uint32_t to_fourcc(const GUID id) 8 | { 9 | if (id.Data2 == 0 && id.Data3 == 0x0010 10 | && id.Data4[0] == 0x80 && id.Data4[1] == 0x00 11 | && id.Data4[2] == 0x00 && id.Data4[3] == 0xAA && id.Data4[4] == 0x00 12 | && id.Data4[5] == 0x38 && id.Data4[6] == 0x9B && id.Data4[7] == 0x71) 13 | return id.Data1; 14 | return 0; 15 | } -------------------------------------------------------------------------------- /MSUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 WangBin 3 | */ 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #ifdef WINAPI_FAMILY 11 | # include 12 | # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 13 | # define MS_API_DESKTOP 1 14 | # else 15 | # define MS_WINRT 1 16 | # define MS_API_APP 1 17 | # endif 18 | #else 19 | # define MS_API_DESKTOP 1 20 | #endif //WINAPI_FAMILY 21 | 22 | // vista is required to build with wrl, but xp runtime works 23 | #pragma push_macro("_WIN32_WINNT") 24 | #if _WIN32_WINNT < _WIN32_WINNT_VISTA 25 | # undef _WIN32_WINNT 26 | # define _WIN32_WINNT _WIN32_WINNT_VISTA 27 | #endif 28 | #include 29 | #if (_MSC_VER + 0) // missing headers in mingw 30 | #include // RuntimeClass 31 | #endif 32 | #pragma pop_macro("_WIN32_WINNT") 33 | using namespace Microsoft::WRL; //ComPtr 34 | 35 | // TODO: define MS_ERROR_STR before including this header to handle module specific errors? 36 | #define MS_ENSURE(f, ...) MS_CHECK(f, return __VA_ARGS__;) 37 | #define MS_WARN(f) MS_CHECK(f) 38 | #define MS_CHECK(f, ...) do { \ 39 | while (FAILED(GetLastError())) {} \ 40 | HRESULT __ms_hr__ = (f); \ 41 | if (FAILED(__ms_hr__)) { \ 42 | std::clog << #f " ERROR@" << __LINE__ << __FUNCTION__ << ": (" << std::hex << __ms_hr__ << std::dec << ") " << std::error_code(__ms_hr__, std::system_category()).message() << std::endl; \ 43 | __VA_ARGS__ \ 44 | } \ 45 | } while (false) 46 | 47 | #define DX_ENSURE(f, ...) MS_CHECK(f, return __VA_ARGS__;) 48 | #define DX_WARN(f) MS_CHECK(f) 49 | 50 | 51 | using module_t = std::remove_pointer::type; 52 | using dll_t = std::unique_ptr; 53 | 54 | uint32_t to_fourcc(const GUID id); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mdk-mft 2 | [libmdk](https://github.com/wang-bin/mdk-sdk) codec plugin based on microsoft media foundation transform 3 | 4 | - D3D11, DXVA accelerated 5 | - h264, hevc, vp8/9, av1 decoder 6 | - aac, mp3, dolby decoder 7 | - sample pool for software decoder 8 | - minimize data copy for software decoder 9 | --------------------------------------------------------------------------------