Skip to content

Instantly share code, notes, and snippets.

@Arlorean
Created October 15, 2017 13:12
Show Gist options
  • Save Arlorean/ac7168a86ae1daed53f41c900dd05252 to your computer and use it in GitHub Desktop.
Save Arlorean/ac7168a86ae1daed53f41c900dd05252 to your computer and use it in GitHub Desktop.
/*
* Copyright (C) 2013 Gotham City. All rights reserved.
* Copyright (C) 2017 Arlorean. All rights reserved. (C#/MediaFoundation.NET port)
*/
using System;
using System.Diagnostics;
using MediaFoundation;
using MediaFoundation.Misc;
using static MediaFoundation.CLSID;
using static MediaFoundation.MFExtern;
using static MediaFoundation.MFAttributesClsid;
using BOOL = System.Boolean;
using DWORD = System.Int32;
using HWND = System.IntPtr;
namespace TestWMF {
class Program {
/// <summary>
/// Display the web camera output in a console window.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args) {
testSource();
testSink();
}
static void testSource() {
HResult hr;
IMFMediaSource pIMFSource = null;
IMFMediaSession pIMFSession = null;
IMFTopology pIMFTopology = null;
HWND hVideoWnd = Process.GetCurrentProcess().MainWindowHandle;
IMFPresentationDescriptor pIMFPD = null;
// Create the Custom Source
CHECK_HR(hr = CreateVideoDeviceSource(out pIMFSource));
// Create the Presentation Descriptor
CHECK_HR(hr = pIMFSource.CreatePresentationDescriptor(out pIMFPD));
// Create the Media Session
CHECK_HR(hr = MFCreateMediaSession(null, out pIMFSession));
// Create the Playback Topology
CHECK_HR(hr = CreatePlaybackTopology(pIMFSource, pIMFPD, hVideoWnd, out pIMFTopology));
// Run Session
CHECK_HR(hr = RunSession(pIMFSession, pIMFTopology));
{
IMFMediaEvent pEvent = null;
while (true) {
HResult hrStatus = HResult.S_OK;
MediaEventType met;
CHECK_HR(hr = pIMFSession.GetEvent(0, out pEvent));
CHECK_HR(hr = pEvent.GetStatus(out hrStatus));
CHECK_HR(hr = pEvent.GetType(out met));
if (FAILED(hrStatus)) {
switch (hrStatus) {
case HResult.MF_E_TOPO_CODEC_NOT_FOUND: printf("MF_E_TOPO_CODEC_NOT_FOUND"); break;
case HResult.MF_E_NO_SAMPLE_TIMESTAMP: printf("MF_E_NO_SAMPLE_TIMESTAMP"); break;
}
printf("Session error: 0x{0:x} (event id: {1:d})\n", hrStatus, met);
hr = hrStatus;
goto done;
}
if (met == MediaEventType.MESessionEnded) {
break;
}
SafeRelease(pEvent);
}
}
done:
if (pIMFSession != null) {
pIMFSession.Shutdown();
}
if (pIMFSource != null) {
pIMFSource.Shutdown();
}
SafeRelease(pIMFSource);
SafeRelease(pIMFSession);
SafeRelease(pIMFPD);
SafeRelease(pIMFTopology);
}
static void testSink() {
}
// Create a playback topology from a media source.
static HResult CreatePlaybackTopology(
IMFMediaSource pSource, // Media source.
IMFPresentationDescriptor pPD, // Presentation descriptor.
HWND hVideoWnd, // Video window.
out IMFTopology ppTopology) // Receives a pointer to the topology.
{
ppTopology = null;
IMFTopology pTopology = null;
DWORD cSourceStreams = 0;
// Create a new topology.
HResult hr = MFCreateTopology(out pTopology);
if (FAILED(hr)) {
goto done;
}
// Get the number of streams in the media source.
hr = pPD.GetStreamDescriptorCount(out cSourceStreams);
if (FAILED(hr)) {
goto done;
}
// For each stream, create the topology nodes and add them to the topology.
for (DWORD i = 0; i < cSourceStreams; i++) {
hr = AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
if (FAILED(hr)) {
goto done;
}
}
// Return the IMFTopology pointer to the caller.
ppTopology = pTopology;
//(*ppTopology).AddRef();
done:
//SafeRelease(pTopology);
return hr;
}
// Add a topology branch for one stream.
//
// For each stream, this function does the following:
//
// 1. Creates a source node associated with the stream.
// 2. Creates an output node for the renderer.
// 3. Connects the two nodes.
//
// The media session will add any decoders that are needed.
static HResult AddBranchToPartialTopology(
IMFTopology pTopology, // Topology.
IMFMediaSource pSource, // Media source.
IMFPresentationDescriptor pPD, // Presentation descriptor.
DWORD iStream, // Stream index.
HWND hVideoWnd) // Window for video playback.
{
IMFStreamDescriptor pSD = null;
IMFActivate pSinkActivate = null;
IMFTopologyNode pSourceNode = null;
IMFTopologyNode pOutputNode = null;
BOOL fSelected = false;
HResult hr = pPD.GetStreamDescriptorByIndex(iStream, out fSelected, out pSD);
if (FAILED(hr)) {
goto done;
}
if (fSelected) {
// Create the media sink activation object.
hr = CreateMediaSinkActivate(pSD, hVideoWnd, out pSinkActivate);
if (FAILED(hr)) {
goto done;
}
// Add a source node for this stream.
hr = AddSourceNode(pTopology, pSource, pPD, pSD, out pSourceNode);
if (FAILED(hr)) {
goto done;
}
// Create the output node for the renderer.
hr = AddOutputNode(pTopology, pSinkActivate, 0, out pOutputNode);
if (FAILED(hr)) {
goto done;
}
// Connect the source node to the output node.
hr = pSourceNode.ConnectOutput(0, pOutputNode, 0);
}
// else: If not selected, don't add the branch.
done:
SafeRelease(pSD);
SafeRelease(pSinkActivate);
SafeRelease(pSourceNode);
SafeRelease(pOutputNode);
return hr;
}
// Create an activation object for a renderer, based on the stream media type.
static HResult CreateMediaSinkActivate(
IMFStreamDescriptor pSourceSD, // Pointer to the stream descriptor.
HWND hVideoWindow, // Handle to the video clipping window.
out IMFActivate ppActivate
) {
ppActivate = null;
IMFMediaTypeHandler pHandler = null;
IMFActivate pActivate = null;
// Get the media type handler for the stream.
HResult hr = pSourceSD.GetMediaTypeHandler(out pHandler);
if (FAILED(hr)) {
goto done;
}
// Get the major media type.
Guid guidMajorType;
hr = pHandler.GetMajorType(out guidMajorType);
if (FAILED(hr)) {
goto done;
}
// Create an IMFActivate object for the renderer, based on the media type.
if (MFMediaType.Audio == guidMajorType) {
// Create the audio renderer.
hr = MFCreateAudioRendererActivate(out pActivate);
}
else if (MFMediaType.Video == guidMajorType) {
// Create the video renderer.
hr = MFCreateVideoRendererActivate(hVideoWindow, out pActivate);
}
else {
// Unknown stream type.
hr = HResult.E_FAIL;
// Optionally, you could deselect this stream instead of failing.
}
if (FAILED(hr)) {
goto done;
}
// Return IMFActivate pointer to caller.
ppActivate = pActivate;
//(*ppActivate).AddRef();
done:
SafeRelease(pHandler);
//SafeRelease(pActivate);
return hr;
}
// Add a source node to a topology.
static HResult AddSourceNode(
IMFTopology pTopology, // Topology.
IMFMediaSource pSource, // Media source.
IMFPresentationDescriptor pPD, // Presentation descriptor.
IMFStreamDescriptor pSD, // Stream descriptor.
out IMFTopologyNode ppNode) // Receives the node pointer.
{
IMFTopologyNode pNode = null;
HResult hr = HResult.S_OK;
CHECK_HR(hr = MFCreateTopologyNode(MFTopologyType.SourcestreamNode, out pNode));
CHECK_HR(hr = pNode.SetUnknown(MF_TOPONODE_SOURCE, pSource));
CHECK_HR(hr = pNode.SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD));
CHECK_HR(hr = pNode.SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD));
CHECK_HR(hr = pTopology.AddNode(pNode));
// Return the pointer to the caller.
ppNode = pNode;
//(*ppNode).AddRef();
done:
//SafeRelease(pNode);
return hr;
}
// Add an output node to a topology.
static HResult AddOutputNode(
IMFTopology pTopology, // Topology.
IMFActivate pActivate, // Media sink activation object.
DWORD dwId, // Identifier of the stream sink.
out IMFTopologyNode ppNode) // Receives the node pointer.
{
IMFTopologyNode pNode = null;
HResult hr = HResult.S_OK;
CHECK_HR(hr = MFCreateTopologyNode(MFTopologyType.OutputNode, out pNode));
CHECK_HR(hr = pNode.SetObject(pActivate));
CHECK_HR(hr = pNode.SetUINT32(MF_TOPONODE_STREAMID, dwId));
CHECK_HR(hr = pNode.SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, 0/*FALSE*/));
CHECK_HR(hr = pTopology.AddNode(pNode));
// Return the pointer to the caller.
ppNode = pNode;
//(*ppNode).AddRef();
done:
//SafeRelease(pNode);
return hr;
}
// Run session
static HResult RunSession(IMFMediaSession pSession, IMFTopology pTopology) {
IMFMediaEvent pEvent = null;
PropVariant var = new PropVariant();
MediaEventType met;
HResult hrStatus = HResult.S_OK;
HResult hr = HResult.S_OK;
CHECK_HR(hr = pSession.SetTopology(0, pTopology));
CHECK_HR(hr = pSession.Start(Guid.Empty, var));
// Check first event
hr = pSession.GetEvent(MFEventFlag.NoWait, out pEvent);
if (hr == HResult.MF_E_NO_EVENTS_AVAILABLE) {
hr = HResult.S_OK;
goto done;
}
CHECK_HR(hr = pEvent.GetStatus(out hrStatus));
if (FAILED(hrStatus)) {
CHECK_HR(hr = pEvent.GetType(out met));
printf("Session error: 0x{0:x} (event id: {1:d})\n", hrStatus, met);
hr = hrStatus;
goto done;
}
done:
SafeRelease(pEvent);
return hr;
}
static HResult CreateVideoDeviceSource(out IMFMediaSource ppSource) {
ppSource = null;
IMFMediaSource pSource = null;
IMFAttributes pAttributes = null;
IMFActivate[] ppDevices = null;
DWORD count = 0;
// Create an attribute store to specify the enumeration parameters.
HResult hr = MFCreateAttributes(out pAttributes, 1);
if (FAILED(hr)) {
goto done;
}
// Source type: video capture devices
hr = pAttributes.SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
);
if (FAILED(hr)) {
goto done;
}
// Enumerate devices.
hr = MFEnumDeviceSources(pAttributes, out ppDevices, out count);
if (FAILED(hr)) {
goto done;
}
if (count == 0) {
hr = HResult.E_FAIL;
goto done;
}
// Create the media source object.
hr = ppDevices[0].ActivateObject(out pSource);
if (FAILED(hr)) {
goto done;
}
ppSource = pSource;
//ppSource.AddRef();
done:
SafeRelease(pAttributes);
for (var i = 0; i < count; i++) {
SafeRelease(ppDevices[i]);
}
//CoTaskMemFree(ppDevices);
//SafeRelease(pSource);
return hr;
}
static void printf(string s) {
Console.WriteLine(s);
}
static void printf(string format, params object[] args) {
Console.WriteLine(format, args);
}
static void CHECK_HR(HResult hr) {
MFError.ThrowExceptionForHR(hr);
}
static bool FAILED(HResult hr) {
return COMBase.Failed(hr);
}
static void SafeRelease(object obj) {
COMBase.SafeRelease(obj);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment