DirectShow自定义源引脚

时间:2013-10-29 18:20:27

标签: wpf sockets directshow.net streaming-video

我是DirectShow的新手。我和许多其他人一样,正在尝试为基于WPF的纸牌游戏创建基于套接字的P2P流媒体解决方案。我希望每个玩家能够通过小视频窗口看到对方。

我的问题是双重的。第一个是如何降低帧采样率和分辨率?我相信320x200 x 15到20 fps应该没问题。我正在使用SampleGrabber回调来获取帧数据并通过套接字发送它;这实际上是在640x480分辨率下无压缩工作。

我的第二个问题是,因为每个帧包含921,600个字节,所以这真的很糟糕,而且我在本地WiFi连接的局域网上渲染速度非常慢。我添加了一个简单的MJPEG压缩(想要稍后切换到h.264),我注意到字节数下降到330-360k左右。不是一个糟糕的改进。

在接收端我是否需要创建自定义DirectShow源引脚以提供从套接字接收的字节,以便我可以附加解码器并在窗口中呈现字节?

我只想先问一下,因为创建一个新的COM对象似乎需要做很多工作(大约15年内没有这样做!),注册它,并使用/调试它。

还有其他办法吗?

此外,如果这是要走的路,我应该在接收端使用SampleGrabber并从解压缩的字节创建BitmapSource,还是应该允许DirectShow创建子窗口?事实是,我想拥有多个其他玩家,我在套接字中设置了一个额外的字节,以告诉他们在哪个表位置。如何依次呈现每个位置?

1 个答案:

答案 0 :(得分:0)

对于那些感兴趣的人,以下是设置分辨率并添加编码器/压缩器的方法:

        // Create a graph builder
        int hr = captureGraphBuilder.SetFiltergraph(graphBuilder);

        // Find a capture device (WebCam) and attach it to the graph
        sourceFilter = FindCaptureDevice();
        hr = graphBuilder.AddFilter(sourceFilter, "Video Capture");

        // Get the source output Pin
        IPin sourcePin = DsFindPin.ByDirection((IBaseFilter)sourceFilter, PinDirection.Output, 0);
        IAMStreamConfig sc = (IAMStreamConfig)sourcePin;
        int count;
        int size;
        sc.GetNumberOfCapabilities(out count, out size);
        VideoInfoHeader v;
        AMMediaType media2 = null;
        IntPtr memPtr = Marshal.AllocCoTaskMem(size);
        for (int i = 0; i < count; ++i)
        {
            sc.GetStreamCaps(i, out media2, memPtr);

            v = (VideoInfoHeader)Marshal.PtrToStructure(media2.formatPtr, typeof(VideoInfoHeader));
            // Break when width is 160
            if (v.BmiHeader.Width == 160)
                break;
        }

        // Set the new media format t0 160 x 120
        hr = sc.SetFormat(media2);
        Marshal.FreeCoTaskMem(memPtr);
        DsUtils.FreeAMMediaType(media2);

        // Create a FramGrabber
        IBaseFilter grabberF = (IBaseFilter)new SampleGrabber();
        ISampleGrabber grabber = (ISampleGrabber)grabberF;

        // Set the media type
        var media = new AMMediaType
        {
            majorType = MediaType.Video,
            subType = MediaSubType.MJPG
            //subType = MediaSubType.RGB24
        };

        // The media sub type will be MJPG
        hr = grabber.SetMediaType(media);
        DsUtils.FreeAMMediaType(media);
        hr = grabber.SetCallback(this, 1);
        hr = graphBuilder.AddFilter(grabberF, "Sample Grabber");
        IPin grabberPin = DsFindPin.ByDirection(grabberF, PinDirection.Input, 0);

        // Get the MPEG compressor
        Guid iid = typeof(IBaseFilter).GUID;
        object compressor = null;
        foreach (DsDevice device in DsDevice.GetDevicesOfCat(FilterCategory.VideoCompressorCategory))//.MediaEncoderCategory))
        {
            if (device.Name == "MJPEG Compressor")
            {
                device.Mon.BindToObject(null, null, ref iid, out compressor);
                hr = graphBuilder.AddFilter((IBaseFilter)compressor, "Compressor");
                break;
            }
            string name = device.Name;
        }

        // This also works!
        //IBaseFilter enc = (IBaseFilter)new MJPGEnc();
        //graphBuilder.AddFilter(enc, "MJPEG Encoder");

        // Get the input and out pins of the compressor
        IBaseFilter enc = (IBaseFilter)compressor;
        IPin encPinIn = DsFindPin.ByDirection(enc, PinDirection.Input, 0);
        IPin encPinOut = DsFindPin.ByDirection(enc, PinDirection.Output, 0);

        // Attach the pins: source to input, output to grabber
        hr = graphBuilder.Connect(sourcePin, encPinIn);
        hr = graphBuilder.Connect(encPinOut, grabberPin);

        // Free the pin resources
        Marshal.ReleaseComObject(sourcePin);
        Marshal.ReleaseComObject(enc);
        Marshal.ReleaseComObject(encPinIn);
        Marshal.ReleaseComObject(encPinOut);
        Marshal.ReleaseComObject(grabberPin);

        // Create a render stream
        hr = captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, null, grabberF);
        Marshal.ReleaseComObject(sourceFilter);
        Configure(grabber);