将g711数据包转换为波形文件时

问题描述

我正在使用Sharppcap库从SIP呼叫中获取数据包。到现在为止还挺好。但是,当我加入这些数据包(MemoryStream中的G.711 Alaw数据包)并与从https://www.codeproject.com/Articles/14237/Using-the-G711-standard获得的AlawDecoder dll进行转换并将其与解码后的数组字节写入Wav文件时,我可以收听记录,但是听起来很不稳定。我是音频编程的新手,所以我没有很多经验。下面是我正在编写的代码片段,以实现此目的:

private static void Device_OnPacketArrival(object sender,CaptureEventArgs e)
    {
        var time = e.Packet.Timeval.Date.AddHours(-3);
        var len = e.Packet.Data.Length;
        


        var packet = PacketDotNet.Packet.ParsePacket(e.Packet.LinkLayerType,e.Packet.Data);
        var device = sender as ICaptureDevice;
        var tcpPacket = packet.Extract<PacketDotNet.TcpPacket>();
        var udpPacket = packet.Extract<PacketDotNet.UdpPacket>();

        if (udpPacket != null)
        {
            var ipPacket = (PacketDotNet.IPPacket)udpPacket.ParentPacket;
            System.Net.IPAddress srcIp = ipPacket.sourceAddress;
            System.Net.IPAddress dstIp = ipPacket.DestinationAddress;
            int srcPort = udpPacket.sourcePort;
            int dstPort = udpPacket.DestinationPort;
            byte[] udpHeaderData = udpPacket.HeaderData;
            byte[] udpPayloadData = udpPacket.PayloadData;
            string decodedUdpPayloadData = Encoding.UTF8.GetString(udpPayloadData);

            if (decodedUdpPayloadData.Contains("m=audio"))
            {
                FindRTPAudioPort(device,decodedUdpPayloadData);
            }
            else if (device.Filter != "udp port 5060")
            {
                RtpPacketsToWave(device,udpPayloadData);
            }
            else
            {
                Console.WriteLine("{0}:{1}:{2},{3} Len={4} {5}:{6} -> {7}:{8} UDP Packet " +
                "\n {9} \n Hex DUMP: {10} \n",time.Hour,time.Minute,time.Second,time.Millisecond,len,srcIp,srcPort,dstIp,dstPort,decodedUdpPayloadData,BitConverter.ToString(udpPayloadData));
            }
        }
        else if (tcpPacket != null)
        {
            var ipPacket = (PacketDotNet.IPPacket)tcpPacket.ParentPacket;
            System.Net.IPAddress srcIp = ipPacket.sourceAddress;
            System.Net.IPAddress dstIp = ipPacket.DestinationAddress;
            int srcPort = tcpPacket.sourcePort;
            int dstPort = tcpPacket.DestinationPort;


            Console.WriteLine("{0}:{1}:{2},{3} Len={4} {5}:{6} -> {7}:{8}",dstPort);
        }
        else
        {
            Console.WriteLine("\n");
        }
    }

    private static void RtpPacketsToWave(ICaptureDevice dev,byte[] payloadData)
    {
        try
        {

            MemoryStreamSingleton memoryStreamSingleton = MemoryStreamSingleton.GetInstance();
            MemoryStream memStream;
            byte[] headlesspayloadData = new byte[160];
            if (payloadData.Length == 172)
            {
                //Skips first 12 bytes containing the packet header
                headlesspayloadData = payloadData.Skip(12).ToArray();
                memStream = new MemoryStream(headlesspayloadData);
                memStream.copyTo(memoryStreamSingleton);
            }
            Console.WriteLine("Payload length: {0}",headlesspayloadData.Length);
            Console.WriteLine(memoryStreamSingleton.Length);
            if(memoryStreamSingleton.Length > 600000)
            {
                WaveFileGenerator(memoryStreamSingleton.ToArray());
                dev.StopCapture();
            }
        }
        catch (Exception ex)
        {

            Console.WriteLine(ex.ToString());
        }
        
    }

    private static void WaveFileGenerator(byte[] buffer)
    {
        try
        {
          
            Console.WriteLine("Device closed,generating audio file..");
            WaveFormat waveFormat = new WaveFormat(8000,16,1);

            short[] pcm16bit = ALawDecoder.ALawDecode(buffer);
            byte[] result1 = new byte[pcm16bit.Length * sizeof(short)];
            Buffer.Blockcopy(pcm16bit,result1,result1.Length);
            

            var outputWave = new WaveFileWriter(@"tmp/test.wav",waveFormat);
            outputWave.Write(result1,result1.Length);
            outputWave.Close();
            var waveFileProvider = new WaveFileReader(@"tmp/test.wav");
            MonoToStereoProvider16 toStereo = new MonoToStereoProvider16(waveFileProvider);
            WaveFileWriter.CreateWaveFile("test.wav",toStereo);
            waveFileProvider.dispose();
            File.Delete(@"tmp/test.wav");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            File.WriteallText("log.txt",ex.ToString());
        }
    }

我不明白我在想什么...

解决方法

由于接收功能花费的时间过多,可能导致数据包溢出,从而导致数据包溢出内部缓冲区。

在接收大量数据包的情况下,接收例程中的任何额外处理(包括打印)都可能导致数据包丢失。

为帮助减少在该例程中花费的时间并最大程度地提高数据包速率,您可以将数据包排队以便在后台线程中进行处理。

SharpPcap示例中有一个如何通过后台线程对数据包进行排队和处理的示例,您可以在https://github.com/chmorgan/sharppcap/blob/master/Examples/QueuingPacketsForBackgroundProcessing/Program.cs

中找到该示例。

使用队列方法时,接收例程会非常迅速地退出,从而可以以很高的速率处理数据包而不会丢失。

此外,您可以添加队列大小检查以确认后台线程是否与传入数据包速率保持同步。

一种降低传入数据包速率的方法是使用网络数据包筛选器(在OS或驱动程序层中运行),并且仅包括潜在的SIP数据包。我对SIP并不熟悉,但是如果您有更多信息,我可以帮助您。您的代码似乎正在使用过滤器,但不清楚在找到SIP流的情况下会发生什么,在这种情况下是否要过滤以仅包括该端口?

让我知道这是否有助于减少或消除声音不稳。