1. 程式人生 > >c#基於udp實現的p2p語音聊天工具

c#基於udp實現的p2p語音聊天工具

initial lis combobox pre cat microsoft user eat let

原創性申明

此博文的出處 為?http://blog.csdn.net/zhujunxxxxx/article/details/40124773假設進行轉載請註明出處。本文作者原創,郵箱[email protected]。如有問題請聯系作者

概述

之前發過一篇文章http://blog.csdn.net/zhujunxxxxx/article/details/38864817?已經實現過了UDP的分包發送數據的功能。而這篇文章主要是一個應用,使用udp傳送語音和文本等信息。

在這個系統中沒有服務端和client。相互通訊都是直接相互聯系的。

可以非常好的實現效果。

語音獲取

要想發送語音信息。首先得獲取語音,這裏有幾種方法,一種是使用DirectX的DirectXsound來錄音,我為了簡便使用一個開源的插件NAudio來實現語音錄取。

在項目中引用NAudio.dll

//------------------錄音相關-----------------------------
        private IWaveIn waveIn;
        private WaveFileWriter writer;


        private void LoadWasapiDevicesCombo()
        {
            var deviceEnum = new MMDeviceEnumerator();
            var devices = deviceEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToList();
            comboBox1.DataSource = devices;
            comboBox1.DisplayMember = "FriendlyName";
        }
        private void CreateWaveInDevice()
        {

            waveIn = new WaveIn();
            waveIn.WaveFormat = new WaveFormat(8000, 1);
            waveIn.DataAvailable += OnDataAvailable;
            waveIn.RecordingStopped += OnRecordingStopped;
        }
        void OnDataAvailable(object sender, WaveInEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
            }
            else
            {
                writer.Write(e.Buffer, 0, e.BytesRecorded);
                int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond);
                if (secondsRecorded >= 10)//最大10s
                {
                    StopRecord();
                }
                else
                {
                    l_sound.Text = secondsRecorded + " s";
                }
            }
        }
        void OnRecordingStopped(object sender, StoppedEventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<StoppedEventArgs>(OnRecordingStopped), sender, e);
            }
            else
            {
                FinalizeWaveFile();
            }
        }
        void StopRecord()
        {
            AllChangeBtn(btn_luyin, true);
            AllChangeBtn(btn_stop, false);
            AllChangeBtn(btn_sendsound, true);
            AllChangeBtn(btn_play, true);


            //btn_luyin.Enabled = true;
            //btn_stop.Enabled = false;
            //btn_sendsound.Enabled = true;
            //btn_play.Enabled = true;
            if (waveIn != null)
                waveIn.StopRecording();
            //Cleanup();
        }
		private void Cleanup()
        {
            if (waveIn != null)
            {
                waveIn.Dispose();
                waveIn = null;
            }
            FinalizeWaveFile();
        }
        private void FinalizeWaveFile()
        {
            if (writer != null)
            {
                writer.Dispose();
                writer = null;
            }
        }
		 //開始錄音
        private void btn_luyin_Click(object sender, EventArgs e)
        {
            btn_stop.Enabled = true;
            btn_luyin.Enabled = false;
            if (waveIn == null)
            {
                CreateWaveInDevice();
            }
            if (File.Exists(soundfile))
            {
                File.Delete(soundfile);
            }

            writer = new WaveFileWriter(soundfile, waveIn.WaveFormat);
            waveIn.StartRecording();
        }




上面的代碼實現了錄音,而且寫入文件p2psound_A.wav

技術分享圖片

語音發送

獲取到語音後我們要把語音發送出去技術分享圖片

