How do I fix 'A source stream node in the topology does not have a source' error in Microsoft Media Foundation?

1

I'm attempting to setup a topology object in my C# wrapper for MMF to start capturing from a capture device as a source. I have an existing and activated IMFMediaSource object, so I create the topology node, add the node to the topology, then I attempt to set the topology. During the IMFMediaSession::SetTopologycall it throws the following exception:

System.Runtime.InteropServices.COMException: A source stream node in the topology does not have a source. (Exception from HRESULT: 0xC00D521A)

Previously, I was using a SourceReader to pull samples off of the media source, so I know that the media source works. I've read in a few different places that if I actually want to display something on screen from the a video device, I can't use SourceReader and SinkWriter, which is why I'm trying to create a topology to use. I can also create a presentation descriptor and pull stream descriptors from the presentation descriptor created from the media source. During that process, I also validate that the subtype is something that I actually can use.

I've also got a media sink output node as part of the topology. That doesn't seem to have any issues or cause exceptions. I've removed the source stream node and left the media sink and it didn't crash.

I've tried searching for further information on this exception, but the only search results that I find are for what the HResult is, which I already have that information.

Here's how I create the topology node (the source, presentation descriptor, and stream discriptor aren't null):

if (sourcePresentationDescriptor == null || videoStreamDescriptor == null || !isSelectedStream)
{
    return false;
}

status = NativeMethods.MFCreateTopologyNode(MFTopologyType.TopologySourcestreamNode, out videoSourceNode);
if (status < 0) { throw new Exception("Not able to create topology node for source."); }

// Finish instantiating source node
videoSourceNode.SetUnknown(CLSIDsMFMediaTypeAttributes.TopoNodeSource, mediaSource);
videoSourceNode.SetUnknown(CLSIDsMFMediaTypeAttributes.TopoNodePresentationDescriptor, sourcePresentationDescriptor);
videoSourceNode.SetUnknown(CLSIDsMFMediaTypeAttributes.TopoNodeStreamDescriptor, videoStreamDescriptor);
topology.AddNode(videoSourceNode);

MediaSession.SetTopology(0, topology); // <-- Exception thrown here

Running MFTrace, I get the following:

CTopologyHelpers::Trace @05114468 >>>>>>>>>>>>> input topology
CTopologyHelpers::TraceNode @ Node 0 @050EDFD8 ID:7FC400000001, 0 inputs, 0 outputs, type 1, MF_TOPONODE_SOURCE=@050E3E68;MF_TOPONODE_STREAM_DESCRIPTOR=@050FE9B0
CMFTopologyNodeDetours::GetGUID @050EDFD8 attribute not found guidKey = MF_TOPONODE_TRANSFORM_OBJECTID
CTopologyHelpers::TraceObject @ UnknownType @00000000 {00000000-0000-0000-0000-000000000000} ((null)), (null)
CMFTopologyDetours::GetUINT32 @05114468 attribute not found guidKey = MF_TOPOLOGY_RESOLUTION_STATUS
CTopologyHelpers::Trace @05114468 MF_TOPOLOGY_RESOLUTION_STATUS = NOT FOUND!!!
CTopologyHelpers::Trace @05114468 <<<<<<<<<<<<< input topology

My thought is that this should create the proper topology node, but I'm very new to this. If this is a problem with my COM wrapper, I wouldn't expect such a specific exception, though I'm not opposed to ruling that out as the problem. Instead of setting the topology it just throws this exception.

UPDATE

