基于wince的网络音视频通信(简单易明版)
由于这个学期的期末将至,因此期末大作业也要开始做了。这次做的是N个月前就做过的网络音视频通信,不过这次做了不少改进。
在这里,先要感谢一下 MacintoshM 大侠,他的帖子给我提供了最原始的源代码,我现在的代码也是在他代码的基础上做出修改的。
OK,现在进入主题,先来介绍一下这个东东:
http://download.csdn.net/user/hellogv
网络音视频通信
下面说说关键的源代码:
以下是WINCE部分的代码
以下是PC部分的关键代码
在这里,先要感谢一下 MacintoshM 大侠,他的帖子给我提供了最原始的源代码,我现在的代码也是在他代码的基础上做出修改的。
OK,现在进入主题,先来介绍一下这个东东:
- 下位机为 mini2440 ,其操作系统是WINCE 5.0,需要在下位机上插入USB摄像头(中星微301,驱动为15分钟限制版),以及插入麦克风耳机;
- 上位机为普通的PC,需要插入麦克风耳机;
- 上位机和下位机的开发环境为Visual Studio 2005。
- 网络视频传输,首先把mini2440与USB摄像头连接在一起,通过USB摄像头捕捉图像(320 x 240,15fps),然后通过UDP协议发送至PC端,在PC屏幕上以15fps的速度显示。
- 网络音频传输,首先在mini2440和PC上都插入麦克风耳机,两者都同时录音,并以G.726编码,然后把压缩包通过UDP协议发送至对方,而在录音的同时,也对来自对方的压缩包用G.726解码,然后再在耳机播放音频。
- 把以前的RTP改为UDP,实践证明,在这个系统中,用UDP的效率会比RTP好,毕竟不需要用到RTP的流量监测等高级功能,仅仅能通信就行了;
- 以前版本基于100M网卡,现在支持10M网卡了,在100M网卡的机器上,可以直接把JPEG图片发过去,但是,在10M的网卡是,每次数据包最大为1440比特,因此要兼容10M网卡,就需要把图像分割成多个1440大小的数据包,分别发送了。
http://download.csdn.net/user/hellogv
网络音视频通信
下面说说关键的源代码:
以下是WINCE部分的代码
- #pragma once
- #include "winsock2.h"
- //RTP支持
- #include "..\UDP\UDP.h"
- //音频支持
- #include "WaveIn.h"
- #include "WaveOut.h"
- //G726支持
- #include "g726.h"
- //摄像头支持
- #include ".\video\\zc030xlib.h"
- #define Video_Width 320 //视频宽度
- #define Video_Height 240 //视频长度
- #define AudioData_Size 960 //每块音频数据包的大小
- #define Compr_AudioData_Size 120 //压缩后音频块的大小
- //音频输入输出变量
- CWaveIn *g_pIn;
- CWaveOut *g_pOut;
- char pin[AudioData_Size],pout[Compr_AudioData_Size];
- char waveout[AudioData_Size];
- //摄像头输入变量
- DWORD dwSize;
- DWORD dwJpg;
- DWORD dwRtnSize[2];/* 0 - for bmp, 1 - for jpeg */
- LPBYTE lpFrameBuffer;
- LPBYTE lpJpgBuffer ;
- //控制变量
- bool isCameraEnabled;
- //UDP
- CUDP_CE m_CEUdp;
- class AVClass
- {
- public:
- //=====================================================================
- // 语法格式: void InitAV(CWnd * p)
- // 实现功能: 初始化音频和视频,用于录音、播放音频,以及播放视频
- // 参数: p为窗口类指针
- // 返回值: 无
- //=====================================================================
- void InitAV(CWnd * p,int local_port,CString remote_ip,int remote_port)
- {
- //-----------------------初始化UDP-----------------------//
- m_CEUdp.m_OnUdpRecv = OnUdpCERecv;
- DWORD nResult = m_CEUdp.Open(p,local_port,remote_ip,remote_port);
- if (nResult <=0)
- {
- AfxMessageBox(_T("打开端口失败"));
- return;
- }
- //------------------------控制变量-----------------------//
- isCameraEnabled=false;
- //-------------------------视频--------------------------//
- int i = capInitCamera();
- dwSize = 320 * 240 * 3;
- dwJpg = 40960;
- lpFrameBuffer = (LPBYTE) malloc (dwSize);
- lpJpgBuffer = (LPBYTE) malloc (dwJpg);
- if(i<=0)
- {
- //::MessageBox(NULL, L"Init camera error ", L"Notice", 0);
- goto video_error;//出错,释放空间
- }
- if (0 != capSetVideoFormat(0, VIDEO_PALETTE_RGB24, VIDEO_SIZE_SIF))
- {
- //::MessageBox(NULL, L"SetVideoFormat error ", L"Notice", 0);
- goto video_error;//出错,释放空间
- }
- if (capStartCamera(0) != 0)
- {
- //::MessageBox(NULL, L"StartCamera error ", L"Notice", 0);
- capStopCamera(0);
- goto video_error;//出错,释放空间
- }
- //没出错,进行视频(控制状态)、音频设置
- isCameraEnabled=true;
- goto audio;
- video_error:
- free (lpFrameBuffer);
- lpFrameBuffer = NULL;
- free (lpJpgBuffer);
- lpJpgBuffer = NULL;
- //-------------------------音频--------------------------//
- audio:
- g_pOut = new CWaveOut();
- g_pIn = new CWaveIn();
- g_pOut->StartPlay();
- g_pIn->StartRec(OnRecCapAndSend,(DWORD)p);
- }
- //=====================================================================
- // 语法格式: void FreeAV()
- // 实现功能: 释放音频、视频
- // 参数: 无
- // 返回值: 无
- //=====================================================================
- void FreeAV()
- {
- //-------------------------视频--------------------------//
- if(isCameraEnabled)
- capStopCamera(0);
- //-------------------------音频--------------------------//
- g_pOut->StopPlay();
- g_pIn->StopRec();
- delete g_pOut;
- delete g_pIn;
- //------------------------UDP------------------------//
- m_CEUdp.Close();
- }
- //=====================================================================
- // 语法格式: void RecAndPlay(WPARAM wParam,LPARAM lParam)
- // 实现功能: 接收网络传来的音频,以及播放
- // 参数: wParam,表示数据;lParam,表示数据长度
- // 返回值: 无
- //=====================================================================
- static void CALLBACK OnUdpCERecv(CWnd * pWnd,char* buf,int nLen,sockaddr * addr)
- {
- g726_Decode(buf,(unsigned char*)waveout);
- g_pOut->Play(waveout,AudioData_Size);
- }
- //=====================================================================
- // 语法格式: static void OnRecCapAndSend(char *data,int length,DWORD userdata)
- // 实现功能: 录音,摄像并且发送
- // 参数: data表示数据,length表示数据长度,userdata暂时没用
- // 返回值: 无
- //=====================================f================================
- static void OnRecCapAndSend(char *data,int length,DWORD userdata)
- {
- //-------------------------音频--------------------------//
- memcpy(pin,g_pIn->buffer,AudioData_Size);
- g726_Encode((unsigned char*)pin,pout);
- m_CEUdp.SendData(pout,Compr_AudioData_Size);
- //-------------------------视频--------------------------//
- if(isCameraEnabled==false)//如果程序不能用摄像头
- return;
- Sleep(15);
- int index=0;
- memset(lpFrameBuffer, 0, dwSize);
- memset(lpJpgBuffer, 0, dwJpg);
- dwRtnSize[0] = dwRtnSize[1] = 0;
- if (capGetPicture(index, lpFrameBuffer, dwSize, lpJpgBuffer, dwJpg, dwRtnSize) == 0)
- {
- /// m_CEUdp.SendData((const char *)lpJpgBuffer,dwRtnSize[1]);
- char tmp[1440];
- int tmp_i=0;
- for(int i=0;i<dwRtnSize[1];i++)
- {
- tmp[tmp_i]=lpJpgBuffer[i];
- tmp_i++;
- if(tmp_i==1440)
- {
- m_CEUdp.SendData(tmp,1440);
- tmp_i=0;
- }
- else if(i==dwRtnSize[1]-1)
- {
- m_CEUdp.SendData(tmp,dwRtnSize[1]-(dwRtnSize[1]/1440)*1440);
- tmp_i=0;
- }
- }
- }
- }
- };
- #pragma once
- #include "winsock2.h"
- //UDP支持
- #include "..\UDP\UDP.h"
- //音频支持
- #include "WaveIn.h"
- #include "waveout.h"
- //G726支持
- #include "g726.h"
- //视频支持
- #include "Gdiplus.h"
- using namespace Gdiplus;
- #define VideoData_Size 1440 //每块视频数据包的大小
- #define Video_Width 320 //视频宽度
- #define Video_Height 240 //视频长度
- #define AudioData_Size 960 //每块音频数据包的大小
- #define Compr_AudioData_Size 120 //压缩后音频块的大小
- //音频输入输出变量
- CWaveIn *g_pIn;
- CWaveOut *g_pOut;
- char pin[AudioData_Size],pout[Compr_AudioData_Size];
- char wave_data[AudioData_Size];
- //UDP变量
- CUDP_CE m_CEUdp;
- //视频输入变量
- GdiplusStartupInput m_gdiPlusInPut;
- ULONG_PTR m_gdiPlusToken;
- char video_data[Video_Width*Video_Height];
- int index;//视频数据当前索引
- class AVClass
- {
- private:
- public:
- //=====================================================================
- // 语法格式: void InitAV(CWnd * p)
- // 实现功能: 初始化音频和视频,用于录音、播放音频,以及播放视频
- // 参数: p为窗口类指针
- // 返回值: 无
- //=====================================================================
- void InitAV(CWnd * p,int local_port,CString remote_ip,int remote_port)
- {
- //-------------------------UDP连接--------------------------//
- m_CEUdp.m_OnUdpRecv = OnUdpCERecv;
- DWORD nResult = m_CEUdp.Open(p,local_port,remote_ip,remote_port);
- if (nResult <=0)
- {
- AfxMessageBox(_T("打开端口失败"));
- return;
- }
- //-------------------------音频--------------------------//
- g_pOut = new CWaveOut();
- g_pIn = new CWaveIn();
- g_pOut->StartPlay();
- g_pIn->StartRec(OnRecording,(DWORD)p);
- //-------------------------视频--------------------------//
- GdiplusStartup( &m_gdiPlusToken, &m_gdiPlusInPut, NULL ); //初始化GDI+
- memset(video_data,0,Video_Width*Video_Height);
- index=0;
- }
- //=====================================================================
- // 语法格式: void FreeAV()
- // 实现功能: 释放音频、视频
- // 参数: 无
- // 返回值: 无
- //=====================================================================
- void FreeAV()
- {
- //-------------------------音频--------------------------//
- g_pOut->StopPlay();
- g_pIn->StopRec();
- delete g_pOut;
- delete g_pIn;
- //-------------------------视频--------------------------//
- GdiplusShutdown(m_gdiPlusToken); //销毁GDI+
- //------------------------UDP--------------------------//
- m_CEUdp.Close();
- }
- //=====================================================================
- // 语法格式: void RecAndPlay(WPARAM wParam,LPARAM lParam,HWND hwnd)
- // 实现功能: 接收网络传来的音频,以及播放
- // 参数: wParam,表示数据;lParam,表示数据长度;hwnd,表示显示视频的窗口句柄
- // 返回值: 无
- //=====================================================================
- static void CALLBACK OnUdpCERecv(CWnd *pWnd,char* buf,int nLen,sockaddr * addr)
- {
- /*测试收到的数据大小
- CString tmp;
- tmp.Format(L"%d",nLen);
- MessageBox(0,tmp,0,0);
- return;*/
- //-------------------------如果是音频数据--------------------------//
- if(nLen==Compr_AudioData_Size)
- {
- g726_Decode(buf,(unsigned char*)wave_data);
- g_pOut->Play(wave_data,AudioData_Size);
- return;
- }
- //-------------------------如果是视频数据--------------------------//
- if(nLen==VideoData_Size)//完整的视频数据块
- {
- for(int i=0;i<nLen;i++)
- {
- video_data[index]=buf[i];
- index++;
- }
- return;
- }
- //视频数据块的最后一块
- for(int i=0;i<nLen;i++)
- {
- video_data[index]=buf[i];
- index++;
- }
- //如果JPEG图像特别大,则肯定是出错,则抛弃
- if(index>Video_Width*Video_Height)
- {
- //MessageBox(0,"缓冲区出错","错误信息",0);
- return;
- }
- try{
- IPicture *pPic;
- IStream *pStm ;
- //分配全局存储空间
- HGLOBAL hGlobal=GlobalAlloc(GMEM_MOVEABLE,index);
- LPVOID pvData=NULL ;
- //锁定分配内存块
- pvData=GlobalLock(hGlobal);
- //复制数据包video_data到pvData
- memcpy(pvData,video_data,index);
- GlobalUnlock(hGlobal);
- CreateStreamOnHGlobal(hGlobal,TRUE,&pStm);
- ULARGE_INTEGER pSeek;
- LARGE_INTEGER dlibMove ={ 0 } ;
- pStm->Seek(dlibMove,STREAM_SEEK_SET ,&pSeek);
- // Sleep(15);
- //装入图形文件
- if(FAILED(OleLoadPicture(pStm,index,TRUE,IID_IPicture,(LPVOID*)&pPic)))
- {//附:如果video_data这个数组包含的图像有错,则OleLoadPicture 容易产生读写内存错误
- // pPic->Release();
- // pStm->Release();
- return ;
- }
- Image img(pStm,0);
- Graphics mGraphics(GetDC(pWnd->m_hWnd));
- mGraphics.DrawImage(&img, 0, 0, Video_Width, Video_Height);
- img.~Image();//会出错
- mGraphics.~Graphics();
- pPic->Release();
- pStm->Release();
- }
- catch(CException * e)
- {}
- memset(video_data,0,Video_Width*Video_Height);
- index=0;
- }
- //=====================================================================
- // 语法格式: static void OnRecording(char *data,int length,DWORD userdata)
- // 实现功能: 释放音频
- // 参数: data表示数据,length表示数据长度,userdata暂时没用
- // 返回值: 无
- //=====================================================================
- static void OnRecording(char *data,int length,DWORD userdata)
- {
- memcpy(pin,g_pIn->buffer,AudioData_Size);
- g726_Encode((unsigned char*)pin,pout);
- m_CEUdp.SendData(pout,Compr_AudioData_Size);
- }
- };