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平台下已经实现了消息、语音、文件传输等功能,近期将实现视频。

时间: 2024-07-30 20:23:29

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

struts2官方 中文教程 系列七:消息资源文件

介绍 在本教程中,我们将探索使用Struts 2消息资源功能(也称为 resource bundles 资源绑定).消息资源提供了一种简单的方法,可以将文本放在一个视图页面中,通过应用程序,创建表单字段标签,并根据用户的语言环境将文本更改为特定的语言. 贴个本帖的地址,以免被爬:struts2官方 中文教程 系列七:消息资源文件  即 http://www.cnblogs.com/linghaoxinpian/p/6906720.html 下载本章节代码 信息资源属性文件 在Struts2 we

Android RakNet 系列之二 功能介绍

简介 RakNet 已经成功地在Android平台上测试成功.RakNet的文档很多,实现起来很简单,下面对Raknet功能细节进行详细了解. 详情 1.RakNet使用哪些数据结构? 结构文件 描述 DS_BinarySearchTree.h 二叉搜索树,以及AVL平衡二叉搜索树 DS_BPlusTree.h B+树,用于快速查询,删除,和插入 DS_BytePool.h 返回某个大小门限的数据块,减少内存碎片 DS_ByteQueue.h 用于读写字节的队列 DS_Heap.h 堆数据结构体

Android RakNet 系列之一 项目介绍

简介 项目对通讯要求越来越高了,为了满足新的需求开始研究RakNet在Android平台的应用,本篇是第一篇,熟悉RakNet的各个功能和插件. RakNet是一个基于UDP网络传输协议的C++网络库,允许程序员在他们自己的程序中实现高效的网络传输服务.通常情况下用于游戏,但也可以用于其它项目. RakNet致力于网络和网络相关服务的游戏引擎.不仅包含了网络通信,也包括游戏级别复制,补丁升级,NAT穿透,和语音聊天.RakNet可以用于任何的应用,且可以与其他任何使用了RakNet的系统通信,不

Android RakNet 系列之六 源码说明

简介 既然选择Raknet开发,那就深入研究其源码结构,为以后的应用打下基础. 详情 1.文件 文件 描述 _FindFirst 快速查找 AutopatcherPatchContext 自动更新.不停 AutopatcherRepositoryInterface 更新 获取重要的数据接口 Base64Encoder base64编码 BitStream 比特流 流结构 CCRakNetSlidingWindow 观测 CCRakNetUDT   CheckSum 校验 CloudClient

android图像处理系列之四--给图片添加边框(上)

图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法. 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下角,右边,右上角,上边,其中左右上下只需要一个有效长度,就像重写水平进度条一样,只需要一个有效的长度,然后平铺,就达到了最后想要的效果,不错,左右上下边采用的也是这样的思路.也可以将八张图片组合在一起,然后读取整张图片,用代码裁剪,下面会给出相应的代码.下面的代码主要是给出第一种方法,后一种给出代码

android图像处理系列之四-- 给图片添加边框(上)

图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法. 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下角,右边,右上角,上边,其中左右上下只需要一个有效长度,就像重写水平进度条一样,只需要一个有效的长度,然后平铺,就达到了最后想要的效果,不错,左右上下边采用的也是这样的思路.也可以将八张图片组合在一起,然后读取整张图片,用代码裁剪,下面会给出相应的代码.下面的代码主要是给出第一种方法,后一种给出代码

android -- 蓝牙 bluetooth (四)OPP文件传输

在前面android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录下的代码,作为蓝牙最基本的一个功能,这部分的代码在之前的版本中就已经有了,新旧版本代码对比很多类名都是一样的,这一部分新东西不多,写在这里帮助大家梳理下流程吧. 有没有这种感觉,智能手机的普及让我们提高了一点对蓝牙的关注,手机间使用蓝牙互传文件应该是最常用的应用之一,手机与电脑也可以通过蓝牙做同样的

Android RakNet 系列之三 移植Portaudio

简介 PortAudio 是一个跨平台采集和播放音频的开源库,不过尚未支持Android平台,笔者决定让PortAudio支持Android.Android上,处理音频的库是OpenSL ES,从API 9开始支持的技术,通过这个标准,Android已经完全可以在native层采集和播放音频.换句话说,PortAudio支持Android,也就是支持OpenSL ES. 详情 Opensl es penSL ES 是无授权费.跨平台.针对嵌入式系统精心优化的硬件音频加速API.它为嵌入式移动多媒

Android RakNet 系列之五 视频通讯 OpenCV4Android

简介 引入OpenCV4Android的目标是在Raknet框架下解决视频通讯的问题,目前在ubuntu下已成功实现,现在把它引用到Android平台下. OpenCV是一个基于开源发行的跨平台计算机视觉库,可以在 Windows, Android, Maemo,FreeBSD, OpenBSD, iOS,Linux 和Mac OS等平台上运行.它轻量级而且高效--由一系列 C 函数和少量 C++ 类构成,同时提供了Python.Ruby.MATLAB等语言的接口,实现了图像处理和计算机视觉方面