Лучший способ отправить голос через сокет в AS3 - PullRequest
2 голосов
/ 10 марта 2012

Я сделал приложение AIR, которое передает голос между двумя компьютерами, это код:

package
{
    import fl.controls.List;

    import flash.events.ProgressEvent;
    import flash.events.SampleDataEvent;
    import flash.events.ServerSocketConnectEvent;
    import flash.media.Microphone;
    import flash.media.Sound;
    import flash.net.ServerSocket;
    import flash.net.Socket;
    import flash.utils.ByteArray;
    import flash.system.System;

    public class VoiceCommunication
    {
        private var soundBytes:ByteArray;
        private var mic:Microphone;
        private var voiceReceiver:ServerSocket;
        private var voiceSender:Socket;
        private var mLocalIP:String;
        private var mRemoteIP:String;
        private var mPort:uint;
        private var sound:Sound;
        private var sample:Number;

        public function VoiceCommunication(localIP:String, port:uint)
        {
            mLocalIP = localIP;
            mPort = port;
        }

        public function startSendingVoice(remoteIP:String = null):void
        {
            mic = Microphone.getMicrophone();//2-null
            mic.gain = 100;
            mic.rate = 44;
            voiceSender = new Socket();//3-null

            mRemoteIP = remoteIP;//5-null
            mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);//1-remove eventlistener

        }

        protected function micSampleDataHandler(event:SampleDataEvent):void
        {
            if(mRemoteIP != null)
            {
                voiceSender.connect(mRemoteIP, mPort);
                while(event.data.bytesAvailable)
                {
                    sample = event.data.readFloat();
                    voiceSender.writeFloat(sample);
                }
                voiceSender.flush();
            }
        }

        public function startReceivingVoice():void
        {
            voiceReceiver = new ServerSocket;//2
            voiceReceiver.bind(mPort, mLocalIP);
            voiceReceiver.addEventListener(ServerSocketConnectEvent.CONNECT, handleConnection);//1
            voiceReceiver.listen();
        }

        protected function handleConnection(event:ServerSocketConnectEvent):void
        {
            var s:Socket = event.socket as Socket;
            s.addEventListener(ProgressEvent.SOCKET_DATA, handleData);      
        }

        protected function handleData(event:ProgressEvent):void
        {
            event.target.removeEventListener(ProgressEvent.SOCKET_DATA, handleData);
            var s:Socket = event.target as Socket;
            soundBytes = null;
            soundBytes = new ByteArray();//3
            s.readBytes(soundBytes);
            playSound();
            s = null;
        }

        private function playSound():void
        {
            soundBytes.position = 0;
            sound = null;
            sound = new Sound();//5
            sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);//4
            sound.play();       
        }

        protected function playbackSampleHandler(event:SampleDataEvent):void
        {
            sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
            for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++) 
            {
                sample = soundBytes.readFloat();
                event.data.writeFloat(sample);
                event.data.writeFloat(sample);
            }
            System.gc();
        }

        public function destroySender():void
        {
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
            mic = null;
            voiceSender = null;
            mRemoteIP = null;
            System.gc();
        }

        public function destroyReceiver():void
        {
            voiceReceiver.removeEventListener(ServerSocketConnectEvent.CONNECT, handleConnection);//1
            voiceReceiver = null;
            soundBytes = null;
            sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
            sound = null;
            System.gc();
        }
    }
}

Это работает нормально, и когда я попробовал, он отправил голос, но проблема в том, что отправка голоса идет слишком медленно и разделяется. Так есть ли лучший способ сделать это?

Я переписал его с помощью датаграммы, но это не сработало, код:

