【问题标题】:wma audio stream to mp3 stream using NAudio c#使用 NAudio c# 将 wma 音频流转换为 mp3 流
【发布时间】:2013-10-25 15:10:54
【问题描述】:

我的任务是使用 NAudio 和 Lame 将 wma 音频流转换为 mp3 流。下面的代码适用于文件名,但我希望它用内存流完成。我在 NAudio 中搜索没有读取 wma 音频流的方法。 NAudio 可以吗?

    public static byte[] ConvertWmaToMp3(uint bitrate = 128)
    {

        FileStream fs = new FileStream("..\\sample.wma", FileMode.Open, FileAccess.Read);            
        var ws = new NAudio.WindowsMediaFormat.WMAFileReader(fs.Name);                                    

        // Setup encoder configuration
        WaveLib.WaveFormat fmt = new WaveLib.WaveFormat(ws.WaveFormat.SampleRate, 16, ws.WaveFormat.Channels);
        Yeti.Lame.BE_CONFIG beconf = new Yeti.Lame.BE_CONFIG(fmt, bitrate);

        // Encode WAV to MP3
        int blen = ws.WaveFormat.AverageBytesPerSecond;
        byte[] buffer = new byte[blen];
        byte[] mp3data = null;

        using (MemoryStream mp3strm = new MemoryStream())
        using (Mp3Writer mp3wri = new Mp3Writer(mp3strm, fmt, beconf))
        {
            int rc;
            while ((rc = ws.Read(buffer, 0, blen)) > 0)
            {
                mp3wri.Write(buffer, 0, rc);
            }

            mp3data = mp3strm.ToArray();
        }

        return mp3data;                      

    }

【问题讨论】:

    标签: c#-4.0 naudio lame wma


    【解决方案1】:

    目前WMAFileReader 类不支持从流中读取数据。 WMA API 支持从IStream 读取 WMA,所以这绝对是可能的。

    如果您想自己实现流式传输,则需要从 CodePlex 获取 WmaFileReaderWmaStream 的源代码,并将它们用作修改后的类的模板。

    首先你需要一个包装类,它为 .NET Stream 提供 COM IStream 接口。这是一个简单的:

    public class InteropStream : IStream, IDisposable
    {
        public readonly Stream intern;
    
        public InteropStream(Stream strm)
        {
            intern = strm;
        }
    
        ~InteropStream()
        {
            Dispose(true);
        }
    
        public void Dispose()
        {
            Dispose(false);
        }
    
        protected void Dispose(bool final)
        {
            if (final)
                intern.Dispose();
        }
    
        #region IStream Members
        public void Clone(out IStream ppstm)
        {
            ppstm = null;
        }
    
        public void Commit(int grfCommitFlags)
        {
            intern.Flush();
        }
    
        readonly byte[] buffer = new byte[4096];
    
        public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
        {
            if (pcbRead != IntPtr.Zero)
                Marshal.WriteInt32(pcbRead, 0);
            if (pcbWritten != IntPtr.Zero)
                Marshal.WriteInt32(pcbWritten, 0);
        }
    
        public void LockRegion(long libOffset, long cb, int dwLockType)
        { }
    
        public void Read(byte[] pv, int cb, IntPtr pcbRead)
        {
            int rc = intern.Read(pv, 0, cb);
            if (pcbRead != IntPtr.Zero)
                Marshal.WriteInt32(pcbRead, rc);
        }
    
        public void Revert()
        { }
    
        public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
        {
            long origin = 0;
            if (dwOrigin == 1) // STREAM_SEEK_CUR
                origin = intern.Position;
            else if (dwOrigin == 2) // STREAM_SEEK_END
                origin = intern.Length;
    
            long pos = origin + dlibMove;
            intern.Position = pos;
    
            if (plibNewPosition != IntPtr.Zero)
                Marshal.WriteInt64(plibNewPosition, pos);
        }
    
        public void SetSize(long libNewSize)
        { }
    
        public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
        {
            var res = new System.Runtime.InteropServices.ComTypes.STATSTG();
    
            res.type = 2; // STGTY_STREAM
            res.cbSize = intern.Length;
    
            pstatstg = res;
        }
    
        public void UnlockRegion(long libOffset, long cb, int dwLockType)
        { }
    
        public void Write(byte[] pv, int cb, IntPtr pcbWritten)
        { }
        #endregion
    }
    

    接下来将WmaStream 代码复制到项目中的新命名空间,并将以下代码添加到类的顶部:

        InteropStream interopStrm = null;
    
        public WmaStream(Stream fileStream)
            : this(fileStream, null)
        { }
    
        public WmaStream(Stream fileStream, WaveFormat OutputFormat)
        {
            interopStrm = new InteropStream(fileStream);
            m_reader = WM.CreateSyncReader(WMT_RIGHTS.WMT_RIGHT_NO_DRM);
            try
            {
                IWMSyncReader2 rdr = m_reader as IWMSyncReader2;
                rdr.OpenStream(interopStrm);
                Init(OutputFormat);
            }
            catch
            {
                try
                {
                    m_reader.Close();
                }
                catch
                {
                }
                m_reader = null;
                throw;
            }
        }
    

    WmaFileReader 和以下代码执行相同操作:

        public WMAFileReader(Stream wmaStream)
        {
            m_wmaStream = new WmaStream2(wmaStream);
            m_waveFormat = m_wmaStream.Format;
        }
    

    现在您可以使用文件名或Stream 实例(MemoryStreamFileStream 等)创建修改后的 WmaFileReader 的实例。Stream 实例需要可读且可查找。

    我已经在我的计算机上找到的几个随机 WMA 文件上尝试了上述方法,加载到 MemoryStream 或使用 FileStream,它按我的预期工作。

    据推测,Mark 正在努力将这个功能添加到 NAudio.Wma 包中,所以在 NAudio 支持之前,请考虑这是一个临时修复。

    【讨论】:

      【解决方案2】:

      使用 NAudio 将 WMA 转换为 MP3 的最简单方法是使用基于 Media Foundation 的类。如果您使用的是 Windows 8 及更高版本,则应该可以使用 MP3 编码器。

      using (var reader = new MediaFoundationReader("test.wma"))
      {
          MediaFoundationEncoder.EncodeToMp3(reader, "test.mp3");
      }
      

      在没有 MP3 编码器的系统上,我倾向于使用 LAME.exe 并通过标准输入流式传输输入音频。有关此方法的更多信息,请参阅my article here

      【讨论】:

        猜你喜欢
        • 2012-07-30
        • 2021-03-06
        • 2015-09-22
        • 2013-01-22
        • 2013-10-10
        • 2012-11-02
        • 2013-11-12
        • 1970-01-01
        • 2021-08-15
        相关资源
        最近更新 更多