如何使用DirectShow.NET C#访问音频流

4
我希望能够使用.NET 3.5 C#和DirectShow.NET将任意音频文件传递给DirectShow过滤器图,并最终接收(PMC音频)流对象。我希望能够达到这样一个点,只需说:
 Stream OpenFile(string filename) {...}

并且

stream.Read(...)

我已经阅读了几天的DirectShow,认为我已经开始理解过滤器和过滤器图的概念。我找到了一些例子(文件 / 设备)如何播放音频或将其写入文件,但似乎找不到流对象的解决方案。这可能吗?如果我错过了什么,请问你能否指点我方向?
最好的祝福,
Hauke

你需要哪种类型的文件(mp3、wav、wma)? - Shay Erlichmen
它应该可以处理 DirectShow 能够处理的任何内容;mp3、mp2、wma。DirectShow 可以处理所有这些,我只是没有找到接口来获取流。 - Hauke
3个回答

5

我想与您分享我解决自己问题的方法(我的重点是异国情调的bwf文件格式。因此得名):

    using System;
    using System.Collections.Generic;
    using System.Text;
    using DirectShowLib;
    using System.Runtime.InteropServices;
    using System.IO;

    namespace ConvertBWF2WAV
    {
        public class BWF2WavConverter : ISampleGrabberCB
        {
            IFilterGraph2 gb = null;
            ICaptureGraphBuilder2 icgb = null;
            IBaseFilter ibfSrcFile = null;
            DsROTEntry m_rot = null;
            IMediaControl m_mediaCtrl = null;
            ISampleGrabber sg = null;


            public BWF2WavConverter() 
            { 
                // Initialize
                int hr;
                icgb = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                gb = (IFilterGraph2) new FilterGraph();
                sg = (ISampleGrabber)new SampleGrabber();

    #if DEBUG
                m_rot = new DsROTEntry(gb);
    #endif
                hr = icgb.SetFiltergraph(gb);
                DsError.ThrowExceptionForHR(hr);
            }

            public void reset()
            {
                gb = null;
                icgb = null;
                ibfSrcFile = null;
                m_rot = null;
                m_mediaCtrl = null;
            }

            public void convert(object obj) 
            {
                string[] pair = obj as string[];
                string srcfile = pair[0];
                string targetfile = pair[1];

                int hr;

                ibfSrcFile = (IBaseFilter)new AsyncReader();
                hr = gb.AddFilter(ibfSrcFile, "Reader");
                DsError.ThrowExceptionForHR(hr);

                IFileSourceFilter ifileSource = (IFileSourceFilter)ibfSrcFile;
                hr = ifileSource.Load(srcfile, null);
                DsError.ThrowExceptionForHR(hr);

                // the guid is the one from ffdshow
                Type fftype = Type.GetTypeFromCLSID(new Guid("0F40E1E5-4F79-4988-B1A9-CC98794E6B55"));
                object ffdshow = Activator.CreateInstance(fftype);
                hr = gb.AddFilter((IBaseFilter)ffdshow, "ffdshow");
                DsError.ThrowExceptionForHR(hr);

                // the guid is the one from the WAV Dest sample in the SDK
                Type type = Type.GetTypeFromCLSID(new Guid("3C78B8E2-6C4D-11d1-ADE2-0000F8754B99"));
                object wavedest = Activator.CreateInstance(type);
                hr = gb.AddFilter((IBaseFilter)wavedest, "WAV Dest");
                DsError.ThrowExceptionForHR(hr);

                // manually tell the graph builder to try to hook up the pin that is left
                IPin pWaveDestOut = null;
                hr = icgb.FindPin(wavedest, PinDirection.Output, null, null, true, 0, out pWaveDestOut);
                DsError.ThrowExceptionForHR(hr);

                // render step 1
                hr = icgb.RenderStream(null, null, ibfSrcFile, (IBaseFilter)ffdshow, (IBaseFilter)wavedest);
                DsError.ThrowExceptionForHR(hr);

                 // Configure the sample grabber
                IBaseFilter baseGrabFlt = sg as IBaseFilter;
                ConfigSampleGrabber(sg);
                IPin pGrabberIn = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
                IPin pGrabberOut = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
                hr = gb.AddFilter((IBaseFilter)sg, "SampleGrabber");
                DsError.ThrowExceptionForHR(hr);
                AMMediaType mediatype = new AMMediaType();
                sg.GetConnectedMediaType(mediatype);

                hr = gb.Connect(pWaveDestOut, pGrabberIn);
                DsError.ThrowExceptionForHR(hr);

                // file writer
                FileWriter file_writer = new FileWriter();
                IFileSinkFilter fs = (IFileSinkFilter)file_writer;
                fs.SetFileName(targetfile, null);
                hr = gb.AddFilter((DirectShowLib.IBaseFilter)file_writer, "File Writer");
                DsError.ThrowExceptionForHR(hr);

                // render step 2
                AMMediaType mediatype2 = new AMMediaType();
                pWaveDestOut.ConnectionMediaType(mediatype2);
                gb.Render(pGrabberOut);

                // alternatively to the file writer use the NullRenderer() to just discard the rest

                // assign control
                m_mediaCtrl = gb as IMediaControl;

                // run
                hr = m_mediaCtrl.Run();
                DsError.ThrowExceptionForHR(hr);


            }


            //
            // configure the SampleGrabber filter of the graph
            //
            void ConfigSampleGrabber(ISampleGrabber sampGrabber)
            {
                AMMediaType media;

                // set the media type. works with "stream" somehow...
                media = new AMMediaType();
                media.majorType = MediaType.Stream;
                //media.subType = MediaSubType.WAVE;
                //media.formatType = FormatType.WaveEx;

                // that's the call to the ISampleGrabber interface
                sg.SetMediaType(media);

                DsUtils.FreeAMMediaType(media);
                media = null;

                // set BufferCB as the desired Callback function
                sg.SetCallback(this, 1);
            }

            public int SampleCB(double a, IMediaSample b)
            {
                return 0;
            }

            /// <summary>
            /// Called on each SampleGrabber hit.
            /// </summary>
            /// <param name="SampleTime">Starting time of the sample, in seconds.</param>
            /// <param name="pBuffer">Pointer to a buffer that contains the sample data.</param>
            /// <param name="BufferLen">Length of the buffer pointed to by pBuffer, in bytes.</param>
            /// <returns></returns>
            public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
            {
                byte[] buffer = new byte[BufferLen];
                Marshal.Copy(pBuffer, buffer, 0, BufferLen);
                using (BinaryWriter binWriter = new BinaryWriter(File.Open(@"C:\directshowoutput.pcm", FileMode.Append)))
                {
                    binWriter.Write(buffer);
                }
                return 0;
            }
        }
    }

1

这个(AVILibrary Wrapper)可能会带领你找到解决方案,它不是基于DirectSound的(我感觉这很偏向于将你的代码与播放硬件进行接口),但可能是答案。

另一种方法可以在这里找到。


1

我发现NAudio对文件类型太过限制。 - Hauke
NAudio目前可以访问任何已安装的ACM编解码器以及DirectX媒体对象(DMOs)。我希望将DirectShow过滤器添加到其中。 - Mark Heath

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接