如何在Microsoft Media Foundation中修复“拓扑中的源流节点没有源”错误?

时间:2019-04-17 21:40:35

标签: c# com-interop ms-media-foundation

我正在尝试在C#包装器中设置MMF的拓扑对象,以开始从捕获设备作为源进行捕获。我有一个已激活的现有IMFMediaSource对象,因此我创建了拓扑节点,将该节点添加到拓扑中,然后尝试设置拓扑。在IMFMediaSession::SetTopology通话期间,它引发以下异常:

  

System.Runtime.InteropServices.COMException:拓扑中的源流节点没有源。 (来自HRESULT的异常:0xC00D521A)

以前,我使用SourceReader从媒体源中提取样本,因此我知道媒体源可以工作。我在几个不同的地方读过,如果我实际上想在视频设备上在屏幕上显示某些内容,则不能使用SourceReader和SinkWriter,这就是为什么我要创建使用的拓扑。我还可以创建一个演示描述符,并从媒体源创建的演示描述符中提取流描述符。在此过程中,我还验证了该子类型是我实际上可以使用的东西。

我还有一个媒体接收器输出节点作为拓扑的一部分。这似乎没有任何问题或引起异常。我已经删除了源流节点并离开了媒体接收器,并且它没有崩溃。

我尝试搜索有关此异常的更多信息,但是我发现的唯一搜索结果是有关HResult的信息,而我已经有了该信息。

这是我创建拓扑节点的方式(源,表示描述符和流描述符不为空):

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

运行MFTrace,我得到以下信息:

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

我的想法是,这应该创建适当的拓扑节点,但我非常。如果这是我的COM包装程序存在的问题,尽管我不反对将其排除为问题,但我不希望出现这样的特定异常。除了设置拓扑外,它只会引发此异常。

更新

这是我用来创建拓扑的完整代码:

        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;
        }

这是激活设备的方式:

        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;
        }

实际上使用激活器:

        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;

我想我在这里犯了一个简单的错误,弄糟了这个问题。

1 个答案:

答案 0 :(得分:0)

罗马的上述回答似乎已经解决了我的问题。

  

我想您的真实源代码有些不同,总体上说得通,但也许某个地方有错字。

简而言之,当我为不同的节点导入不同的GUID时,我错误地复制并粘贴了其中一个节点的GUID之一。这意味着,当Windows SDK尝试对其进行处理时,会感到困惑,因为.NET包装器中的GUID不好。具体来说,“呈现描述符节点”已设置为与“源节点”相同的GUID。