Introduction
WinRT and WRL introduce the requirement that most Media Foundation interfaces can no longer be directly created. If one wants to record audio or video through a sink, the only way to do so is with quite literally hundreds to thousands of lines of code in a custom sink. This prototype will get someone started towards achieving that with greater ease. Native C++ and C++/CX compatible. This is also compatible with non-WinRT/non-WRL uses since custom sinks could also be used prior to Windows 8 but generally shortcut interfaces were used.
Background
Knowledge of WinRT, WRL, C++, C++/CX, MediaCapture, Media Foundation, ATL and COM are recommended.
Using the Code
The block of code here is for all scenarios including non-WinRT/non-WRL cases where it is bound as an output node of a topology.
#ifdef _WINRT_DLL
#ifdef __cplusplus_winrt
Windows::Media::MediaProperties::VideoEncodingProperties^ vidProps;
Windows::Media::MediaProperties::AudioEncodingProperties^ audProps;
Windows::Media::MediaProperties::MediaEncodingProfile^ medEncProf =
ref new Windows::Media::MediaProperties::MediaEncodingProfile;
medEncProf->Video = vidProps;
medEncProf->Audio = audProps;
Windows::Foundation::Collections::IPropertySet^ pSet =
ref new Windows::Foundation::Collections::PropertySet();
Windows::Media::Capture::MediaCapture^ medCap;
HRESULT hr = medCap->StartPreviewToCustomSinkAsync(medEncProf,
ref new Platform::String(RuntimeClass_TimeStamp_WebCamSecExtensions_MediaSink), pSet);
hr = medCap->StopPreviewAsync();
#else
ABI::Windows::Media::Capture::IMediaCapture* medCap;
ABI::Windows::Media::MediaProperties::IVideoEncodingProperties* vidProps;
ABI::Windows::Media::MediaProperties::IAudioEncodingProperties* audProps;
ABI::Windows::Foundation::IAsyncAction** pAction;
HRESULT hr;
Microsoft::WRL::ComPtr<ABI::Windows::Media::Capture::IMediaCaptureVideoPreview> imedPrevCap;
if (pAction == nullptr) return E_POINTER;
hr = medCap->QueryInterface<ABI::Windows::Media::Capture::IMediaCaptureVideoPreview>(&imedPrevCap);
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<IActivationFactory> objFactory;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference
(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(), objFactory.ReleaseAndGetAddressOf());
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<IInspectable> medEncProf;
hr = objFactory->ActivateInstance(medEncProf.ReleaseAndGetAddressOf());
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<ABI::Windows::Media::MediaProperties::IMediaEncodingProfile> encProps;
hr = medEncProf.As<ABI::Windows::Media::MediaProperties::IMediaEncodingProfile>(&encProps);
if (FAILED(hr)) return hr;
hr = encProps->put_Video(vidProps);
if (FAILED(hr)) return hr;
hr = encProps->put_Audio(audProps);
if (FAILED(hr)) return hr;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference
(RuntimeClass_Windows_Foundation_Collections_PropertySet).Get(), objFactory.ReleaseAndGetAddressOf());
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<IInspectable> pSet;
hr = objFactory->ActivateInstance(pSet.GetAddressOf());
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IPropertySet> pProps;
hr = pSet.As(&pProps);
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable *>> spSetting;
hr = pSet.As(&spSetting);
if (FAILED(hr)) return hr;
hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference
(RuntimeClass_Windows_Foundation_PropertyValue).Get(), objFactory.ReleaseAndGetAddressOf());
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValueStatics> spPropVal;
hr = objFactory.As(&spPropVal);
if (FAILED(hr)) return hr;
hr = imedPrevCap->StartPreviewToCustomSinkIdAsync(encProps.Get(), Microsoft::WRL::Wrappers::HStringReference
(RuntimeClass_TimeStamp_WebCamSecExtensions_MediaSink).Get(), pProps.Get(), pAction);
hr = imedPrevCap->StopPreviewAsync(pAction);
#endif
#else
CComPtr<IMFTopologyNode> pNode; HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
if (SUCCEEDED(hr)) {
CComPtr<IMFMediaSink> pSink;
hr = TimeStamp::WebCamSecExtensions::MediaSink::CreateInstance<IMFMediaSink>(&pSink);
if (FAILED(hr)) return hr;
CComPtr<IMFAttributes> pAttr;
hr = pSink->QueryInterface(&pAttr);
if (FAILED(hr)) return hr;
CComPtr<IMFMediaTypeHandler> pHandler; hr = pSD->GetMediaTypeHandler(&pHandler);
if (FAILED(hr)) return hr;
CComPtr<IMFMediaType> pMediaType;
hr = pHandler->GetCurrentMediaType(&pMediaType);
if (FAILED(hr)) return hr;
hr = pAttr->SetUnknown(MF_MEDIASINK_PREFERREDTYPE, pMediaType.Detach());
if (FAILED(hr)) return hr;
hr = pNode->SetObject(pSink);
}
#endif
Points of Interest
Passing parameters is done through a PropertyBag
in WinRT/WRL yet through the IMFAttribute
interface in non-WinRT/non-WRL code which can be found through IUnknown QueryInterface
on the Custom Sink object.
An object of type ICustomSinkCallback
must be passed and the interface implemented to utilize the features including initialization, cleanup, property change notification, preparing for samples and sample processing.
When initializing the custom sink, if planning to reuse the MediaCapture
object, then be sure to pass the activation name to StartPreviewToCustomSinkAsync
and not an instance of a CustomSink
object. There is a bug in the MediaCapture
library which causes it to become in a bad state if not using the activation ID which is present in Windows 8 and Windows 8.1.
The code is compatible with Visual Studio 2012/2013/2015 and Windows 8/8.1/10.
History