Hi,
I am new to media foundation and currently building a live streaming application based on basic playback.
For the application i create a custom media source since the video frames i receive contain additional header around them and also i need to perform some handshaking with the camera after which i start receiving the video frames.
So using media foundation i managed to create my custom source, source node and ouput node both interconnected.
However i get session topology set error.
Error says:-> (HRESULT = 0xc00d36e6 The requested attribute was not found.)
Also i am using MediaFoundationWrapper from Mediafoundation.Net
I have included my code blocks below
Player class
/****************************************************************************
While the underlying libraries are covered by LGPL, this sample is released
as public domain. It is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
*****************************************************************************/
using System;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using MediaFoundation;
using MediaFoundation.EVR;
using MediaFoundation.Misc;
using System.IO;
using LiveSource;
class CPlayer : COMBase, IMFAsyncCallback
{
#region externs
[DllImport("user32", CharSet = CharSet.Auto)]
private extern static int PostMessage(
IntPtr handle, int msg, IntPtr wParam, IntPtr lParam);
#endregion
#region Declarations
const int WM_APP = 0x8000;
const int WM_APP_ERROR = WM_APP + 2;
const int WM_APP_NOTIFY = WM_APP + 1;
const int WAIT_TIMEOUT = 258;
const int MF_VERSION = 0x10070;
public enum PlayerState
{
Ready = 0,
OpenPending,
Started,
PausePending,
Paused,
StartPending,
}
#endregion
public CPlayer(IntPtr hVideo, IntPtr hEvent)
{
TRACE(("CPlayer::CPlayer"));
Debug.Assert(hVideo != IntPtr.Zero);
Debug.Assert(hEvent != IntPtr.Zero);
m_pSession = null;
m_pSource = null;
m_pVideoDisplay = null;
m_hwndVideo = hVideo;
m_hwndEvent = hEvent;
m_state = PlayerState.Ready;
m_hCloseEvent = new AutoResetEvent(false);
int hr = MFExtern.MFStartup(0x10070, MFStartup.Full);
MFError.ThrowExceptionForHR(hr);
}
#if DEBUG
// Destructor is private. Caller should call Release.
~CPlayer()
{
Debug.Assert(m_pSession == null); // If FALSE, the app did not call Shutdown().
}
#endif
#region Public methods
public int OpenURL(string sURL)
{
TRACE("CPlayer::OpenURL");
TRACE("URL = " + sURL);
// 1. Create a new media session.
// 2. Create the media source.
// 3. Create the topology.
// 4. Queue the topology [asynchronous]
// 5. Start playback [asynchronous - does not happen in this method.]
int hr = S_Ok;
try
{
IMFTopology pTopology = null;
// Create the media session.
CreateSession();
// Create the media source.
CreateMediaSource(sURL);
// Create a partial topology.
CreateTopologyFromSource(out pTopology);
// Set the topology on the media session.
hr = m_pSession.SetTopology(0, pTopology);
MFError.ThrowExceptionForHR(hr);
// Set our state to "open pending"
m_state = PlayerState.OpenPending;
NotifyState();
SafeRelease(pTopology);
// If SetTopology succeeded, the media session will queue an
// MESessionTopologySet event.
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
NotifyError(hr);
m_state = PlayerState.Ready;
}
return hr;
}
public int PlayLiveStream()
{
TRACE("CPlayer::OpenURL");
//TRACE("URL = " + sURL);
// 1. Create a new media session.
// 2. Create the media source.
// 3. Create the topology.
// 4. Queue the topology [asynchronous]
// 5. Start playback [asynchronous - does not happen in this method.]
int hr = S_Ok;
try
{
IMFTopology pTopology = null;
// Create the media session.
CreateSession();
// Create the media source.
CreateLiveMediasource();
// Create a partial topology.
CreateTopologyFromSource(out pTopology);
// Set the topology on the media session.
hr = m_pSession.SetTopology(0, pTopology);
MFError.ThrowExceptionForHR(hr);
// ((LiveMediaSource)m_pSource).Start();
// Set our state to "open pending"
m_state = PlayerState.Ready;
NotifyState();
SafeRelease(pTopology);
// If SetTopology succeeded, the media session will queue an
// MESessionTopologySet event.
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
NotifyError(hr);
m_state = PlayerState.Ready;
}
return hr;
}
public int Play()
{
TRACE("CPlayer::Play");
if (m_state != PlayerState.Paused)
{
return E_Fail;
}
if (m_pSession == null || m_pSource == null)
{
return E_Unexpected;
}
int hr = S_Ok;
try
{
StartPlayback();
m_state = PlayerState.StartPending;
NotifyState();
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
NotifyError(hr);
}
return hr;
}
public int Pause()
{
TRACE("CPlayer::Pause");
if (m_state != PlayerState.Started)
{
return E_Fail;
}
if (m_pSession == null || m_pSource == null)
{
return E_Unexpected;
}
int hr = S_Ok;
try
{
hr = m_pSession.Pause();
MFError.ThrowExceptionForHR(hr);
m_state = PlayerState.PausePending;
NotifyState();
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
NotifyError(hr);
}
return hr;
}
public int Shutdown()
{
TRACE("CPlayer::ShutDown");
int hr = S_Ok;
try
{
if (m_hCloseEvent != null)
{
// Close the session
CloseSession();
// Shutdown the Media Foundation platform
hr = MFExtern.MFShutdown();
MFError.ThrowExceptionForHR(hr);
m_hCloseEvent.Close();
m_hCloseEvent = null;
}
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
}
return hr;
}
// Video functionality
public int Repaint()
{
int hr = S_Ok;
if (m_pVideoDisplay != null)
{
try
{
hr = m_pVideoDisplay.RepaintVideo();
MFError.ThrowExceptionForHR(hr);
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
}
}
return hr;
}
public int ResizeVideo(short width, short height)
{
int hr = S_Ok;
TRACE(string.Format("ResizeVideo: {0}x{1}", width, height));
if (m_pVideoDisplay != null)
{
try
{
MFRect rcDest = new MFRect();
MFVideoNormalizedRect nRect = new MFVideoNormalizedRect();
nRect.left = 0;
nRect.right = 1;
nRect.top = 0;
nRect.bottom = 1;
rcDest.left = 0;
rcDest.top = 0;
rcDest.right = width;
rcDest.bottom = height;
hr = m_pVideoDisplay.SetVideoPosition(nRect, rcDest);
MFError.ThrowExceptionForHR(hr);
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
}
}
return hr;
}
public PlayerState GetState()
{
return m_state;
}
public bool HasVideo()
{
return (m_pVideoDisplay != null);
}
#endregion
#region IMFAsyncCallback Members
int IMFAsyncCallback.GetParameters(out MFASync pdwFlags, out MFAsyncCallbackQueue pdwQueue)
{
pdwFlags = MFASync.FastIOProcessingCallback;
pdwQueue = MFAsyncCallbackQueue.Standard;
//throw new COMException("IMFAsyncCallback.GetParameters not implemented in Player", E_NotImplemented);
return S_Ok;
}
int IMFAsyncCallback.Invoke(IMFAsyncResult pResult)
{
int hr;
IMFMediaEvent pEvent = null;
MediaEventType meType = MediaEventType.MEUnknown; // Event type
int hrStatus = 0; // Event status
MFTopoStatus TopoStatus = MFTopoStatus.Invalid; // Used with MESessionTopologyStatus event.
try
{
// Get the event from the event queue.
hr = m_pSession.EndGetEvent(pResult, out pEvent);
MFError.ThrowExceptionForHR(hr);
// Get the event type.
hr = pEvent.GetType(out meType);
MFError.ThrowExceptionForHR(hr);
// Get the event status. If the operation that triggered the event did
// not succeed, the status is a failure code.
hr = pEvent.GetStatus(out hrStatus);
MFError.ThrowExceptionForHR(hr);
TRACE(string.Format("Media event: " + meType.ToString()));
// Check if the async operation succeeded.
if (Succeeded(hrStatus))
{
// Switch on the event type. Update the internal state of the CPlayer as needed.
switch (meType)
{
case MediaEventType.MESessionTopologyStatus:
// Get the status code.
int i;
hr = pEvent.GetUINT32(MFAttributesClsid.MF_EVENT_TOPOLOGY_STATUS, out i);
MFError.ThrowExceptionForHR(hr);
TopoStatus = (MFTopoStatus)i;
switch (TopoStatus)
{
case MFTopoStatus.Ready:
OnTopologyReady(pEvent);
break;
default:
// Nothing to do.
break;
}
break;
case MediaEventType.MESessionStarted:
OnSessionStarted(pEvent);
break;
case MediaEventType.MESessionPaused:
OnSessionPaused(pEvent);
break;
case MediaEventType.MESessionClosed:
OnSessionClosed(pEvent);
break;
case MediaEventType.MEEndOfPresentation:
OnPresentationEnded(pEvent);
break;
}
}
else
{
// The async operation failed. Notify the application
NotifyError(hrStatus);
}
}
finally
{
// Request another event.
if (meType != MediaEventType.MESessionClosed)
{
hr = m_pSession.BeginGetEvent(this, null);
MFError.ThrowExceptionForHR(hr);
}
SafeRelease(pEvent);
}
return S_Ok;
}
#endregion
#region Protected methods
// NotifyState: Notifies the application when the state changes.
protected void NotifyState()
{
PostMessage(m_hwndEvent, WM_APP_NOTIFY, new IntPtr((int)m_state), IntPtr.Zero);
}
// NotifyState: Notifies the application when an error occurs.
protected void NotifyError(int hr)
{
TRACE("NotifyError: 0x" + hr.ToString("X"));
m_state = PlayerState.Ready;
PostMessage(m_hwndEvent, WM_APP_ERROR, new IntPtr(hr), IntPtr.Zero);
}
protected void CreateSession()
{
// Close the old session, if any.
CloseSession();
// Create the media session.
int hr = MFExtern.MFCreateMediaSession(null, out m_pSession);
MFError.ThrowExceptionForHR(hr);
// Start pulling events from the media session
hr = m_pSession.BeginGetEvent(this, null);
MFError.ThrowExceptionForHR(hr);
}
protected void CloseSession()
{
int hr;
if (m_pVideoDisplay != null)
{
Marshal.ReleaseComObject(m_pVideoDisplay);
m_pVideoDisplay = null;
}
if (m_pSession != null)
{
hr = m_pSession.Close();
MFError.ThrowExceptionForHR(hr);
// Wait for the close operation to complete
bool res = m_hCloseEvent.WaitOne(5000, true);
if (!res)
{
TRACE(("WaitForSingleObject timed out!"));
}
}
// Complete shutdown operations
// 1. Shut down the media source
if (m_pSource != null)
{
hr = m_pSource.Shutdown();
MFError.ThrowExceptionForHR(hr);
SafeRelease(m_pSource);
m_pSource = null;
}
// 2. Shut down the media session. (Synchronous operation, no events.)
if (m_pSession != null)
{
hr = m_pSession.Shutdown();
MFError.ThrowExceptionForHR(hr);
Marshal.ReleaseComObject(m_pSession);
m_pSession = null;
}
}
protected void StartPlayback()
{
TRACE("CPlayer::StartPlayback");
Debug.Assert(m_pSession != null);
int hr = m_pSession.Start(Guid.Empty, new PropVariant());
MFError.ThrowExceptionForHR(hr);
}
protected void CreateMediaSource(string sURL)
{
TRACE("CPlayer::CreateMediaSource");
IMFSourceResolver pSourceResolver;
object pSource;
// Create the source resolver.
int hr = MFExtern.MFCreateSourceResolver(out pSourceResolver);
MFError.ThrowExceptionForHR(hr);
try
{
// Use the source resolver to create the media source.
MFObjectType ObjectType = MFObjectType.Invalid;
hr = pSourceResolver.CreateObjectFromURL(
sURL, // URL of the source.
MFResolution.MediaSource, // Create a source object.
null, // Optional property store.
out ObjectType, // Receives the created object type.
out pSource // Receives a pointer to the media source.
);
MFError.ThrowExceptionForHR(hr);
// Get the IMFMediaSource interface from the media source.
m_pSource = (IMFMediaSource)pSource;
}
finally
{
// Clean up
Marshal.ReleaseComObject(pSourceResolver);
}
}
protected void CreateLiveMediasource()
{
TRACE("CPlayer::CreateMediaSource");
try
{
// Get the IMFMediaSource interface from the media source.
m_pSource = new LiveMediaSource();
}
finally
{
}
}
protected void CreateTopologyFromSource(out IMFTopology ppTopology)
{
TRACE("CPlayer::CreateTopologyFromSource");
Debug.Assert(m_pSession != null);
Debug.Assert(m_pSource != null);
IMFTopology pTopology = null;
IMFPresentationDescriptor pSourcePD = null;
int cSourceStreams = 0;
int hr;
try
{
// Create a new topology.
hr = MFExtern.MFCreateTopology(out pTopology);
MFError.ThrowExceptionForHR(hr);
// Create the presentation descriptor for the media source.
hr = m_pSource.CreatePresentationDescriptor(out pSourcePD);
MFError.ThrowExceptionForHR(hr);
// Get the number of streams in the media source.
hr = pSourcePD.GetStreamDescriptorCount(out cSourceStreams);
MFError.ThrowExceptionForHR(hr);
TRACE(string.Format("Stream count: {0}", cSourceStreams));
// For each stream, create the topology nodes and add them to the topology.
for (int i = 0; i < cSourceStreams; i++)
{
AddBranchToPartialTopology(pTopology, pSourcePD, i);
}
// Return the IMFTopology pointer to the caller.
ppTopology = pTopology;
}
catch
{
// If we failed, release the topology
SafeRelease(pTopology);
throw;
}
finally
{
SafeRelease(pSourcePD);
}
}
protected void AddBranchToPartialTopology(IMFTopology pTopology, IMFPresentationDescriptor pSourcePD, int iStream)
{
int hr;
TRACE("CPlayer::AddBranchToPartialTopology");
Debug.Assert(pTopology != null);
IMFStreamDescriptor pSourceSD = null;
IMFTopologyNode pSourceNode = null;
IMFTopologyNode pOutputNode = null;
bool fSelected = false;
try
{
// Get the stream descriptor for this stream.
hr = pSourcePD.GetStreamDescriptorByIndex(iStream, out fSelected, out pSourceSD);
MFError.ThrowExceptionForHR(hr);
// Create the topology branch only if the stream is selected.
// Otherwise, do nothing.
if (fSelected)
{
// Create a source node for this stream.
CreateSourceStreamNode(pSourcePD, pSourceSD, out pSourceNode);
// Create the output node for the renderer.
CreateOutputNode(pSourceSD, out pOutputNode);
// Add both nodes to the topology.
hr = pTopology.AddNode(pSourceNode);
MFError.ThrowExceptionForHR(hr);
hr = pTopology.AddNode(pOutputNode);
MFError.ThrowExceptionForHR(hr);
// Connect the source node to the output node.
hr = pSourceNode.ConnectOutput(0, pOutputNode, 0);
MFError.ThrowExceptionForHR(hr);
}
}
finally
{
// Clean up.
SafeRelease(pSourceSD);
SafeRelease(pSourceNode);
SafeRelease(pOutputNode);
}
}
protected void CreateSourceStreamNode(IMFPresentationDescriptor pSourcePD, IMFStreamDescriptor pSourceSD, out IMFTopologyNode ppNode)
{
Debug.Assert(m_pSource != null);
int hr;
IMFTopologyNode pNode = null;
try
{
// Create the source-stream node.
hr = MFExtern.MFCreateTopologyNode(MFTopologyType.SourcestreamNode, out pNode);
MFError.ThrowExceptionForHR(hr);
// Set attribute: Pointer to the media source.
hr = pNode.SetUnknown(MFAttributesClsid.MF_TOPONODE_SOURCE, m_pSource);
MFError.ThrowExceptionForHR(hr);
// Set attribute: Pointer to the presentation descriptor.
hr = pNode.SetUnknown(MFAttributesClsid.MF_TOPONODE_PRESENTATION_DESCRIPTOR, pSourcePD);
MFError.ThrowExceptionForHR(hr);
// Set attribute: Pointer to the stream descriptor.
hr = pNode.SetUnknown(MFAttributesClsid.MF_TOPONODE_STREAM_DESCRIPTOR, pSourceSD);
MFError.ThrowExceptionForHR(hr);
// Return the IMFTopologyNode pointer to the caller.
ppNode = pNode;
}
catch
{
// If we failed, release the pnode
SafeRelease(pNode);
throw;
}
}
protected void CreateOutputNode(IMFStreamDescriptor pSourceSD, out IMFTopologyNode ppNode)
{
IMFTopologyNode pNode = null;
IMFMediaTypeHandler pHandler = null;
IMFActivate pRendererActivate = null;
Guid guidMajorType = Guid.Empty;
int hr = S_Ok;
// Get the stream ID.
int streamID = 0;
try
{
try
{
hr = pSourceSD.GetStreamIdentifier(out streamID); // Just for debugging, ignore any failures.
MFError.ThrowExceptionForHR(hr);
}
catch
{
TRACE("IMFStreamDescriptor::GetStreamIdentifier" + hr.ToString());
}
// Get the media type handler for the stream.
hr = pSourceSD.GetMediaTypeHandler(out pHandler);
MFError.ThrowExceptionForHR(hr);
// get hte major type for the media type using the handler
hr = pHandler.GetMajorType(out guidMajorType);
MFError.ThrowExceptionForHR(hr);
// Create a downstream node.
hr = MFExtern.MFCreateTopologyNode(MFTopologyType.OutputNode, out pNode);
MFError.ThrowExceptionForHR(hr);
// Create an IMFActivate object for the renderer, based on the media type.
if (MFMediaType.Audio == guidMajorType)
{
// Create the audio renderer.
TRACE(string.Format("Stream {0}: audio stream", streamID));
hr = MFExtern.MFCreateAudioRendererActivate(out pRendererActivate);
MFError.ThrowExceptionForHR(hr);
}
else if (MFMediaType.Video == guidMajorType)
{
// Create the video renderer.
TRACE(string.Format("Stream {0}: video stream", streamID));
hr = MFExtern.MFCreateVideoRendererActivate(m_hwndVideo, out pRendererActivate);
MFError.ThrowExceptionForHR(hr);
}
else
{
TRACE(string.Format("Stream {0}: Unknown format", streamID));
throw new COMException("Unknown format", E_Fail);
}
// Set the IActivate object on the output node.
hr = pNode.SetObject(pRendererActivate);
MFError.ThrowExceptionForHR(hr);
// Return the IMFTopologyNode pointer to the caller.
ppNode = pNode;
}
catch
{
// If we failed, release the pNode
SafeRelease(pNode);
throw;
}
finally
{
// Clean up.
SafeRelease(pHandler);
SafeRelease(pRendererActivate);
}
}
// Media event handlers
protected void OnTopologyReady(IMFMediaEvent pEvent)
{
int hr;
object o;
TRACE("CPlayer::OnTopologyReady");
// Ask for the IMFVideoDisplayControl interface.
// This interface is implemented by the EVR and is
// exposed by the media session as a service.
// Note: This call is expected to fail if the source
// does not have video.
try
{
hr = MFExtern.MFGetService(
m_pSession,
MFServices.MR_VIDEO_RENDER_SERVICE,
typeof(IMFVideoDisplayControl).GUID,
out o
);
MFError.ThrowExceptionForHR(hr);
m_pVideoDisplay = o as IMFVideoDisplayControl;
}
catch (InvalidCastException)
{
m_pVideoDisplay = null;
}
try
{
StartPlayback();
}
catch (Exception ce)
{
hr = Marshal.GetHRForException(ce);
NotifyError(hr);
}
// If we succeeded, the Start call is pending. Don't notify the app yet.
}
protected void OnSessionStarted(IMFMediaEvent pEvent)
{
TRACE("CPlayer::OnSessionStarted");
m_state = PlayerState.Started;
NotifyState();
}
protected void OnSessionPaused(IMFMediaEvent pEvent)
{
TRACE("CPlayer::OnSessionPaused");
m_state = PlayerState.Paused;
NotifyState();
}
protected void OnSessionClosed(IMFMediaEvent pEvent)
{
TRACE("CPlayer::OnSessionClosed");
// The application thread is waiting on this event, inside the
// CPlayer::CloseSession method.
m_hCloseEvent.Set();
}
protected void OnPresentationEnded(IMFMediaEvent pEvent)
{
TRACE("CPlayer::OnPresentationEnded");
// The session puts itself into the stopped state autmoatically.
m_state = PlayerState.Ready;
NotifyState();
}
#endregion
#region Member Variables
protected IMFMediaSession m_pSession;
protected IMFMediaSource m_pSource;
protected IMFVideoDisplayControl m_pVideoDisplay;
protected IMFMediaType m_pMediaType;
protected IntPtr m_hwndVideo; // Video window.
protected IntPtr m_hwndEvent; // App window to receive events.
protected PlayerState m_state; // Current state of the media session.
protected AutoResetEvent m_hCloseEvent; // Event to wait on while closing
#endregion
}