首页 > 代码库 > Android RakNet 系列之四 实现消息、语音、文件传输

Android RakNet 系列之四 实现消息、语音、文件传输

简介

       RakNet在Win平台上已经实现消息、语音、文件传输了,但在Android平台下尚未实现,笔者决定把源码移植到Android平台下测试。

详情

实现消息

项目自带Chat Example Client和Chat Example Server实现消息,源码简单易懂,此处就不介绍了,直接贴上图片。


测试通过,消息是以Toast方式显示的,图片未捕捉到显示。

实现语音

Win平台下实现语音是通过Portaudio进行的,Portaudio尚未支持Android,要实现语音怎么办?

Java层实现可以,但笔者觉得麻烦,决定移植Portaudio到Android。关于这方面的信息请查看博客:Android RakNet 系列之三 移植Portaudio。

实现原理:Raknet通过PortAudio进行语音采集以及播放,再通过RakVoice语音插件进行语音发送与接受,RakVoice中使用了speex编码传输。

Jni实现主要代码如下:

class Voice { //声音一个操作语音的类
public:
	Voice();
	virtual ~Voice();
	
	void run(const char* ip); //关键执行
	int portInAudioCallback(const void *inputBuffer);
	int portOutAudioCallback(void *inputBuffer);
	void waitForStop();
	void stop();
public:
	bool mute;
	RakNet::RakPeerInterface *rakPeer;
	RakNet::RakVoice rakVoice;
	PaError err;
	bool isStoped;
	bool runState;
};
void Voice::run(const char* ip) { //通过Ip连接对方机子,然后传输。
	////////////////////////////////////////////
	if (err != paNoError) {
		LOGI("Pa_Initialize fail: %s",Pa_GetErrorText(err));
		return;
	}

	mute = false;

	PaStream *stream;

	unsigned int maxConnectionsAllowed = 4;
	unsigned int maxPlayersPerServer = 4;
	unsigned short serverPort = 6000;

	RakNet::SocketDescriptor socketDescriptor(serverPort, 0);

	if (rakPeer->Startup(maxConnectionsAllowed, &socketDescriptor, 1) != RakNet::RAKNET_STARTED) {
		LOGI("Startup fail:");
		return;
	}

	rakPeer->SetMaximumIncomingConnections(maxPlayersPerServer);

	rakPeer->AttachPlugin(&rakVoice);
	rakVoice.Init(SAMPLE_RATE, FRAMES_PER_BUFFER * sizeof (short));

	PaDeviceIndex numdev;
	const PaDeviceInfo *info;
	int i;

	numdev = Pa_GetDeviceCount();

	PaStreamParameters inparam, outparam;
	memset(&inparam, 0, sizeof (PaStreamParameters));

	inparam.device = Pa_GetDefaultInputDevice();
	inparam.channelCount = 1;
	inparam.sampleFormat = paInt16;

	memset(&outparam, 0, sizeof (PaStreamParameters));

	outparam.device = Pa_GetDefaultOutputDevice();
	outparam.channelCount = 1;
	outparam.sampleFormat = paInt16;

	PaError err = Pa_OpenStream(&stream,
		&inparam,
		&outparam, 
		SAMPLE_RATE, 
		FRAMES_PER_BUFFER,
		paClipOff,
		PAInOutCallback,
		this);
	err = Pa_SetStreamFinishedCallback(stream, &StreamFinished );
	if(err != paNoError){
		LOGI("Cannot set stream finish callback");
		return;
	}

	if (err != paNoError) {
		LOGI("Pa_OpenStream fail: %s",Pa_GetErrorText(err));
		return;
	}

	err = Pa_StartStream(stream);

	if (err != paNoError) {
		LOGI("Pa_StartStream fail: %s",Pa_GetErrorText(err));
		return;
	}

	if (ip) {
		LOGI("Connect: %s",ip);
		rakPeer->Connect(ip, serverPort, 0, 0);
	}

	RakNet::Packet *p;
	unsigned char typeId;
	isStoped = false;
	runState = true;
	while (1) {
		if(!runState)
		{
			Pa_AbortStream(stream);
			Pa_CloseStream(stream);
			isStoped = true;
			return;
		}

		p=rakPeer->Receive();
		while (p)
		{
			LOGI("Receive data from: %s   guid",p->systemAddress.ToString(),p->guid.ToString());

			if (p->data[0]==ID_CONNECTION_REQUEST_ACCEPTED)
			{
					LOGI("ID_CONNECTION_REQUEST_ACCEPTED from %s\n", p->systemAddress.ToString());
					rakVoice.RequestVoiceChannel(p->guid);
			}
			else if (p->data[0]==ID_CONNECTION_ATTEMPT_FAILED)
			{
					LOGI("ID_CONNECTION_ATTEMPT_FAILED\n");
			}
			else if (p->data[0]==ID_RAKVOICE_OPEN_CHANNEL_REQUEST || p->data[0]==ID_RAKVOICE_OPEN_CHANNEL_REPLY)
			{
				LOGI("Got new channel from %s\n", p->systemAddress.ToString());
			}
			else if (p->data[0]==ID_NAT_TARGET_NOT_CONNECTED)
			{
				RakNet::RakNetGUID g;
				RakNet::BitStream b(p->data, p->length, false);
				b.IgnoreBits(8); // Ignore the ID_...
				b.Read(g);
				LOGI("ID_NAT_TARGET_NOT_CONNECTED for %s\n", g.ToString());
			}
			else if (p->data[0]==ID_NAT_TARGET_UNRESPONSIVE)
			{
				RakNet::RakNetGUID g;
				RakNet::BitStream b(p->data, p->length, false);
				b.IgnoreBits(8); // Ignore the ID_...
				b.Read(g);
				LOGI("ID_NAT_TARGET_UNRESPONSIVE for %s\n", g.ToString());
			}
			else if (p->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST)
			{
				RakNet::RakNetGUID g;
				RakNet::BitStream b(p->data, p->length, false);
				b.IgnoreBits(8); // Ignore the ID_...
				b.Read(g);
				LOGI("ID_NAT_CONNECTION_TO_TARGET_LOST for %s\n", g.ToString());
			}
			else if (p->data[0]==ID_NAT_ALREADY_IN_PROGRESS)
			{
				RakNet::RakNetGUID g;
				RakNet::BitStream b(p->data, p->length, false);
				b.IgnoreBits(8); // Ignore the ID_...
				b.Read(g);
				LOGI("ID_NAT_ALREADY_IN_PROGRESS for %s\n", g.ToString());
			}
			else if (p->data[0]==ID_NAT_PUNCHTHROUGH_FAILED)
			{
				LOGI("ID_NAT_PUNCHTHROUGH_FAILED for %s\n", p->guid.ToString());
			}
			else if (p->data[0]==ID_NAT_PUNCHTHROUGH_SUCCEEDED)
			{
				LOGI("ID_NAT_PUNCHTHROUGH_SUCCEEDED for %s. Connecting...\n", p->guid.ToString());
				rakPeer->Connect(p->systemAddress.ToString(false),p->systemAddress.GetPort(),0,0);
			}
			else if (p->data[0]==ID_ALREADY_CONNECTED)
			{
				LOGI("ID_ALREADY_CONNECTED\n");
			}
			else if (p->data[0]==ID_RAKVOICE_CLOSE_CHANNEL)
			{
				LOGI("ID_RAKVOICE_CLOSE_CHANNEL\n");
			}
			else if (p->data[0]==ID_DISCONNECTION_NOTIFICATION)
			{
				LOGI("ID_DISCONNECTION_NOTIFICATION\n");
			}
			else if (p->data[0]==ID_NEW_INCOMING_CONNECTION)
			{
				LOGI("ID_NEW_INCOMING_CONNECTION\n");
			}
			else if(p->data[0]==ID_CONNECTION_LOST)
			{
				LOGI("ID_CONNECTION_LOST 可靠的数据包不能被传递到指定的分组系统");
			}
			else
			{
				LOGI("Unknown packet ID %i\n", p->data[0]);
			}

			rakPeer->DeallocatePacket(p);
			p=rakPeer->Receive();
		}


		Pa_Sleep( 30 );
	}
}

效果如图:


实现文件传输
Win平台下的文件传输Demo直接移植到Android平台便可实现,代码如下:

class CBTransferInterface : public RakNet::FileListTransferCBInterface //传输文件接口
{
public:
	 bool OnFile(OnFileStruct *onFileStruct);
	virtual void OnFileProgress(FileProgressStruct *fps);
	virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs);
};

// Sender progress notification
class TestFileListProgress : public RakNet::FileListProgress //进度通知
{
	virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, RakNet::SystemAddress targetSystem, unsigned short setID);
	virtual void OnFilePushesComplete( RakNet::SystemAddress systemAddress, unsigned short setID );
	virtual void OnSendAborted( RakNet::SystemAddress systemAddress );

};

class FileTransferClient //客户端
{
public:
	int testTransfer(const char* ip,const char* filePath);
	void stopTransfer();
	void waitForStop();
public:
	int transferStatus;
	int isStoped;
};

效果如图:



结束

      至此,在Android平台下已经实现了消息、语音、文件传输等功能,近期将实现视频。


Android RakNet 系列之四 实现消息、语音、文件传输