我制作了一个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();
}
}
}
}
这很可能是问题的根源:http://forums.adobe.com/message/3111816.TCP Nagle算法。这基本上是这样做的,当你向套接字写入一堆小块数据时,它不会单独发送它们,而是将它们保留起来并汇集在一起,以便发送一个"完整"的数据包,而不是大量的小数据包(这会导致网络上更大、更稳定的负载)。
如果可能,请使用数据报套接字重新写入。据我所知,目前没有办法禁用Flash中的Nagle算法。
更新
在深入研究代码后,有几件事:第一件事是将每个字节一次一个地写入TCP套接字。不要这样做,写下整个字节数组并刷新它
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);
}
如果这对你有用,请告诉我。与此同时,我正在根据这些怀疑重新编写您的代码,所以如果我能在您之前编译并工作一个项目,我会在这里发布项目文件。
哪里不使用Adobe Cirrus(以前的Stratus)来创建peer2peer应用程序而不是套接字?
使用(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框架和基于范围的事件驱动服务中获得了额外的好处。通过使用开源Red5媒体服务器,您正在开发一个真正开放和可扩展的平台,该平台可用于视频会议、多用户游戏和企业应用软件。快乐编码,享受我们强大的免费社区服务器"