package
{
    import fl.controls.List;

    import flash.events.DatagramSocketDataEvent;
    import flash.events.ProgressEvent;
    import flash.events.SampleDataEvent;
    import flash.events.ServerSocketConnectEvent;
    import flash.media.Microphone;
    import flash.media.Sound;
    import flash.net.DatagramSocket;
    import flash.net.ServerSocket;
    import flash.net.Socket;
    import flash.system.System;
    import flash.utils.ByteArray;

    public class VoiceComm
    {
        private var soundBytes:ByteArray;
        private var mic:Microphone;
        private var socket:DatagramSocket
        private var mList:List;
        private var mLocalIP:String;
        private var mRemoteIP:String;
        private var mPort:uint;
        private var sound:Sound;
        private var sample:Number;
        private var bytesToBeSent:ByteArray;

        public function VoiceComm(localIP:String, port:uint)
        {
            socket = new DatagramSocket();
            mLocalIP = localIP;
            mPort = port;
        }

        public function startSendingVoice(remoteIP:String = null):void
        {
            mic = Microphone.getMicrophone();
            mic.gain = 100;
            mic.rate = 44;
            mRemoteIP = remoteIP;
            mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

        }

        protected function micSampleDataHandler(event:SampleDataEvent):void
        {
            var sample:Number;
            if(mRemoteIP != null)
            {
                while(event.data.bytesAvailable)
                {
                    sample = event.data.readFloat();
                    bytesToBeSent = null;
                    bytesToBeSent = new ByteArray();
                    bytesToBeSent.writeFloat(sample);
                    socket.send(bytesToBeSent, 0, 0, mRemoteIP, mPort);
                }
            }
        }

        public function startReceivingVoice():void
        {
            socket.bind(mPort, mLocalIP);
            socket.addEventListener(DatagramSocketDataEvent.DATA, handleData);
            socket.receive();
        }


        protected function handleData(event:DatagramSocketDataEvent):void
        {

            trace("Data inthere!");
            soundBytes = null;
            soundBytes = new ByteArray();//3
            event.data.readBytes(soundBytes);
            playSound();

        }

        private function playSound():void
        {
            soundBytes.position = 0;
            sound = null;
            sound = new Sound();//5
            sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);//4
            sound.play();       
        }

        protected function playbackSampleHandler(event:SampleDataEvent):void
        {
            sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
            for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++) 
            {
                sample = soundBytes.readFloat();
                event.data.writeFloat(sample);
                event.data.writeFloat(sample);
            }
            System.gc();
        }

        public function destroySender():void
        {
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
            mic = null;
            mList = null;
            mRemoteIP = null;
            System.gc();
        }

        public function destroyReceiver():void
        {
            soundBytes = null;
            sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
            sound = null;
            System.gc();
        }
    }
}

** Обновление:

Код после модификации:

package
{
    import fl.controls.List;
    import flash.events.DatagramSocketDataEvent;
    import flash.events.SampleDataEvent;
    import flash.media.Microphone;
    import flash.media.Sound;
    import flash.net.DatagramSocket;
    import flash.net.Socket;
    import flash.system.System;
    import flash.utils.ByteArray;

    public class VoiceComm
    {
        private var mic:Microphone;
        private var soundBytes:ByteArray;
        private var sample:Number;
        private var sound:Sound;
        private var socket:DatagramSocket;
        private var mLocalIP:String;
        private var mPort:uint;
        private var mRemoteIP:String;
        private var receivedBytes:ByteArray;

        public function VoiceComm(localIP:String, port:uint)
        {
            socket = new DatagramSocket();
            mLocalIP = localIP;
            mPort = port;
        }
        //destroyers:

        public function destroySender():void
        {
            mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
            mic = null;
            soundBytes = null;
            System.gc();
        }

        public function destroyReceiver():void
        {
            socket.removeEventListener(DatagramSocketDataEvent.DATA, playSound);
            receivedBytes = null;
            System.gc();
        }

        //sending part
        public function startSendingVoice(ip:String = null):void
        {
            mRemoteIP = ip;
            mic = Microphone.getMicrophone();
            mic.rate = 44;
            mic.gain = 100;
            mic.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
        }

        protected function sampleData(event:SampleDataEvent):void
        {
            soundBytes = new ByteArray();
            while(event.data.bytesAvailable)
            {
                var sample:Number = event.data.readFloat();
                soundBytes.writeFloat(sample);
            }
            if(mRemoteIP != null)
            {
                event.data.position = 0;
                socket.send(soundBytes, 0, event.data.length, mRemoteIP, mPort);
            }
        }


        //receiving part
        public function startReceivingVoice():void
        {           
            socket.bind(mPort, mLocalIP);
            socket.addEventListener(DatagramSocketDataEvent.DATA, playSound);
            socket.receive();
        }

        protected function playSound(event:DatagramSocketDataEvent):void
        {
            trace("got data!");
            receivedBytes = new ByteArray();
            event.data.readBytes(receivedBytes);
            receivedBytes.position = 0;
            var sound:Sound = new Sound();
            sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler, false, 0, true);
            sound.play();
        }

        protected function playbackSampleHandler(event:SampleDataEvent):void
        {
            trace("Playing Sound...");
            for (var i:int = 0; i < 8192 && receivedBytes.bytesAvailable > 0; i++) 
            {
                var sample:Number = receivedBytes.readFloat();
                event.data.writeFloat(sample);
                event.data.writeFloat(sample);
                System.gc();
            }

        }

    }
}

