zoukankan      html  css  js  c++  java
  • C#基于UDP实现的P2P语音聊天工具(1)

    这篇文章主要是一个应用,使用udp传送语音和文本等信息。在这个系统中没有服务端和客户端,相互通讯都是直接相互联系的。能够很好的实现效果。

    语音获取

    要想发送语音信息,首先得获取语音,这里有几种方法,一种是使用DirectX的DirectXsound来录音,我为了简便使用一个开源的插件NAudio来实现语音录取。 在项目中引用NAudio.dll

    1. //------------------录音相关----------------------------- 
    2.         private IWaveIn waveIn; 
    3.         private WaveFileWriter writer; 
    4.  
    5.         private void LoadWasapiDevicesCombo() 
    6.         { 
    7.             var deviceEnum = new MMDeviceEnumerator(); 
    8.             var devices = deviceEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToList(); 
    9.             comboBox1.DataSource = devices; 
    10.             comboBox1.DisplayMember = "FriendlyName"; 
    11.         } 
    12.         private void CreateWaveInDevice() 
    13.         { 
    14.  
    15.             waveIn = new WaveIn(); 
    16.             waveIn.WaveFormat = new WaveFormat(8000, 1); 
    17.             waveIn.DataAvailable += OnDataAvailable; 
    18.             waveIn.RecordingStopped += OnRecordingStopped; 
    19.         } 
    20.         void OnDataAvailable(object sender, WaveInEventArgs e) 
    21.         { 
    22.             if (this.InvokeRequired) 
    23.             { 
    24.                 this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e); 
    25.             } 
    26.             else 
    27.             { 
    28.                 writer.Write(e.Buffer, 0, e.BytesRecorded); 
    29.                 int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond); 
    30.                 if (secondsRecorded >= 10)//最大10s 
    31.                 { 
    32.                     StopRecord(); 
    33.                 } 
    34.                 else 
    35.                 { 
    36.                     l_sound.Text = secondsRecorded + " s"; 
    37.                 } 
    38.             } 
    39.         } 
    40.         void OnRecordingStopped(object sender, StoppedEventArgs e) 
    41.         { 
    42.             if (InvokeRequired) 
    43.             { 
    44.                 BeginInvoke(new EventHandler<StoppedEventArgs>(OnRecordingStopped), sender, e); 
    45.             } 
    46.             else 
    47.             { 
    48.                 FinalizeWaveFile(); 
    49.             } 
    50.         } 
    51.         void StopRecord() 
    52.         { 
    53.             AllChangeBtn(btn_luyin, true); 
    54.             AllChangeBtn(btn_stop, false); 
    55.             AllChangeBtn(btn_sendsound, true); 
    56.             AllChangeBtn(btn_play, true); 
    57.  
    58.             //btn_luyin.Enabled = true; 
    59.             //btn_stop.Enabled = false; 
    60.             //btn_sendsound.Enabled = true; 
    61.             //btn_play.Enabled = true; 
    62.             if (waveIn != null) 
    63.                 waveIn.StopRecording(); 
    64.             //Cleanup(); 
    65.         } 
    66.   private void Cleanup() 
    67.         { 
    68.             if (waveIn != null) 
    69.             { 
    70.                 waveIn.Dispose(); 
    71.                 waveIn = null; 
    72.             } 
    73.             FinalizeWaveFile(); 
    74.         } 
    75.         private void FinalizeWaveFile() 
    76.         { 
    77.             if (writer != null) 
    78.             { 
    79.                 writer.Dispose(); 
    80.                 writer = null; 
    81.             } 
    82.         } 
    83.    //开始录音 
    84.         private void btn_luyin_Click(object sender, EventArgs e) 
    85.         { 
    86.             btn_stop.Enabled = true; 
    87.             btn_luyin.Enabled = false; 
    88.             if (waveIn == null) 
    89.             { 
    90.                 CreateWaveInDevice(); 
    91.             } 
    92.             if (File.Exists(soundfile)) 
    93.             { 
    94.                 File.Delete(soundfile); 
    95.             } 
    96.  
    97.             writer = new WaveFileWriter(soundfile, waveIn.WaveFormat); 
    98.             waveIn.StartRecording(); 
    99.         } 

    上面的代码实现了录音,并且写入文件p2psound_A.wav

    C#基于UDP实现的P2P语音聊天工具

    语音发送

    获取到语音后我们要把语音发送出去

    当我们录好音后点击发送,这部分相关代码是

    1. MsgTranslator tran = null; 
    2. ublic Form1() 
    3.        { 
    4.            InitializeComponent(); 
    5.            LoadWasapiDevicesCombo();//显示音频设备 
    6.  
    7.            Config cfg = SeiClient.GetDefaultConfig(); 
    8.            cfg.Port = 7777; 
    9.            UDPThread udp = new UDPThread(cfg); 
    10.            tran = new MsgTranslator(udp, cfg); 
    11.            tran.MessageReceived += tran_MessageReceived; 
    12.            tran.Debuged += new EventHandler<DebugEventArgs>(tran_Debuged); 
    13.        } 
    14.  private void btn_sendsound_Click(object sender, EventArgs e) 
    15.        { 
    16.            if (t_ip.Text == "") 
    17.            { 
    18.                MessageBox.Show("请输入ip"); 
    19.                return; 
    20.            } 
    21.            if (t_port.Text == "") 
    22.            { 
    23.                MessageBox.Show("请输入端口号"); 
    24.                return; 
    25.            } 
    26.            string ip = t_ip.Text; 
    27.            int port = int.Parse(t_port.Text); 
    28.            string nick = t_nick.Text; 
    29.            string msg = "语音消息"; 
    30.  
    31.            IPEndPoint remote = new IPEndPoint(IPAddress.Parse(ip), port); 
    32.            Msg m = new Msg(remote, "zz", nick, Commands.SendMsg, msg, "Come From A"); 
    33.            m.IsRequireReceive = true; 
    34.            m.ExtendMessageBytes = FileContent(soundfile); 
    35.            m.PackageNo = Msg.GetRandomNumber(); 
    36.            m.Type = Consts.MESSAGE_BINARY; 
    37.            tran.Send(m); 
    38.        } 
    39.  private byte[] FileContent(string fileName) 
    40.        { 
    41.            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); 
    42.            try 
    43.            { 
    44.                byte[] buffur = new byte[fs.Length]; 
    45.                fs.Read(buffur, 0, (int)fs.Length); 
    46.  
    47.                return buffur; 
    48.            } 
    49.            catch (Exception ex) 
    50.            { 
    51.                return null; 
    52.            } 
    53.            finally 
    54.            { 
    55.                if (fs != null) 
    56.                { 
    57.  
    58.                    //关闭资源 
    59.                    fs.Close(); 
    60.                } 
    61.            } 
    62.        } 

    如此一来我们就把产生的语音文件发送出去了

    语音的接收与播放

    其实语音的接收和文本消息的接收没有什么不同,只不过语音发送的时候是以二进制发送的,因此我们在收到语音后 就应该写入到一个文件里面去,接收完成后,播放这段语音就行了。

    下面这段代码主要是把收到的数据保存到文件中去,这个函数式我的NetFrame里收到消息时所触发的事件,在文章前面提过的那篇文章里

    1. void tran_MessageReceived(object sender, MessageEventArgs e) 
    2.         { 
    3.             Msg msg = e.msg; 
    4.  
    5.             if (msg.Type == Consts.MESSAGE_BINARY) 
    6.             { 
    7.                 string m = msg.Type + "->" + msg.UserName + "发来二进制消息!"; 
    8.                 AddServerMessage(m); 
    9.                 if (File.Exists(recive_soundfile)) 
    10.                 { 
    11.                     File.Delete(recive_soundfile); 
    12.                 } 
    13.                 FileStream fs = new FileStream(recive_soundfile, FileMode.Create, FileAccess.Write); 
    14.                 fs.Write(msg.ExtendMessageBytes, 0, msg.ExtendMessageBytes.Length); 
    15.                 fs.Close(); 
    16.                 //play_sound(recive_soundfile); 
    17.                 ChangeBtn(true); 
    18.  
    19.             } 
    20.             else 
    21.             { 
    22.                 string m = msg.Type + "->" + msg.UserName + "说:" + msg.NormalMsg; 
    23.                 AddServerMessage(m); 
    24.             } 
    25.         } 

    收到语音消息后,我们要进行播放,播放时仍然用刚才那个插件播放

    1. //--------播放部分---------- 
    2.         private IWavePlayer wavePlayer; 
    3.         private WaveStream reader; 
    4.  
    5.         public void play_sound(string filename) 
    6.         { 
    7.             if (wavePlayer != null) 
    8.             { 
    9.                 wavePlayer.Dispose(); 
    10.                 wavePlayer = null; 
    11.             } 
    12.             if (reader != null) 
    13.             { 
    14.                 reader.Dispose(); 
    15.             } 
    16.             reader = new MediaFoundationReader(filename, new MediaFoundationReader.MediaFoundationReaderSettings() { SingleReaderObject = true }); 
    17.  
    18.             if (wavePlayer == null) 
    19.             { 
    20.  
    21.                 wavePlayer = new WaveOut(); 
    22.                 wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped; 
    23.                 wavePlayer.Init(reader); 
    24.             } 
    25.             wavePlayer.Play(); 
    26.         } 
    27.         private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs) 
    28.         { 
    29.             if (stoppedEventArgs.Exception != null) 
    30.             { 
    31.                 MessageBox.Show(stoppedEventArgs.Exception.Message); 
    32.             } 
    33.             if (wavePlayer != null) 
    34.             { 
    35.                 wavePlayer.Stop(); 
    36.             } 
    37.             btn_luyin.Enabled = true; 
    38.         }private void btn_play_Click(object sender, EventArgs e) 
    39.         { 
    40.             btn_luyin.Enabled = false; 
    41.             play_sound(soundfile); 
    42.         }

    C#基于UDP实现的P2P语音聊天工具

    C#基于UDP实现的P2P语音聊天工具

    在上面演示了接收和发送一段语音消息的界面

    技术总结

    主要用到的技术就是UDP和NAudio的录音和播放功能

    其中用到的UDP传输类我放在了github上面 地址在我的博客左边的个人介绍里有地址  项目地址 https://github.com/zhujunxxxxx/ZZNetFrame

    希望这篇文章能够提供一个思路。

  • 相关阅读:
    SQL Server 删除重复数据只保留一条
    英语冠词有哪些?
    英语基本语法
    统一身份认证服务(客户端用户身份验证)
    解决MVC中使用BundleConfig.RegisterBundles引用Css及js文件发布后丢失的问题
    统一身份认证服务 源码(客户端)
    MVC 如何设定默认默认路由为指定的Area下的某个action(笔记)
    MongoDB安装笔记
    消息队列第二篇:MessageQueue实战(课程订单)
    消息队列第一篇:MessageQueue介绍
  • 原文地址:https://www.cnblogs.com/weiweiboqi/p/4521449.html
Copyright © 2011-2022 走看看