基于wince的网络音视频通信(简单易明版)

本文来自http://blog.csdn.net/hellogv/
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 11"> <meta name="Originator" content="Microsoft Word 11"> <link rel="File-List" href="file:///G:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"> <link rel="Edit-Time-Data" href="file:///G:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_editdata.mso"> <!--[if !mso]> <style> v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} </style> <![endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning/> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL/> <w:BalanceSingleByteDoubleByteWidth/> <w:DoNotLeaveBackslashAlone/> <w:ULTrailSpace/> <w:DoNotExpandShiftReturn/> <w:AdjustLineHeightInTable/> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:UseFELayout/> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><style> <!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"\@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; line-height:125%; mso-pagination:none; font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体; mso-font-kerning:1.0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;} </style> <![endif]--> <!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="[email protected]@[email protected]@[email protected]@[email protected]@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter"/> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0"/> <v:f eqn="sum @0 1 0"/> <v:f eqn="sum 0 0 @1"/> <v:f eqn="prod @2 1 2"/> <v:f eqn="prod @3 21600 pixelWidth"/> <v:f eqn="prod @3 21600 pixelHeight"/> <v:f eqn="sum @0 0 1"/> <v:f eqn="prod @6 1 2"/> <v:f eqn="prod @7 21600 pixelWidth"/> <v:f eqn="sum @8 21600 0"/> <v:f eqn="prod @7 21600 pixelHeight"/> <v:f eqn="sum @10 21600 0"/> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/> <o:lock v:ext="edit" aspectratio="t"/> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:392.25pt; height:314.25pt'> <v:imagedata src="file:///G:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image001.jpg" o:title="IMAGE_00001"/> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->


由于这个学期的期末将至,因此期末大作业也要开始做了。这次做的是N个月前就做过的网络音视频通信,不过这次做了不少改进。
在这里,先要感谢一下 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大小的数据包,分别发送了。
源代码可以到这里下载(2008.12.01才可以下载):
http://download.csdn.net/user/hellogv
网络音视频通信

下面说说关键的源代码:

