将通过 HTTP 流式传输的 wav 实时转换为 mp3

Posted

技术标签:

【中文标题】将通过 HTTP 流式传输的 wav 实时转换为 mp3【英文标题】:Convert wav streamed over HTTP to mp3, in real-time 【发布时间】:2014-06-13 16:53:22 【问题描述】:

背景:我正在使用一项服务,该服务返回 MIME 类型为 audio/wav 的数据。我需要为此音频提供播放机制(当前构建为 MVC 应用程序)。例如,我的端点类似于https://audio.fooservice.com/GetAudio?audioId=123

音频为 8kHz,1 声道 u-law。

由于在使用 html5 <audio> 标签时跨浏览器的格式支持不同,我无法使用原始的 u-law wav,因为 Internet Explorer 不会播放它。

我建议的解决方案是将源格式实时转换为 mp3。

我已经从这里和 NAudio 论坛中的各种其他问题中拼凑出一个部分可行的解决方案,但它会引发异常,如下面的 cmets 所述:

private void NAudioTest(string url)

    Stream outStream = new MemoryStream();
    var format = WaveFormat.CreateMuLawFormat(8000, 1);

    using (Stream ms = new MemoryStream())
    
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.KeepAlive = false;
        request.ProtocolVersion = HttpVersion.Version10;

        using (Stream stream = request.GetResponse().GetResponseStream())
        
            using (var reader = new RawSourceWaveStream(stream, format))
            
                // reader is not seekable; we need to convert to a byte array to seek
                var bytes = reader.ToByteArray();

                // create a new stream from the byte aray
                var seekableStream = new MemoryStream(bytes);

                // instantiating a WaveFileReader as follows will throw an exception:
                // "System.FormatException: Not a WAVE file - no RIFF header"
                using (var waveReader = new WaveFileReader(seekableStream))
                
                    using (var pcmStream = WaveFormatConversionStream.CreatePcmStream(waveReader))
                    
                        var pcmBytes = pcmStream.ToByteArray();
                        var mp3 = pcmBytes.ToMp3();
                    
                
            
        
    


public static class StreamExtensions

    public static byte[] ToByteArray(this Stream stream)
    
        var ms = new MemoryStream();
        var buffer = new byte[1024];
        int bytes = 0;

        while ((bytes = stream.Read(buffer, 0, buffer.Length)) > 0)
            ms.Write(buffer, 0, bytes);

        return ms.ToArray();
    


public static class ByteExtensions

    public static byte[] ToMp3(this byte[] bytes)
    
        using (var outStream = new MemoryStream())
        
            using (var ms = new MemoryStream(bytes))
            
                using (var reader = new WaveFileReader(ms))
                
                    using (var writer = new LameMP3FileWriter(outStream, reader.WaveFormat, 64))
                    
                        reader.CopyTo(writer);
                        return outStream.ToArray();
                    
                
            
        
    

我一天中的大部分时间都在研究这个问题,我觉得我在似乎应该相当简单的东西中引入了不必要的复杂性。

任何帮助将不胜感激。

注意:我无法更改源格式,支持 IE 是必需的。

编辑:我解决了 RIFF 异常并能够生成 MP3 流,但它只是白噪声。希望我也能解决这个问题。我的新代码如下:

[HttpGet]
public ActionResult GetMp3(string url)

    if (String.IsNullOrWhiteSpace(url))
        return null;

    var muLawFormat = WaveFormat.CreateMuLawFormat(8000, 1);
    var compressedStream = new MemoryStream();

    using (var ms = new MemoryStream())
    
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.KeepAlive = false;
        request.ProtocolVersion = HttpVersion.Version10;

        using (Stream webStream = request.GetResponse().GetResponseStream())
        
            var buffer = new byte[4096];
            int read;
            while (webStream != null && (read = webStream.Read(buffer, 0, buffer.Length)) > 0)
                ms.Write(buffer, 0, read);
        

        ms.Position = 0;

        using (WaveStream wav = WaveFormatConversionStream.CreatePcmStream(new RawSourceWaveStream(ms, muLawFormat)))
        using (var mp3 = new LameMP3FileWriter(compressedStream, new WaveFormat(), LAMEPreset.MEDIUM_FAST))
            wav.CopyTo(mp3);
    

    compressedStream.Seek(0, 0);
    return new FileStreamResult(compressedStream, "audio/mpeg");

【问题讨论】:

您必须将源的实际波形格式提供给LameMP3FileWriter 构造函数。而不是new WaveFormat() 使用wav.WaveFormat 否则它将读取错误的数据。 谢谢科里。我错误地认为CreatePcmStream 方法将返回与默认WaveFormat 构造函数格式相同的流。事实证明,我的源流的文档(和 MIME 类型)不正确,我花了一周的时间来摆弄它。 【参考方案1】:

这对我有用(我需要做你想做的事)。希望这对其他人也有帮助。我将 NAudio 与 LAME 一起使用。

您必须确保将 libmp3lamexx.dll 文件复制到您的网络服务器的 BIN 位置或 %PATH% 变量中的某个文件夹,否则它将不起作用。

        string sq = /* URL of WAV file (http://foo.com/blah.wav) */

        Response.ContentType = "audio/mpeg";

        using (WebClient wc = new WebClient())
        
            if (!sq.ToLower().EndsWith(".wav"))
            
                byte[] rawFile = wc.DownloadData(sq.Trim());
                Response.OutputStream.Write(rawFile, 0, rawFile.Length);
            
            else
            
                using (var wavReader = new WaveFileReader(new MemoryStream(wc.DownloadData(sq.Trim()))))
                
                    try
                    
                        using (var wavWriter = new LameMP3FileWriter(Response.OutputStream, wavReader.WaveFormat, LAMEPreset.ABR_128))
                        
                            wavReader.CopyTo(wavWriter);
                        
                    
                    catch (ArgumentException)
                    
                        var newFormat = new WaveFormat(wavReader.WaveFormat.SampleRate, 16, 2);

                        using (var pcmStream = new WaveFormatConversionStream(newFormat, wavReader))
                        
                            using (var wavWriter = new LameMP3FileWriter(Response.OutputStream, pcmStream.WaveFormat, LAMEPreset.ABR_128))
                            
                                pcmStream.CopyTo(wavWriter);
                            
                        
                    
                
            

            Response.Flush();
            Response.End();
        

【讨论】:

以上是关于将通过 HTTP 流式传输的 wav 实时转换为 mp3的主要内容,如果未能解决你的问题,请参考以下文章

通过 CloudFront 进行私有 HTTP 实时流式传输

如何实时流式传输音频文件

将实时 http 流式传输到 HTML5 视频客户端的最佳方法 [关闭]

使用 .ts 流 url 在网络上实时流式传输

具有不同长度的音频文件的 HTTP 实时流式传输

通过 socket.io 流式传输实时音频