標籤:des android style blog http io ar color os
簡介
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 notificationclass 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 系列之四 實現訊息、語音、檔案傳輸