Ответы [ 3 ]

2 голосов
/ 10 марта 2012

Это, скорее всего, источник проблемы: http://forums.adobe.com/message/3111816. Алгоритм TCP Nagle.По сути, это то, что когда вы записываете кучу небольших порций данных в сокет, вместо того, чтобы отправлять их все по отдельности, он будет удерживать их и объединять их вместе, чтобы отправить один «полный» пакет, а нетонны мелких (что приводит к большей постоянной нагрузке на сеть).

Если возможно, переписать, используя вместо этого сокеты датаграмм.Насколько мне известно, в настоящее время нет способа отключить алгоритм Nagle во Flash.

Обновление

Несколько вещей после тщательного изучения вашего кода: во-первых,Вы записываете каждый байт по одному в сокет TCP.Не делайте этого, напишите весь bytearray и очистите его.

if(mRemoteIP != null)
{
    voiceSender.connect(mRemoteIP, mPort);
    voiceSender.writeBytes(event.data, 0, event.data.length);
    voiceSender.flush();
}

Это может решить ваши проблемы.Если нет, то причина, по которой ваша версия UDP по двум причинам.Во-первых, опять же, вы записываете по одному байту за раз и отправляете, но, что более важно, вы говорите ему вообще не записывать данные в сокет.Эта строка является виновником:

socket.send(bytesToBeSent, 0, 0, mRemoteIP, mPort);

Обратите внимание на третий параметр, "0".Этот параметр представляет собой объем данных из предоставленного байтового массива, который должен быть записан в сокет.Так что, по сути, вы говорите, не отправляйте ничего.Измените код следующим образом:

if(isConnected == true)
{
    event.data.position = 0;
    localSendingSocket.send(event.data, 0, event.data.length, remoteIP, remotePort);
}

Дайте мне знать, если это сработает для вас.Тем временем я переписываю ваш код на основе этих подозрений, поэтому, если мне удастся получить проект, скомпилированный и работающий до вас, я опубликую файлы проекта здесь.

0 голосов
/ 29 марта 2013

Где не использовать Adobe Cirrus (ранее Stratus для создания приложения peer2peer вместо сокета?

0 голосов
/ 10 марта 2012

использовать сервер (OS) Red5.

http://www.red5.org/

реклама:

"Red5 Media Server 1.0 предоставляет мощное потоковое видео и многопользовательское решение для © Adobe © Flash Player и других захватывающих клиентских технологий. Основанное на Java и некоторых из самых мощных сред с открытым исходным кодом, Red5 представляет собой надежное решение для бизнеса любых размеров, включая предприятие. Red5 включает поддержку последних многопользовательских API, включая NetConnection, NetStream и SharedObject, и в то же время предоставляет мощную реализацию RTMP / сервлетов. Помимо поддержки протокола RTMP, на сервере приложений имеется встроенный контейнер сервлетов Tomcat для веб-приложений Java EE. Разработка приложений получает дополнительные преимущества от событийно-ориентированных сервисов на основе Spring Framework и Scope. Используя Red Source Media Server с открытым исходным кодом, вы разрабатываете с действительно открытой и расширяемой платформой, которая может использоваться в видеоконференциях, многопользовательских играх и прикладном программном обеспечении для предприятий. Удачного кодирования и наслаждайтесь нашим мощным бесплатным сервером сообщества "

...