以下是WINCE部分的代码
  1. #pragmaonce
  2. #include"winsock2.h"
  3. //RTP支持
  4. #include"..\UDP\UDP.h"
  5. //音频支持
  6. #include"WaveIn.h"
  7. #include"WaveOut.h"
  8. //G726支持
  9. #include"g726.h"
  10. //摄像头支持
  11. #include".\video\\zc030xlib.h"
  12. #defineVideo_Width320//视频宽度
  13. #defineVideo_Height240//视频长度
  14. #defineAudioData_Size960//每块音频数据包的大小
  15. #defineCompr_AudioData_Size120//压缩后音频块的大小
  16. //音频输入输出变量
  17. CWaveIn*g_pIn;
  18. CWaveOut*g_pOut;
  19. charpin[AudioData_Size],pout[Compr_AudioData_Size];
  20. charwaveout[AudioData_Size];
  21. //摄像头输入变量
  22. DWORDdwSize;
  23. DWORDdwJpg;
  24. DWORDdwRtnSize[2];/*0-forbmp,1-forjpeg*/
  25. LPBYTElpFrameBuffer;
  26. LPBYTElpJpgBuffer;
  27. //控制变量
  28. boolisCameraEnabled;
  29. //UDP
  30. CUDP_CEm_CEUdp;
  31. classAVClass
  32. {
  33. public:
  34. //=====================================================================
  35. //语法格式:voidInitAV(CWnd*p)
  36. //实现功能:初始化音频和视频,用于录音、播放音频,以及播放视频
  37. //参数:p为窗口类指针
  38. //返回值:无
  39. //=====================================================================
  40. voidInitAV(CWnd*p,intlocal_port,CStringremote_ip,intremote_port)
  41. {
  42. //-----------------------初始化UDP-----------------------//
  43. m_CEUdp.m_OnUdpRecv=OnUdpCERecv;
  44. DWORDnResult=m_CEUdp.Open(p,local_port,remote_ip,remote_port);
  45. if(nResult<=0)
  46. {
  47. AfxMessageBox(_T("打开端口失败"));
  48. return;
  49. }
  50. //------------------------控制变量-----------------------//
  51. isCameraEnabled=false;
  52. //-------------------------视频--------------------------//
  53. inti=capInitCamera();
  54. dwSize=320*240*3;
  55. dwJpg=40960;
  56. lpFrameBuffer=(LPBYTE)malloc(dwSize);
  57. lpJpgBuffer=(LPBYTE)malloc(dwJpg);
  58. if(i<=0)
  59. {
  60. //::MessageBox(NULL,L"Initcameraerror",L"Notice",0);
  61. gotovideo_error;//出错,释放空间
  62. }
  63. if(0!=capSetVideoFormat(0,VIDEO_PALETTE_RGB24,VIDEO_SIZE_SIF))
  64. {
  65. //::MessageBox(NULL,L"SetVideoFormaterror",L"Notice",0);
  66. gotovideo_error;//出错,释放空间
  67. }
  68. if(capStartCamera(0)!=0)
  69. {
  70. //::MessageBox(NULL,L"StartCameraerror",L"Notice",0);
  71. capStopCamera(0);
  72. gotovideo_error;//出错,释放空间
  73. }
  74. //没出错,进行视频(控制状态)、音频设置
  75. isCameraEnabled=true;
  76. gotoaudio;
  77. video_error:
  78. free(lpFrameBuffer);
  79. lpFrameBuffer=NULL;
  80. free(lpJpgBuffer);
  81. lpJpgBuffer=NULL;
  82. //-------------------------音频--------------------------//
  83. audio:
  84. g_pOut=newCWaveOut();
  85. g_pIn=newCWaveIn();
  86. g_pOut->StartPlay();
  87. g_pIn->StartRec(OnRecCapAndSend,(DWORD)p);
  88. }
  89. //=====================================================================
  90. //语法格式:voidFreeAV()
  91. //实现功能:释放音频、视频
  92. //参数:无
  93. //返回值:无
  94. //=====================================================================
  95. voidFreeAV()
  96. {
  97. //-------------------------视频--------------------------//
  98. if(isCameraEnabled)
  99. capStopCamera(0);
  100. //-------------------------音频--------------------------//
  101. g_pOut->StopPlay();
  102. g_pIn->StopRec();
  103. deleteg_pOut;
  104. deleteg_pIn;
  105. //------------------------UDP------------------------//
  106. m_CEUdp.Close();
  107. }
  108. //=====================================================================
  109. //语法格式:voidRecAndPlay(WPARAMwParam,LPARAMlParam)
  110. //实现功能:接收网络传来的音频,以及播放
  111. //参数:wParam,表示数据;lParam,表示数据长度
  112. //返回值:无
  113. //=====================================================================
  114. staticvoidCALLBACKOnUdpCERecv(CWnd*pWnd,char*buf,intnLen,sockaddr*addr)
  115. {
  116. g726_Decode(buf,(unsignedchar*)waveout);
  117. g_pOut->Play(waveout,AudioData_Size);
  118. }
  119. //=====================================================================
  120. //语法格式:staticvoidOnRecCapAndSend(char*data,intlength,DWORDuserdata)
  121. //实现功能:录音,摄像并且发送
  122. //参数:data表示数据,length表示数据长度,userdata暂时没用
  123. //返回值:无
  124. //=====================================f================================
  125. staticvoidOnRecCapAndSend(char*data,intlength,DWORDuserdata)
  126. {
  127. //-------------------------音频--------------------------//
  128. memcpy(pin,g_pIn->buffer,AudioData_Size);
  129. g726_Encode((unsignedchar*)pin,pout);
  130. m_CEUdp.SendData(pout,Compr_AudioData_Size);
  131. //-------------------------视频--------------------------//
  132. if(isCameraEnabled==false)//如果程序不能用摄像头
  133. return;
  134. Sleep(15);
  135. intindex=0;
  136. memset(lpFrameBuffer,0,dwSize);
  137. memset(lpJpgBuffer,0,dwJpg);
  138. dwRtnSize[0]=dwRtnSize[1]=0;
  139. if(capGetPicture(index,lpFrameBuffer,dwSize,lpJpgBuffer,dwJpg,dwRtnSize)==0)
  140. {
  141. ///m_CEUdp.SendData((constchar*)lpJpgBuffer,dwRtnSize[1]);
  142. chartmp[1440];
  143. inttmp_i=0;
  144. for(inti=0;i<dwRtnSize[1];i++)
  145. {
  146. tmp[tmp_i]=lpJpgBuffer[i];
  147. tmp_i++;
  148. if(tmp_i==1440)
  149. {
  150. m_CEUdp.SendData(tmp,1440);
  151. tmp_i=0;
  152. }
  153. elseif(i==dwRtnSize[1]-1)
  154. {
  155. m_CEUdp.SendData(tmp,dwRtnSize[1]-(dwRtnSize[1]/1440)*1440);
  156. tmp_i=0;
  157. }
  158. }
  159. }
  160. }
  161. };