Here's the full code that I'm using for creating the topology:

        private bool CreateMediaSessionAndTopology(IMFMediaSource mediaSource, IntPtr videoHWnd, string uniqueDeviceName)
        {
            IMFTopology topology;
            IMFTopologyNode videoSourceNode;
            IMFTopologyNode outputVideoSinkNode;
            //IMFTopologyNode transformNode;
            IMFPresentationDescriptor sourcePresentationDescriptor = null;
            IMFStreamDescriptor videoStreamDescriptor = null;
            IMFMediaTypeHandler mediaTypeHandler;
            IMFMediaType captureMediaType;
            IMFActivate rendererActivator;

            uint sourceStreamCount;
            bool isSelectedStream = false;
            Guid majorMediaType;
            Guid[] subTypes =
            {
                MFMediaTypeGuids.NV12,
                MFMediaTypeGuids.YUY2,
                MFMediaTypeGuids.UYVY,
                MFMediaTypeGuids.RGB32,
                MFMediaTypeGuids.RGB24,
                MFMediaTypeGuids.IYUV
            };

            try
            {
                // Create the topology
                uint status = NativeMethods.MFCreateTopology(out topology);
                if (status < 0) { throw new Exception("Couldn't create topology"); }

                sourcePresentationDescriptor = mediaSource.CreatePresentationDescriptor();
                sourceStreamCount = sourcePresentationDescriptor.GetStreamDescriptorCount();

                for (uint i = 0; i < sourceStreamCount; i++)
                {
                    videoStreamDescriptor = sourcePresentationDescriptor.GetStreamDescriptorByIndex(i, out isSelectedStream);
                    if (videoStreamDescriptor != null)
                    {
                        mediaTypeHandler = videoStreamDescriptor.GetMediaTypeHandler();

                        majorMediaType = mediaTypeHandler.GetMajorType();

                        if (majorMediaType != MFMediaTypeGuids.Video) { continue; }

                        if (!isSelectedStream) { continue; }

                        uint MaxSubTypes = 250; //magic number for now;

                        for (uint j = 0; j < MaxSubTypes; j++)
                        {
                            captureMediaType = mediaTypeHandler.GetMediaTypeByIndex(j);
                            if (captureMediaType == null) { continue; }


                            Guid subtype = captureMediaType.GetGuid(CLSIDsMFMediaTypeAttributes.SubType);
                            for (int k = 0; k < subTypes.Length; k++)
                            {
                                if (subtype == subTypes[k])
                                {
                                    mediaTypeHandler.SetCurrentMediaType(captureMediaType);
                                    break;
                                }
                            }
                            break;
                        }

                        break;
                    }
                }

                if (sourcePresentationDescriptor == null || videoStreamDescriptor == null || !isSelectedStream)
                {
                    return false;
                }

                status = NativeMethods.MFCreateTopologyNode(MFTopologyType.TopologySourcestreamNode, out videoSourceNode);
                if (status < 0) { throw new Exception("Not able to create topology node for source."); }

                // Finish instantiating source node
                videoSourceNode.SetUnknown(CLSIDsMFMediaTypeAttributes.TopoNodeSource, mediaSource);
                videoSourceNode.SetUnknown(CLSIDsMFMediaTypeAttributes.TopoNodePresentationDescriptor, sourcePresentationDescriptor);
                videoSourceNode.SetUnknown(CLSIDsMFMediaTypeAttributes.TopoNodeStreamDescriptor, videoStreamDescriptor);
                topology.AddNode(videoSourceNode);

                // Create Video Sink node
                status = NativeMethods.MFCreateTopologyNode(MFTopologyType.TopologyOutputNode, out outputVideoSinkNode);
                if (status < 0) { throw new Exception("Couldn't create output node."); }

                status = NativeMethods.MFCreateVideoRendererActivate(videoHWnd, out rendererActivator);
                if (status < 0) { throw new Exception("Couldn't create renderer activator"); }

                outputVideoSinkNode.SetObject(rendererActivator);

                topology.AddNode(outputVideoSinkNode);

                videoSourceNode.ConnectOutput(0, outputVideoSinkNode, 0);

                // Create the session
                status = NativeMethods.MFCreateMediaSession(null, out _mediaSession);
                if (status < 0) { throw new Exception("Not able to create media session."); }

                _mediaSessionCallbackHandler = new DeviceCaptureCallbackHandler();
                _mediaSessionCallbackHandler.MediaSession = _mediaSession;
                _mediaSessionCallbackHandler.MediaSessionAsyncCallbackEvent = HandleMediaSessionCallbackEvent;
                _mediaSessionCallbackHandler.MediaSessionAsyncCallbackError = HandleMediaSessionCallbackErrors;

                _mediaSession.BeginGetEvent(_mediaSessionCallbackHandler, null);

                _mediaSession.SetTopology(0, topology);
            }
            catch (Exception ex)
            {
                throw new Exception("Failed to create topology", ex);
            }
            return true;
        }

Here's how the device is activated:

        public Device(IMFActivate activator)
        {
            string friendlyName;
            string symbolicLinkName;

            _activator = activator;

            activator.GetAllocatedString(MfAttributeSourceTypeGuids.MfDevsourceAttributeFriendlyName, out friendlyName);
            FriendlyName = friendlyName;

            activator.GetAllocatedString(MfAttributeSourceTypeGuids.MfDevsourceAttributeSourceTypeVidcapSymbolicLink, out symbolicLinkName);
            SymbolicLinkName = symbolicLinkName;
        }

Actually using the activator:

        public bool ActivateDevice()
        {
            object source;

            try
            {
                source = _activator.ActivateObject(typeof(IMFMediaSource).GUID);
                MediaSource = source as IMFMediaSource;
            }
            catch (System.Exception ex)
            {
                throw new System.Exception("Couldn't activate object", ex);
            }
            return true;

I'm thinking there's a simple mistake that I'm making in here that is messing this up.

c#
com-interop
ms-media-foundation
asked on Stack Overflow Apr 17, 2019 by geoffwild11 • edited Apr 25, 2019 by geoffwild11

1 Answer

0

Roman's response above appears to have been the solution to my problem.

I suppose your real source code is a bit different, making sense in general but maybe there is a typo somewhere.

In short, when I was importing in the different GUIDs for the different nodes, I had incorrectly copied and pasted one of the GUIDs for one of the nodes. This meant that when the Windows SDK was trying to do something with it, it would get confused because there was a bad GUID in my .NET wrapper. Specifically, the Presentation Descriptor Node was set to the same GUID as Source Node.

answered on Stack Overflow Jun 6, 2019 by geoffwild11

User contributions licensed under CC BY-SA 3.0