當我們錄好音後點擊發送,這部分相關代碼是

 MsgTranslator tran = null;
 public Form1()
        {
            InitializeComponent();
            LoadWasapiDevicesCombo();//顯示音頻設備

            Config cfg = SeiClient.GetDefaultConfig();
            cfg.Port = 7777;
            UDPThread udp = new UDPThread(cfg);
            tran = new MsgTranslator(udp, cfg);
            tran.MessageReceived += tran_MessageReceived;
            tran.Debuged += new EventHandler<DebugEventArgs>(tran_Debuged);
        }
		private void btn_sendsound_Click(object sender, EventArgs e)
        {
            if (t_ip.Text == "")
            {
                MessageBox.Show("請輸入ip");
                return;
            }
            if (t_port.Text == "")
            {
                MessageBox.Show("請輸入端口號");
                return;
            }
            string ip = t_ip.Text;
            int port = int.Parse(t_port.Text);
            string nick = t_nick.Text;
            string msg = "語音消息";

            IPEndPoint remote = new IPEndPoint(IPAddress.Parse(ip), port);
            Msg m = new Msg(remote, "zz", nick, Commands.SendMsg, msg, "Come From A");
            m.IsRequireReceive = true;
            m.ExtendMessageBytes = FileContent(soundfile);
            m.PackageNo = Msg.GetRandomNumber();
            m.Type = Consts.MESSAGE_BINARY;
            tran.Send(m);
        }
		private byte[] FileContent(string fileName)
        {
            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);

                return buffur;
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                if (fs != null)
                {

                    //關閉資源
                    fs.Close();
                }
            }
        }

如此一來我們就把產生的語音文件發送出去了


語音的接收與播放

事實上語音的接收和文本消息的接收沒有什麽不同,僅僅只是語音發送的時候是以二進制發送的。因此我們在收到語音後 就應該寫入到一個文件中面去,接收完畢後。播放這段語音即可了。

以下這段代碼主要是把收到的數據保存到文件中去。這個函數式我的NetFrame裏收到消息時所觸發的事件,在文章前面提過的那篇文章裏

void tran_MessageReceived(object sender, MessageEventArgs e)
        {
            Msg msg = e.msg;

            if (msg.Type == Consts.MESSAGE_BINARY)
            {
                string m = msg.Type + "->" + msg.UserName + "發來二進制消息!";
                AddServerMessage(m);
                if (File.Exists(recive_soundfile))
                {
                    File.Delete(recive_soundfile);
                }
                FileStream fs = new FileStream(recive_soundfile, FileMode.Create, FileAccess.Write);
                fs.Write(msg.ExtendMessageBytes, 0, msg.ExtendMessageBytes.Length);
                fs.Close();
                //play_sound(recive_soundfile);
                ChangeBtn(true);

            }
            else
            {
                string m = msg.Type + "->" + msg.UserName + "說:" + msg.NormalMsg;
                AddServerMessage(m);
            }
        }

收到語音消息後,我們要進行播放,播放時仍然用剛才那個插件播放

//--------播放部分----------
        private IWavePlayer wavePlayer;
        private WaveStream reader;


        public void play_sound(string filename)
        {
            if (wavePlayer != null)
            {
                wavePlayer.Dispose();
                wavePlayer = null;
            }
            if (reader != null)
            {
                reader.Dispose();
            }
            reader = new MediaFoundationReader(filename, new MediaFoundationReader.MediaFoundationReaderSettings() { SingleReaderObject = true });

            if (wavePlayer == null)
            {

                wavePlayer = new WaveOut();
                wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped;
                wavePlayer.Init(reader);
            }
            wavePlayer.Play();
        }
        private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs)
        {
            if (stoppedEventArgs.Exception != null)
            {
                MessageBox.Show(stoppedEventArgs.Exception.Message);
            }
            if (wavePlayer != null)
            {
                wavePlayer.Stop();
            }
            btn_luyin.Enabled = true;
        }private void btn_play_Click(object sender, EventArgs e)
        {
            btn_luyin.Enabled = false;
            play_sound(soundfile);
        }

技術分享圖片

技術分享圖片

技術分享圖片

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


技術總結

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

當中用到的UDP傳輸類我放在了github上面 地址在我的博客左邊的個人介紹裏有地址 ?項目地址?https://github.com/zhujunxxxxx/ZZNetFrame

希望這篇文章可以提供一個思路。


更新程序下載地址?http://download.csdn.net/detail/zhujunxxxxx/8061125?非常好的代碼,希望大家喜歡


c#基於udp實現的p2p語音聊天工具