none
Передача звука по сети(VS 08, C#, DirectSound, TCP) RRS feed

  • Вопрос

  • В общем пытаюсь написать элементарное приложеньице для передачи захватываемого с микрофона звука по сети:

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Microsoft.DirectX.DirectSound;
    using System.IO;
    using System.Threading;
    using System.Net;
    using System.Net.Sockets;
    
    namespace My_Voise_Chat
    {
        public partial class Form1 : Form
        {
    
            private CaptureBufferDescription captureBufferDescription;
            private AutoResetEvent autoResetEvent;
            private Notify notify;
            private WaveFormat waveFormat;
            private Capture capture;
            private int bufferSize;
            private CaptureBuffer captureBuffer;              //Listens and sends data on port 1550, used in synchronous mode.
            private Device device;
            private SecondaryBuffer playbackBuffer;
            private BufferDescription playbackBufferDescription;
            private bool bStop;
            private byte[] byteData = new byte[1024];
            private string ip;
            private int port;
            private volatile int nUdpClientFlag;
            private volatile bool bIsCallActive;
            
    
            NetworkStream myns;
            BinaryWriter mysw;
            TcpClient myclient;
    
    
            TcpListener mytcpl;
            Socket mysocket;
            NetworkStream ns;
    
            public Form1()
            {
                InitializeComponent();
                Initialize();
            }
            private void Initialize()
            {
                try
                {
                    device = new Device();
                    device.SetCooperativeLevel(this, CooperativeLevel.Normal);
    
                    CaptureDevicesCollection captureDeviceCollection = new CaptureDevicesCollection();
    
                    DeviceInformation deviceInfo = captureDeviceCollection[0];
    
                    capture = new Capture(deviceInfo.DriverGuid);
    
                    short channels = 1; //Stereo.
                    short bitsPerSample = 16; //16Bit, alternatively use 8Bits.
                    int samplesPerSecond = 22050; //11KHz use 11025 , 22KHz use 22050, 44KHz use 44100 etc.
    
                    //Set up the wave format to be captured.
                    waveFormat = new WaveFormat();
                    waveFormat.Channels = channels;
                    waveFormat.FormatTag = WaveFormatTag.Pcm;
                    waveFormat.SamplesPerSecond = samplesPerSecond;
                    waveFormat.BitsPerSample = bitsPerSample;
                    waveFormat.BlockAlign = (short)(channels * (bitsPerSample / (short)8));
                    waveFormat.AverageBytesPerSecond = waveFormat.BlockAlign * samplesPerSecond;
    
                    captureBufferDescription = new CaptureBufferDescription();
                    captureBufferDescription.BufferBytes = waveFormat.AverageBytesPerSecond / 5;//approx 200 milliseconds of PCM data.
                    captureBufferDescription.Format = waveFormat;
    
                    playbackBufferDescription = new BufferDescription();
                    playbackBufferDescription.BufferBytes = waveFormat.AverageBytesPerSecond / 5;
                    playbackBufferDescription.Format = waveFormat;
                    playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device);
    
                    bufferSize = captureBufferDescription.BufferBytes;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "VoiceChat-Initialize ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Call();
            }
            private void Call()
            {
                try
                {
                    ip = textBox1.Text;
                    port = int.Parse(textBox2.Text);
                    //Get the IP we want to call.
                    InitializeCall();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "VoiceChat-Call ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            private void Send()
            {
                try
                {
                    //The following lines get audio from microphone and then send them 
                    //across network.
    
                    captureBuffer = new CaptureBuffer(captureBufferDescription, capture);
    
                    CreateNotifyPositions();
    
                    int halfBuffer = bufferSize / 2;
    
                    captureBuffer.Start(true);
    
                    bool readFirstBufferPart = true;
                    int offset = 0;
                    MemoryStream memStream = new MemoryStream();
                    
                    bStop = false;
                    while (!bStop)
                    {
                        autoResetEvent.WaitOne();
                        memStream.Seek(0, SeekOrigin.Begin);
                        captureBuffer.Read(offset, memStream, halfBuffer, LockFlag.None);
                        readFirstBufferPart = !readFirstBufferPart;
                        offset = readFirstBufferPart ? 0 : halfBuffer;
    
                        //TODO: Fix this ugly way of initializing differently.
    
                        //Choose the vocoder. And then send the data to other party at port 1550.
    
    
    
                        byte[] dataToWrite = memStream.GetBuffer();
                        MemoryStream memS = new MemoryStream();
                        int Len = dataToWrite.Length;
                        byte[] arrLen = BitConverter.GetBytes(Len);
                        memS.Write(arrLen, 0, arrLen.Length);
                        memS.Write(dataToWrite, 0, dataToWrite.Length);
                        byte[] ToWrite = memS.GetBuffer();
                        //udpClient.Send(dataToWrite, dataToWrite.Length, otherPartyIP.Address.ToString(), 1550);
                        myclient = new TcpClient(ip, port);//Connecting with server
                        myns = myclient.GetStream();
                        mysw = new BinaryWriter(myns);
                        mysw.Write(ToWrite);//send the stream to above address
                        //memStream.Flush();
                        mysw.Flush();
                        myns.Flush();
                        
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "VoiceChat-Send ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    captureBuffer.Stop();
                }
            }
    
            private void Receive()
            {
                try
                {
                        //Receive data.
                        IPAddress ipAdd = IPAddress.Parse(ip);
                        mytcpl = new TcpListener(ipAdd, port);
    
                        mytcpl.Start();						 // Start Listening on That Port
    
                        mytcpl.AcceptTcpClient();
                        mysocket = mytcpl.AcceptSocket();		 // Accept Any Request From Client and Start a Session
                        ns = new NetworkStream(mysocket);
    
                        byte[] arrLenght = new byte[4];
                        ns.Read(arrLenght, 0, 4);
                        int len = BitConverter.ToInt32(arrLenght, 0);
    
                        byte[] byteData = new byte[len];
                        
                        //Считываем и заносим в очередь
                        int tmp = 0;
                        while (tmp < len)
                        {
                            tmp += ns.Read(byteData, tmp, len - tmp);
                        }
                        
                        //byte[] byteData = udpClient.Receive(ref remoteEP);
    
                        //G711 compresses the data by 50%, so we allocate a buffer of double
                        //the size to store the decompressed data.
                        byte[] byteDecodedData = new byte[byteData.Length * 2];
    
                        //Decompress data using the proper vocoder.
    
                        byteDecodedData = new byte[byteData.Length];
                        byteDecodedData = byteData;
    
    
    
                        //Play the data received to the user.
                        playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device);
                        playbackBuffer.Write(0, byteDecodedData, LockFlag.None);
                        playbackBuffer.Play(0, BufferPlayFlags.Default);
                        mytcpl.Stop();
                        if (mysocket.Connected == true)		     // Looping While Connected to Receive Another Message 
                        {
                            while (true)
                            {
                                Receive();				 // Back to First Method
                            }
                        }
                    
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "VoiceChat-Receive ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
    
            private void CreateNotifyPositions()
            {
                try
                {
                    autoResetEvent = new AutoResetEvent(false);
                    notify = new Notify(captureBuffer);
                    BufferPositionNotify bufferPositionNotify1 = new BufferPositionNotify();
                    bufferPositionNotify1.Offset = bufferSize / 2 - 1;
                    bufferPositionNotify1.EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle();
                    BufferPositionNotify bufferPositionNotify2 = new BufferPositionNotify();
                    bufferPositionNotify2.Offset = bufferSize - 1;
                    bufferPositionNotify2.EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle();
    
                    notify.SetNotificationPositions(new BufferPositionNotify[] { bufferPositionNotify1, bufferPositionNotify2 });
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "VoiceChat-CreateNotifyPositions ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
    
            private void InitializeCall()
            {
                try
                {
                    //Start listening on port 1500.
    
                    Thread receiverThread = new Thread(new ThreadStart(Receive));
                    Thread senderThread = new Thread(new ThreadStart(Send));
                    
                    bIsCallActive = true;
    
                    //Start the receiver and sender thread.
                    receiverThread.Start();
                    //Receive();
                    senderThread.Start();
                    
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "VoiceChat-InitializeCall ()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
    
            private void UninitializeCall()
            {
                //Set the flag to end the Send and Receive threads.
                bStop = true;
    
                bIsCallActive = false;
            }
    
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                if (bIsCallActive)
                {
                    UninitializeCall();
                }
            }
    
    
    
        }
    }
    
    В общем все бы хорошо, но звук приходит не полностью. Такое ощущение что каждый второй пакет не передается. И получается я слышу себя урывками. Помоите разобратся в чем дело.

    • Перемещено Tagore Bandlamudi 1 октября 2010 г. 22:37 MSDN Forums consolidation (От:Visual C#)
    28 марта 2010 г. 15:25

Ответы

  • Добрый день!

    Собственно говоря, UDP протокол в примере выбран не просто так. TCP – протокол гарантирующий доставку всех пакетов, UDP - не гарантирует доставку пакета (датаграмм), но позволяет получить большую скорость при передаче данных. Собственно говоря, поэтому его и используют при передаче потокового видео и звука, причем при передаче потокового звука, Вам не так уж и страшно, если потеряется пакет с некоторым числом отсчетов.


    • Предложено в качестве ответа I.Vorontsov 31 марта 2010 г. 9:39
    • Помечено в качестве ответа I.Vorontsov 31 марта 2010 г. 14:50
    30 марта 2010 г. 14:11

Все ответы

  • Для примера посмотрите следующую реализацию:

    A Voice Chat Application in C#
    Для связи [Mail]
    29 марта 2010 г. 6:02
  • Для примера посмотрите следующую реализацию:

    A Voice Chat Application in C#
    Для связи [Mail]

     

    Собственно говоря на основе этого примера и делал я свое приложение, а именно сменил протокол - с UDP на TCP, и поубирал сжатие. И вот Сами видите что из этого вышло.

     

    Путем долгих разбирательств вычислил - что проблемы начинаются здесь:

     

     

    while (!bStop)
                    {
                        autoResetEvent.WaitOne();
                        memStream.Seek(0, SeekOrigin.Begin);
                        captureBuffer.Read(offset, memStream, halfBuffer, LockFlag.None);
                        readFirstBufferPart = !readFirstBufferPart;
                        offset = readFirstBufferPart ? 0 : halfBuffer;
    
                        //TODO: Fix this ugly way of initializing differently.
    
                        //Choose the vocoder. And then send the data to other party at port 1550.
    
    
    
                        byte[] dataToWrite = memStream.GetBuffer();
                        MemoryStream memS = new MemoryStream();
                        int Len = dataToWrite.Length;
                        byte[] arrLen = BitConverter.GetBytes(Len);
                        memS.Write(arrLen, 0, arrLen.Length);
                        memS.Write(dataToWrite, 0, dataToWrite.Length);
                        byte[] ToWrite = memS.GetBuffer();
                        //udpClient.Send(dataToWrite, dataToWrite.Length, otherPartyIP.Address.ToString(), 1550);
                        myclient = new TcpClient(ip, port);//Connecting with server
                        myns = myclient.GetStream();
                        mysw = new BinaryWriter(myns);
                        mysw.Write(ToWrite);//send the stream to above address
                        //memStream.Flush();
                        mysw.Flush();
                        myns.Flush();
                        
                    }

     

    Т.е. все работает нормально, но почему-то отправляется только пакет при каждом втором прохождении цикла в функции Send(), который я показал чуть выше. Т.е. каждый раз вызывается 

    mysw.Write(ToWrite);//send the stream to above address
    , но почему то на принимающей стороне принимается именно каждый второй. Как будто половина вовсе не отправляется...

     

    29 марта 2010 г. 15:24
  • Добрый день!

    Собственно говоря, UDP протокол в примере выбран не просто так. TCP – протокол гарантирующий доставку всех пакетов, UDP - не гарантирует доставку пакета (датаграмм), но позволяет получить большую скорость при передаче данных. Собственно говоря, поэтому его и используют при передаче потокового видео и звука, причем при передаче потокового звука, Вам не так уж и страшно, если потеряется пакет с некоторым числом отсчетов.


    • Предложено в качестве ответа I.Vorontsov 31 марта 2010 г. 9:39
    • Помечено в качестве ответа I.Vorontsov 31 марта 2010 г. 14:50
    30 марта 2010 г. 14:11
  • А не подскажите, как передать потоковое видео по сети, а то с видео никогда не работал, или статейку какую нибудь.
    18 февраля 2012 г. 12:53
  • Здравствуйте!

    Тут справедливо замечено что надо бы по протоколу UDP вести передачу. И ещё. Если будете напрямую вызывать waveIn и waveOut то непрерывного звука не добьётесь. Создайте лучше фильтр DirectShow, и всё станет непрерывно....

    21 сентября 2015 г. 10:20