以下是PC部分的关键代码
  1. #pragmaonce
  2. #include"winsock2.h"
  3. //UDP支持
  4. #include"..\UDP\UDP.h"
  5. //音频支持
  6. #include"WaveIn.h"
  7. #include"waveout.h"
  8. //G726支持
  9. #include"g726.h"
  10. //视频支持
  11. #include"Gdiplus.h"
  12. usingnamespaceGdiplus;
  13. #defineVideoData_Size1440//每块视频数据包的大小
  14. #defineVideo_Width320//视频宽度
  15. #defineVideo_Height240//视频长度
  16. #defineAudioData_Size960//每块音频数据包的大小
  17. #defineCompr_AudioData_Size120//压缩后音频块的大小
  18. //音频输入输出变量
  19. CWaveIn*g_pIn;
  20. CWaveOut*g_pOut;
  21. charpin[AudioData_Size],pout[Compr_AudioData_Size];
  22. charwave_data[AudioData_Size];
  23. //UDP变量
  24. CUDP_CEm_CEUdp;
  25. //视频输入变量
  26. GdiplusStartupInputm_gdiPlusInPut;
  27. ULONG_PTRm_gdiPlusToken;
  28. charvideo_data[Video_Width*Video_Height];
  29. intindex;//视频数据当前索引
  30. classAVClass
  31. {
  32. private:
  33. public:
  34. //=====================================================================
  35. //语法格式:voidInitAV(CWnd*p)
  36. //实现功能:初始化音频和视频,用于录音、播放音频,以及播放视频
  37. //参数:p为窗口类指针
  38. //返回值:无
  39. //=====================================================================
  40. voidInitAV(CWnd*p,intlocal_port,CStringremote_ip,intremote_port)
  41. {
  42. //-------------------------UDP连接--------------------------//
  43. m_CEUdp.m_OnUdpRecv=OnUdpCERecv;
  44. DWORDnResult=m_CEUdp.Open(p,local_port,remote_ip,remote_port);
  45. if(nResult<=0)
  46. {
  47. AfxMessageBox(_T("打开端口失败"));
  48. return;
  49. }
  50. //-------------------------音频--------------------------//
  51. g_pOut=newCWaveOut();
  52. g_pIn=newCWaveIn();
  53. g_pOut->StartPlay();
  54. g_pIn->StartRec(OnRecording,(DWORD)p);
  55. //-------------------------视频--------------------------//
  56. GdiplusStartup(&m_gdiPlusToken,&m_gdiPlusInPut,NULL);//初始化GDI+
  57. memset(video_data,0,Video_Width*Video_Height);
  58. index=0;
  59. }
  60. //=====================================================================
  61. //语法格式:voidFreeAV()
  62. //实现功能:释放音频、视频
  63. //参数:无
  64. //返回值:无
  65. //=====================================================================
  66. voidFreeAV()
  67. {
  68. //-------------------------音频--------------------------//
  69. g_pOut->StopPlay();
  70. g_pIn->StopRec();
  71. deleteg_pOut;
  72. deleteg_pIn;
  73. //-------------------------视频--------------------------//
  74. GdiplusShutdown(m_gdiPlusToken);//销毁GDI+
  75. //------------------------UDP--------------------------//
  76. m_CEUdp.Close();
  77. }
  78. //=====================================================================
  79. //语法格式:voidRecAndPlay(WPARAMwParam,LPARAMlParam,HWNDhwnd)
  80. //实现功能:接收网络传来的音频,以及播放
  81. //参数:wParam,表示数据;lParam,表示数据长度;hwnd,表示显示视频的窗口句柄
  82. //返回值:无
  83. //=====================================================================
  84. staticvoidCALLBACKOnUdpCERecv(CWnd*pWnd,char*buf,intnLen,sockaddr*addr)
  85. {
  86. /*测试收到的数据大小
  87. CStringtmp;
  88. tmp.Format(L"%d",nLen);
  89. MessageBox(0,tmp,0,0);
  90. return;*/
  91. //-------------------------如果是音频数据--------------------------//
  92. if(nLen==Compr_AudioData_Size)
  93. {
  94. g726_Decode(buf,(unsignedchar*)wave_data);
  95. g_pOut->Play(wave_data,AudioData_Size);
  96. return;
  97. }
  98. //-------------------------如果是视频数据--------------------------//
  99. if(nLen==VideoData_Size)//完整的视频数据块
  100. {
  101. for(inti=0;i<nLen;i++)
  102. {
  103. video_data[index]=buf[i];
  104. index++;
  105. }
  106. return;
  107. }
  108. //视频数据块的最后一块
  109. for(inti=0;i<nLen;i++)
  110. {
  111. video_data[index]=buf[i];
  112. index++;
  113. }
  114. //如果JPEG图像特别大,则肯定是出错,则抛弃
  115. if(index>Video_Width*Video_Height)
  116. {
  117. //MessageBox(0,"缓冲区出错","错误信息",0);
  118. return;
  119. }
  120. try{
  121. IPicture*pPic;
  122. IStream*pStm;
  123. //分配全局存储空间
  124. HGLOBALhGlobal=GlobalAlloc(GMEM_MOVEABLE,index);
  125. LPVOIDpvData=NULL;
  126. //锁定分配内存块
  127. pvData=GlobalLock(hGlobal);
  128. //复制数据包video_data到pvData
  129. memcpy(pvData,video_data,index);
  130. GlobalUnlock(hGlobal);
  131. CreateStreamOnHGlobal(hGlobal,TRUE,&pStm);
  132. ULARGE_INTEGERpSeek;
  133. LARGE_INTEGERdlibMove={0};
  134. pStm->Seek(dlibMove,STREAM_SEEK_SET,&pSeek);
  135. //Sleep(15);
  136. //装入图形文件
  137. if(FAILED(OleLoadPicture(pStm,index,TRUE,IID_IPicture,(LPVOID*)&pPic)))
  138. {//附:如果video_data这个数组包含的图像有错,则OleLoadPicture容易产生读写内存错误
  139. //pPic->Release();
  140. //pStm->Release();
  141. return;
  142. }
  143. Imageimg(pStm,0);
  144. GraphicsmGraphics(GetDC(pWnd->m_hWnd));
  145. mGraphics.DrawImage(&img,0,0,Video_Width,Video_Height);
  146. img.~Image();//会出错
  147. mGraphics.~Graphics();
  148. pPic->Release();
  149. pStm->Release();
  150. }
  151. catch(CException*e)
  152. {}
  153. memset(video_data,0,Video_Width*Video_Height);
  154. index=0;
  155. }
  156. //=====================================================================
  157. //语法格式:staticvoidOnRecording(char*data,intlength,DWORDuserdata)
  158. //实现功能:释放音频
  159. //参数:data表示数据,length表示数据长度,userdata暂时没用
  160. //返回值:无
  161. //=====================================================================
  162. staticvoidOnRecording(char*data,intlength,DWORDuserdata)
  163. {
  164. memcpy(pin,g_pIn->buffer,AudioData_Size);
  165. g726_Encode((unsignedchar*)pin,pout);
  166. m_CEUdp.SendData(pout,Compr_AudioData_Size);
  167. }
  168. };