在AS3中通过套接字发送语音的更好方法

时间:2012-03-10 05:57:41

标签: actionscript-3 sockets tcp voice

我制作了一个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 个答案:

答案 0 :(得分:2)

这很可能是问题的根源:http://forums.adobe.com/message/3111816。 TCP Nagle算法。这基本上是做什么的,当你向套接字写入一堆小数据时,而不是将它们全部单独发送出来,它会将它们保留回来并将它们汇集在一起​​以便发送一个“完整”数据包而不是大量小的(导致网络上更大,恒定的负载)。

如果可能,请使用数据报套接字重写。据我所知,目前无法在Flash中禁用Nagle算法。

<强>更新

深入查看代码之后的几件事:第一个是你每次向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”。该参数是应该写入套接字的提供的bytearray中的数据量。所以你基本上是说,不要发送任何东西。像这样更改代码:

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

让我知道这是否适合您。与此同时,我正在根据这些怀疑重新编写代码,所以如果我设法在你之前编译并运行一个项目,我会在这里发布项目文件。

答案 1 :(得分:0)

使用(OS)Red5服务器。

http://www.red5.org/

模糊:

“Red5 Media Server 1.0为©Adobe©Flash Player和其他令人兴奋的客户端技术提供了强大的视频流和多用户解决方案。基于Java和一些最强大的开源框架,Red5是一个可靠的解决方案适用于各种规模的企业,包括企业。 Red5包括对最新的多用户API的支持,包括NetConnection,NetStream和SharedObject,同时提供强大的RTMP / Servlet实现。除了支持RTMP协议之外,应用程序服务器还具有用于Java EE Web应用程序的嵌入式Tomcat Servlet容器。应用程序开发从Spring Framework和基于Scope的事件驱动服务中获得额外的好处。 通过使用开源Red5媒体服务器,您正在开发一个真正开放和可扩展的平台,可用于视频会议,多用户游戏和企业应用软件。 快乐编码,享受我们强大的免费社区服务器“

答案 2 :(得分:0)

哪里不使用Adobe Cirrus(以前用Stratus创建peer2peer应用程序而不是套接字?