Tuesday, November 10, 2009 11:07 AM
Maurits
How to turn on HDCP or SCMS in an audio playback app
There's an MSDN sample of how to turn on HDCP or SCMS in a playback app. The sample is loosely based on a test app I wrote, but there are still some rough edges. For example, the CMFAttributeImpl<T> template is not part of the SDK or the DDK. Also, there's a leak in the GetDigitalAudioEndpoint implementation. (Using the private template was my bug. The leak was someone else's.)
Exercise: find the leak.
There are also rough edges in the test app. For one thing, it doesn't play audio - the way to use the app is to
- Start playing audio using your favorite audio playback application, to the digital output on which want to turn on HDCP or SCMS.
- Run the trustedaudiodrivers2.exe command line to do what you want it to.
Source and binaries (x86 and amd64) attached.
>trustedaudiodrivers2
trustedaudiodrivers2 --list-devices
trustedaudiodrivers2
--device [ communications | console | multimedia | <device-name>]
--copy-ok [ 0 | 1 ]
--digital-output-disable [ 0 | 1 ]
--test-certificate-enable [ 0 | 1 ]
--drm-level <drm-level>
--list-devices: displays a list of output devices.
Sets a Trusted Audio Drivers 2 output policy on a given audio endpoint.
--device
communications: set policy on default communications render endpoint.
console: set policy on default console render endpoint.
multimedia: set policy on default multimedia render endpoint.
<device-name>: set policy on the endpoint with this long name.
--copy-ok: 1 or 0. Set to 0 to turn on "copy protection" (SCMS or HDCP)
--digital-output-disable: 1 or 0. Set to 1 to disable output (or turn on HDCP.)
--test-certificate-enable: 1 or 0. Set to 1 to allow the test certificate.
--drm-level: set to a number. 1100 is a good default.
Here's a sample command line which should disable the digital out (or enable HDCP, if the driver supports it.) This is line-wrapped for readability only.
>trustedaudiodrivers2.exe
--device "Acer H213H (High Definition Audio Device)"
--copy-ok 1
--digital-output-disable 1
--test-certificate-enable 1
--drm-level 1300
If all goes well the output should look something like this:
Output Trust Authorities on this endpoint: 2
Processing Output Trust Authority #1 of 2
OTA action is PEACTION_PLAY (1)
GetOriginatorID() called
GetOriginatorID() called
GenerateRequiredSchemas() called
dwAttributes: MFOUTPUTATTRIBUTE_DIGITAL (0x00000001)
guidOutputSubType: MFCONNECTOR_HDMI ({57CD596D-CE47-11D9-92DB-000BDB28FF98})
cProtectionSchemasSupported: 1
MFPROTECTION_TRUSTEDAUDIODRIVERS ({65BDF3D2-0168-4816-A533-55D47B027101})
MFPROTECTION_TRUSTEDAUDIODRIVERS supported.
IMFOutputSchema::GetSchemaType called
IMFOutputSchema::GetConfigurationData called
IMFOutputTrustAuthority::SetPolicy returned 0x00000000
Processing Output Trust Authority #2 of 2
OTA action is PEACTION_EXTRACT (4)
Skipping as the OTA action is not PEACTION_PLAY
Policy successfully applied. Press any key to release IMFTrustedOutput...
At that point I pressed a key, which redacted the policy, and the music started playing again.
Common causes of failures:
- No audio is playing to that device. Setting protection requires an active audio stream.
- The audio device is not S/PDIF or HDMI.
- The audio driver is not capable of enforcing the protection.
- The audio driver is test-signed only; try --test-certificate-enable 1
- Kernel debugging is enabled.
- Driver verifier is enabled.
This source should compile with just publically available headers and no special voodoo on the part of the developer.
EDIT: 11/13/2009 10:05 AM
A subtle problem was called to my attention. This line triggers an ATL ASSERT:
hr = pMFOutputTrustAuthority->SetPolicy(&pMFOutputPolicy, 1, &pbTicket, &cbTicket);
The ASSERT is in CComPtr<IMFOutputPolicy>::operator& which ASSERTs if the pointer CComPtr<IMFOutputPolicy>::p is not NULL.
My first instinct was to bypass the assertion like this:
hr = pMFOutputTrustAuthority->SetPolicy(&pMFOutputPolicy.p, 1, &pbTicket, &cbTicket);
But ASSERTs are there for a reason. The fundamental problem here is that I'm trying to be too clever.
Despite its name, IMFOutputTrustAuthority::SetPolicy takes, not an IMFOutputPolicy *, but an array of IMFOutputPolicy *s. A little confusing, yes. As this is effectively a sample, I should be emphasizing this behavior, not covering it up.
So here's the fix I decided on. Updated source and binaries attached.
// we're only setting a single policy but the API allows setting multiple policies
// (like WaitForMultipleObjects)
IMFOutputPolicy *rMFOutputPolicies[1] = { pMFOutputPolicy };
hr = pMFOutputTrustAuthority->SetPolicy(
rMFOutputPolicies,
ARRAYSIZE(rMFOutputPolicies),
&pbTicket,
&cbTicket
);