Closed cjw1115 closed 2 years ago
When generate a request in founction IMFContentDecryptionModuleSession::GenerateRequest, it failed with error code ‘0x8004c3e8’
Which version of Windows are you using? If you are on Win11, please use mftracelog to capture a trace when the error code occurs and add it to this issue.
Hi cjrog,
here is a simple full trace: simple_full_trace.zip
I use same CDM config in a UWP app, it works! After GenerateRequest, callback KeyMessage is called.
the only difference is storePath
//My AppContianer without package identify, so I create a LocalCache folder manually
configProperties.storagePath = L"C:\\Users\\xxx\\AppData\\Local\\Packages\\com-cq-test-container\\LocalCache\\";
// UWP with it's own LocalCache folder
configProperties.storagePath = winrt::Windows::Storage::ApplicationData::Current().LocalCacheFolder().Path().c_str();
And I found Edge browser use same API, but looks it work! Im not if there any special config.
Thanks
Now I obseved another error code“MSPR_E_REDIRECT" , a thread exited with that code, then a rpc timeout message printed in Output window.
When running outside a UWP application, you must invoke the following method which requires a number of pre-steps.
Most of the code you'll need to obtain an IMFPMPHostApp can be found in the storecdm sample in cdm.cpp inside Cdm_clearkey_IMFContentDecryptionModule::RuntimeClassInitialize and ::GetService.
You have to create a couple of property stores (with MediaProtectionSystemId set to the PlayReady system ID [which you can obtain from PlayReady statics, see link below] and key system string of "com.microsoft.playready.recommendation").
From there, create a IMediaProtectionPMPServerFactory, create a IMediaProtectionPMPServer, and from that obtain the IMFPMPHostApp which you then pass to SetPMPHostApp.
I hope this helps, -Sam Wenker Microsoft PlayReady Architect
Hi @swenkeratmicrosoft Thanks for your helpful information.
the sample code in storeCdm use IMFPMPHostApp
, if I use it too in my code, the line
hr = spIMFGetService->GetService(MF_PMP_SERVICE, IID_PPV_ARGS(pmpHostApp.put()));
will failed with message "0xc00d36ba : The object does not support the specified service.", but when I change IMFPMPHostApp to IMFPMPHost, it will be okay.
However, SetPMPHostApp
require interface IMFPMPHostApp
, I still can not get a IMFPMPHostApp
.
Here is code to create pmpserver:
winrt::Windows::Foundation::Collections::PropertySet playreadyProp;
playreadyProp.Insert(systemId, winrt::box_value(L"com.microsoft.playready.recommendation"));
winrt::Windows::Foundation::Collections::PropertySet pmpServerProp;
pmpServerProp.Insert(L"Windows.Media.Protection.MediaProtectionSystemId", winrt::box_value(systemId));
pmpServerProp.Insert(L"Windows.Media.Protection.MediaProtectionSystemIdMapping", playreadyProp);
//pmpServerProp.Insert(L"Windows.Media.Protection.PMPStoreContext", winrt::box_value(L"com-cq-test-container"));
// This line will create a subproecss "MFPMP.EXE"
winrt::Windows::Media::Protection::MediaProtectionPMPServer pmpServer{ pmpServerProp };
//ComPtr<IMFPMPHostApp> pmpHostApp;
winrt::com_ptr<IMFPMPHost> pmpHostApp;
winrt::com_ptr<IMFGetService> spIMFGetService = pmpServer.as<IMFGetService>();
hr = spIMFGetService->GetService(MF_PMP_SERVICE, IID_PPV_ARGS(pmpHostApp.put()));
This works in a Win32 App. but it will not work in an AppContianer. My final target is to wrap a win32 process in an AppContainer, like edge browser.
Do you have any other suggestion about getting a IMFPMPHostApp?
Thanks again!
I think that whether you can obtain an IMFPMPHostApp (using GetService above) actually depends on whether or not you are running in an app container. In other words, I think your application will most likely only function after you've done the work to wrap it in an app container.
Since you're planning to do that anyway, can you front-load the work to run your current win32 process inside an app container and see if obtaining IMFPMPHostAPp works in that scenario?
I hope this helps, -Sam Wenker Microsoft PlayReady Architect
I tried in three cases to GetService for IMFPMPHostApp
CreateAppContainerProfile
, has no package identityFor 1 and 3, always get error "WinRT originate error - 0xC00D36BA : 'The object does not support the specified service.'." For 2, it works as expected.
Look like that mfpmp server create IMFPMPHostApp or IMFPMPHost based on different app model. I'm confused why I created AppContainer by code can not get IMFPMPHostApp.
Do you know mfpmp server's internal behavior about this? how it judge app model? And edge browser also create AppContainer by code(found in chromium code base), curious why it works
From looking at the source code in the OS itself, the following is the check to determine whether IMFPMPHostApp can be returned. However, I don't know what actually causes this API to return "InboxOnly" versus "All". It's obviously something that the browser is doing - perhaps you can figure out what?
AppPolicyMediaFoundationCodecLoading policy;
AppPolicyGetMediaFoundationCodecLoading(GetCurrentThreadEffectiveToken(), &policy);
switch (policy)
{
case AppPolicyMediaFoundationCodecLoading_InboxOnly:
// this case allows IMFPMPHostApp
default:
// this case does not
-Sam Wenker Microsoft PlayReady Architect
Since edge also have this error:"0xC00D36BA : 'The object does not support the specified service.'" looks their AppPolicyMediaFoundationCodecLoading is all too. I also verified it in Chromium.
If I don't set SetPMPHostApp, just go to step GenerateRequest, it failed with code 0x8004c3e8. And in this step, I found it will access mspr.hds file multiple times and access Playready web service.
In the VS Output Panel, a code "0x8004b839(MSPR_E_REDIRECT)" is printed too. no sure which internal step failed!
Is their any chance to find out reason in my previous trace file. simple_full_trace.zip
Thanks.
Are you saying that when you debug Edge/Chrome, when it invokes the following code specifically to obtain the IMFPMPHostApp interface, it also gets 0xC00D36BA?
winrt::com_ptr
I ask because the error 0xC00D36BA is returned in dozens of places and 99% of them are expected and handled, so if you're just seeing that error code in the VS Output Panel when debugging Edge/Chrome, that's totally meaningless.
MSPR_E_REDIRECT is also expected and handled internally. It is benign and can be ignored.
Looking at the VS Output Panel for error codes in the OS is generally useless. Code like the following is extremely common in the OS. You'd see "E_FOO" in your VS Output Panel even though no end-user would ever be impacted by it.
hr = doStuff(); if( hr == S_OK ) { doOk(); } else if( hr == E_FOO ) { hr = S_OK; doFoo(); } else { fail... }
As far as I know, GenerateRequest is expected to fail with 0x8004c3e8 unless SetPMPHostApp is called in both the browser and in your scenario.
-Sam Wenker Microsoft PlayReady Architect
I've gotten some more information from another developer that will hopefully help us here.
I assume you already have the following pointer initialized to something.
winrt::com_ptr
First, try the following pseudocode. Note the use of MF_CDM_SERVICE and not MF_PMP_SERVICE.
winrt::com_ptr<IMFGetService> spIMFGetService = spIMFContentDecryptionModule.as<IMFGetService>();
winrt::com_ptr<IMFPMPHostApp> spIMFPMPHostApp;
hr = spIMFGetService->GetService(MF_CDM_SERVICE, IID_PPV_ARGS(spIMFPMPHostApp.put()));
// If hr succeeds, you've got your spIMFPMPHostApp and you can call SetPMPHostApp.
If that doesn't work, my next reply will have alternative (longer) pseudo-code to try.
-Sam Wenker Microsoft PlayReady Architect
//
// First, define a class that implements IMFPMPHostApp yourself
// It's implemented on top of IMFPMPHost itself
// This is obviously pseudocode, for example "CHK" isn't defined but it's where you'd do error handling for failed HRESULTs
//
class PMPHostWrapper : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, IMFPMPHostApp> {
public:
PMPHostWrapper() {}
~PMPHostWrapper() {}
HRESULT RuntimeClassInitialize(winrt::com_ptr<IMFPMPHost>& host) {
m_spIMFPMPHost = host.as(&m_spIMFPMPHost);
return S_OK;
}
STDMETHODIMP LockProcess() override {
return m_spIMFPMPHost->LockProcess();
}
STDMETHODIMP UnlockProcess() override {
return m_spIMFPMPHost->UnlockProcess();
}
STDMETHODIMP ActivateClassById(LPCWSTR id, IStream* stream, REFIID riid, void** activated_class) override {
HRESULT hr = S_OK;
wchar_t guid[MAX_PATH] = {};
StringFromGUID2(riid, guid, std::size(guid));
ComPtr<IMFAttributes> creation_attributes;
CHK(MFCreateAttributes(&creation_attributes, 3));
CHK(creation_attributes->SetString(GUID_ClassName, id));
if (stream) {
STATSTG statstg;
CHK(stream->Stat(&statstg, STATFLAG_NOOPEN | STATFLAG_NONAME));
std::vector<uint8_t> stream_blob(statstg.cbSize.LowPart);
unsigned long read_size = 0;
CHK(stream->Read(&stream_blob[0], stream_blob.size(), &read_size));
CHK(creation_attributes->SetBlob(GUID_ObjectStream, &stream_blob[0],
read_size));
}
// Serialize attributes
ComPtr<IStream> output_stream;
CHK(CreateStreamOnHGlobal(nullptr, TRUE, &output_stream));
CHK(MFSerializeAttributesToStream(creation_attributes.Get(), 0,
output_stream.Get()));
CHK(output_stream->Seek({}, STREAM_SEEK_SET, nullptr));
ComPtr<IMFActivate> activator;
CHK(m_spIMFPMPHost->CreateObjectByCLSID(
CLSID_EMEStoreActivate, output_stream.Get(), IID_PPV_ARGS(&activator)));
CHK(activator->ActivateObject(riid, activated_class));
return hr;
}
private:
winrt::com_ptr<IMFPMPHost> m_spIMFPMPHost;
};
// Again, note the use of MF_CDM_SERVICE and *not* MF_PMP_SERVICE.
winrt::com_ptr<IMFGetService> spIMFGetService = spIMFContentDecryptionModule.as<IMFGetService>();
winrt::com_ptr<IMFPMPHost> spIMFPMPHost;
hr = spIMFGetService->GetService(MF_CDM_SERVICE, IID_PPV_ARGS(spIMFPMPHost.put()));
// Assuming hr succeeds, you've got your spIMFPMPHost, so now create your wrapper...
winrt::com_ptr<IMFPMPHost> spIMFPMPHostApp;
hr = MakeAndInitialize<PMPHostWrapper>(&spIMFPMPHostApp, spIMFPMPHost);
// Assuming hr succeeds, you've got your spIMFPMPHostApp and you can call SetPMPHostApp.
Do either my previous reply or this reply get GenerateRequest working?
-Sam Wenker Microsoft PlayReady Architect
I can create a CMD(PMP Server)by interface
IMFContentDecryptionModuleAccess::CreateContentDecryptionModule
,
then, with below code, I can get a IMFPMPHost object from the CDM, but it can not be used to get object IMFPMPHostApp in AppContainer.
winrt::com_ptr<IMFPMPHost> pmpHost;
winrt::com_ptr<IMFGetService> cdmService = cdm.as<IMFGetService>();
hr = cdmService->GetService(MF_CONTENTDECRYPTIONMODULE_SERVICE, IID_PPV_ARGS(pmpHost.put()));
BTW, I can not find definition of MF_CDM_SERVICE, but I found MF_CONTENTDECRYPTIONMODULE_SERVICE, looks they are same thing.
However, this is a big progress since I don't need to use interface
Windows::Media::Protection::MediaProtectionPMPServer
to create PMP server again to obtain a IMFPMPHost object
After I can get a IMFPMPHost object from CDM. I tried to use PMPHostWrapper you provided. It works!!!
BTW, below three GUID are used in this wrapper. I can not find them in any header files until I google them.
I found two of them in https://gist.github.com/stevemk14ebr/af8053c506ef895cd520f8017a81f913, but for GUID_ObjectStream, I didn't find it.(Luckly, IStream is null in my case, so I don't need to use it now)
are they from Microsoft internal code? could you share them too?
Thanks again! The blocker here is removed! This issues can be closed!
Great! I'm so glad we were able to assist you and will close this issue with one last comment.
You are correct - those three required guids are not currently published in any public header, and that's a bug in the Windows SDK. I will follow-up internally here at Microsoft to get that fixed for a future version of the Windows SDK; most likely they will be placed in mfidl.idl / mfidl.h (but don't quote me). In the meantime, here they are.
EXTERN_GUID(GUID_ObjectStream, 0x3e73735c, 0xe6c0, 0x481d, 0x82, 0x60, 0xee, 0x5d, 0xb1, 0x34, 0x3b, 0x5f); EXTERN_GUID(GUID_ClassName, 0x77631a31, 0xe5e7, 0x4785, 0xbf, 0x17, 0x20, 0xf5, 0x7b, 0x22, 0x48, 0x02); EXTERN_GUID(CLSID_EMEStoreActivate, 0x2df7b51e, 0x797b, 0x4d06, 0xbe, 0x71, 0xd1, 0x4a, 0x52, 0xcf, 0x84, 0x21);
You are also correct that MF_CONTENTDECRYPTIONMODULE_SERVICE is the public version of MF_CDM_SERVICE. (They have the same underlying value.)
I really appreciate your patience while we worked through this with you. :)
-Sam Wenker Microsoft PlayReady Architect
Hi, I tried to create a AppContainer and use the sample code from MediaEngineEMEUWPSample, but I got below errors:
Exception thrown at 0x00007FF89C2D474C (KernelBase.dll) in MediaEngineEMEConsoleSample.exe: WinRT originate error - 0xC00D36BA : 'The object does not support the specified service.'.
however, the hresult is S_OK. Then I can continue other steps.Since there is no any docs on internet about this, could you help to help me from Microsoft side. Thanks!
BTW, I found MSEdge.exe also have #1 problem when I attach its process, but it can play Playready content by MediaFoundation successfully.