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

由于这个学期的期末将至,因此期末大作业也要开始做了。这次做的是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. #pragma once
  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. #define Video_Width 320 //视频宽度
  13. #define Video_Height 240 //视频长度
  14. #define AudioData_Size 960  //每块音频数据包的大小
  15. #define Compr_AudioData_Size 120  //压缩后音频块的大小
  16. //音频输入输出变量
  17. CWaveIn *g_pIn;
  18. CWaveOut *g_pOut;   
  19. char pin[AudioData_Size],pout[Compr_AudioData_Size];
  20. char waveout[AudioData_Size];
  21. //摄像头输入变量
  22. DWORD dwSize;
  23. DWORD dwJpg;
  24. DWORD dwRtnSize[2];/* 0 - for bmp, 1 - for jpeg */
  25. LPBYTE lpFrameBuffer;
  26. LPBYTE lpJpgBuffer ;
  27. //控制变量
  28. bool isCameraEnabled;
  29. //UDP
  30. CUDP_CE m_CEUdp;
  31. class AVClass
  32. {
  33. public:
  34.     //=====================================================================
  35.     //  语法格式:   void InitAV(CWnd * p)
  36.     //  实现功能:   初始化音频和视频,用于录音、播放音频,以及播放视频
  37.     //  参数:     p为窗口类指针
  38.     //  返回值:    无
  39.     //=====================================================================
  40.     void InitAV(CWnd * p,int local_port,CString remote_ip,int remote_port)
  41.     {
  42.        //-----------------------初始化UDP-----------------------//
  43.        m_CEUdp.m_OnUdpRecv = OnUdpCERecv;
  44.        DWORD nResult = 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.         int i = 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"Init camera error ", L"Notice", 0);
  61.             goto video_error;//出错,释放空间
  62.         }
  63.         if (0 != capSetVideoFormat(0, VIDEO_PALETTE_RGB24, VIDEO_SIZE_SIF))
  64.         {
  65.             //::MessageBox(NULL, L"SetVideoFormat error ", L"Notice", 0);
  66.             goto video_error;//出错,释放空间
  67.         }
  68.         if (capStartCamera(0) != 0) 
  69.         {
  70.             //::MessageBox(NULL, L"StartCamera error ", L"Notice", 0);
  71.             capStopCamera(0);
  72.             goto video_error;//出错,释放空间
  73.         }
  74.         //没出错,进行视频(控制状态)、音频设置
  75.         isCameraEnabled=true;
  76.         goto audio;
  77. video_error:    
  78.         free (lpFrameBuffer);
  79.         lpFrameBuffer = NULL;
  80.         free (lpJpgBuffer);
  81.         lpJpgBuffer = NULL;
  82.         //-------------------------音频--------------------------//
  83. audio:  
  84.         g_pOut = new CWaveOut();
  85.         g_pIn = new CWaveIn();
  86.         g_pOut->StartPlay();
  87.         g_pIn->StartRec(OnRecCapAndSend,(DWORD)p);
  88.         
  89.     }
  90.     //=====================================================================
  91.     //  语法格式:   void FreeAV()
  92.     //  实现功能:   释放音频、视频
  93.     //  参数:     无
  94.     //  返回值:    无
  95.     //=====================================================================
  96.     void FreeAV()
  97.     {
  98.         //-------------------------视频--------------------------//
  99.         if(isCameraEnabled)
  100.             capStopCamera(0);
  101.         //-------------------------音频--------------------------//
  102.         g_pOut->StopPlay();
  103.         g_pIn->StopRec();
  104.         delete g_pOut;
  105.         delete g_pIn;
  106.         //------------------------UDP------------------------//
  107.         m_CEUdp.Close();
  108.     }
  109.     //=====================================================================
  110.     //  语法格式:   void RecAndPlay(WPARAM wParam,LPARAM lParam)
  111.     //  实现功能:   接收网络传来的音频,以及播放
  112.     //  参数:     wParam,表示数据;lParam,表示数据长度
  113.     //  返回值:    无
  114.     //=====================================================================
  115.     static void CALLBACK OnUdpCERecv(CWnd * pWnd,char* buf,int nLen,sockaddr * addr)
  116.     {
  117.         g726_Decode(buf,(unsigned char*)waveout);
  118.         
  119.         g_pOut->Play(waveout,AudioData_Size);
  120.     }
  121.     //=====================================================================
  122.     //  语法格式:   static void OnRecCapAndSend(char *data,int length,DWORD userdata)
  123.     //  实现功能:   录音,摄像并且发送
  124.     //  参数:     data表示数据,length表示数据长度,userdata暂时没用
  125.     //  返回值:    无
  126.     //=====================================f================================
  127.     static void OnRecCapAndSend(char *data,int length,DWORD userdata)
  128.     {
  129.         //-------------------------音频--------------------------//
  130.         memcpy(pin,g_pIn->buffer,AudioData_Size);   
  131.         g726_Encode((unsigned char*)pin,pout);
  132.         m_CEUdp.SendData(pout,Compr_AudioData_Size);
  133.         //-------------------------视频--------------------------//
  134.         if(isCameraEnabled==false)//如果程序不能用摄像头
  135.             return;
  136.         Sleep(15);
  137.         int index=0;
  138.         memset(lpFrameBuffer, 0, dwSize);
  139.         memset(lpJpgBuffer, 0, dwJpg);
  140.         dwRtnSize[0] = dwRtnSize[1] = 0;
  141.         if (capGetPicture(index, lpFrameBuffer, dwSize, lpJpgBuffer, dwJpg, dwRtnSize) == 0)
  142.         {
  143.             /// m_CEUdp.SendData((const char *)lpJpgBuffer,dwRtnSize[1]);
  144.             char tmp[1440];
  145.             int tmp_i=0;
  146.             for(int i=0;i<dwRtnSize[1];i++)
  147.             {
  148.                 tmp[tmp_i]=lpJpgBuffer[i];
  149.                 tmp_i++;
  150.                 if(tmp_i==1440)
  151.                 {
  152.                     m_CEUdp.SendData(tmp,1440);
  153.                     tmp_i=0;
  154.                     
  155.                 }
  156.                 else if(i==dwRtnSize[1]-1)
  157.                 {
  158.     m_CEUdp.SendData(tmp,dwRtnSize[1]-(dwRtnSize[1]/1440)*1440);
  159.                     tmp_i=0;
  160.                 }
  161.             }
  162.         }
  163.     }
  164. };
以下是PC部分的关键代码
  1. #pragma once
  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. using namespace Gdiplus; 
  13. #define VideoData_Size 1440 //每块视频数据包的大小
  14. #define Video_Width 320 //视频宽度
  15. #define Video_Height 240 //视频长度
  16. #define AudioData_Size 960  //每块音频数据包的大小
  17. #define Compr_AudioData_Size 120  //压缩后音频块的大小
  18. //音频输入输出变量
  19. CWaveIn *g_pIn;
  20. CWaveOut *g_pOut;   
  21. char pin[AudioData_Size],pout[Compr_AudioData_Size];
  22. char wave_data[AudioData_Size];
  23. //UDP变量
  24. CUDP_CE m_CEUdp;
  25. //视频输入变量
  26. GdiplusStartupInput m_gdiPlusInPut;
  27. ULONG_PTR m_gdiPlusToken;
  28. char video_data[Video_Width*Video_Height];
  29. int index;//视频数据当前索引
  30. class AVClass
  31. {
  32. private:
  33.     
  34. public:
  35.   //=====================================================================
  36.     //  语法格式:   void InitAV(CWnd * p)
  37.     //  实现功能:   初始化音频和视频,用于录音、播放音频,以及播放视频
  38.     //  参数:     p为窗口类指针
  39.     //  返回值:    无
  40.     //=====================================================================
  41.     void InitAV(CWnd * p,int local_port,CString remote_ip,int remote_port)
  42.     {
  43.         //-------------------------UDP连接--------------------------//
  44.         m_CEUdp.m_OnUdpRecv = OnUdpCERecv;
  45.         DWORD nResult = m_CEUdp.Open(p,local_port,remote_ip,remote_port);
  46.         if (nResult <=0) 
  47.         {
  48.             AfxMessageBox(_T("打开端口失败"));
  49.             return;
  50.         }
  51.         //-------------------------音频--------------------------//
  52.         g_pOut = new CWaveOut();
  53.         g_pIn = new CWaveIn();
  54.         g_pOut->StartPlay();
  55.         g_pIn->StartRec(OnRecording,(DWORD)p);  
  56.         //-------------------------视频--------------------------//
  57.         GdiplusStartup( &m_gdiPlusToken, &m_gdiPlusInPut, NULL ); //初始化GDI+ 
  58.         memset(video_data,0,Video_Width*Video_Height);
  59.         index=0;
  60.     }
  61.     //=====================================================================
  62.     //  语法格式:   void FreeAV()
  63.     //  实现功能:   释放音频、视频
  64.     //  参数:     无
  65.     //  返回值:    无
  66.     //=====================================================================
  67.     void FreeAV()
  68.     {
  69.         
  70.         //-------------------------音频--------------------------//
  71.         g_pOut->StopPlay();
  72.         g_pIn->StopRec();
  73.         delete g_pOut;
  74.         delete g_pIn;
  75.         //-------------------------视频--------------------------//       
  76.         GdiplusShutdown(m_gdiPlusToken); //销毁GDI+
  77.         //------------------------UDP--------------------------//
  78.         m_CEUdp.Close();
  79.     }
  80.     //=====================================================================
  81.     //  语法格式:   void RecAndPlay(WPARAM wParam,LPARAM lParam,HWND hwnd)
  82.     //  实现功能:   接收网络传来的音频,以及播放
  83.     //  参数:     wParam,表示数据;lParam,表示数据长度;hwnd,表示显示视频的窗口句柄
  84.     //  返回值:    无
  85.     //=====================================================================
  86.      static void CALLBACK OnUdpCERecv(CWnd *pWnd,char* buf,int nLen,sockaddr * addr)
  87.      {
  88.         /*测试收到的数据大小
  89.         
  90.         CString tmp;
  91.         tmp.Format(L"%d",nLen);
  92.         MessageBox(0,tmp,0,0);
  93.         return;*/
  94.         //-------------------------如果是音频数据--------------------------//
  95.         if(nLen==Compr_AudioData_Size)
  96.         {
  97.             g726_Decode(buf,(unsigned char*)wave_data);
  98.             g_pOut->Play(wave_data,AudioData_Size);
  99.             return;
  100.         }
  101.         //-------------------------如果是视频数据--------------------------//
  102.       
  103.         if(nLen==VideoData_Size)//完整的视频数据块
  104.         {
  105.             for(int i=0;i<nLen;i++)
  106.             {
  107.                 video_data[index]=buf[i];
  108.                 index++;
  109.             }
  110.             return;
  111.         }
  112.         //视频数据块的最后一块
  113.         for(int i=0;i<nLen;i++)
  114.         {
  115.             video_data[index]=buf[i];
  116.             index++;
  117.         }
  118.         //如果JPEG图像特别大,则肯定是出错,则抛弃
  119.         if(index>Video_Width*Video_Height)
  120.         {
  121.             //MessageBox(0,"缓冲区出错","错误信息",0);
  122.             return;
  123.         }
  124.         
  125.     
  126.         try{
  127.             IPicture *pPic;
  128.             IStream *pStm ; 
  129.             //分配全局存储空间 
  130.             HGLOBAL hGlobal=GlobalAlloc(GMEM_MOVEABLE,index);
  131.             LPVOID pvData=NULL ;
  132.             //锁定分配内存块 
  133.             pvData=GlobalLock(hGlobal);
  134.             //复制数据包video_data到pvData
  135.             memcpy(pvData,video_data,index);
  136.             GlobalUnlock(hGlobal);
  137.             CreateStreamOnHGlobal(hGlobal,TRUE,&pStm);
  138.             ULARGE_INTEGER        pSeek;       
  139.             LARGE_INTEGER    dlibMove  ={  0  }  ;  
  140.             pStm->Seek(dlibMove,STREAM_SEEK_SET  ,&pSeek);  
  141.         //  Sleep(15);
  142.             //装入图形文件
  143.             if(FAILED(OleLoadPicture(pStm,index,TRUE,IID_IPicture,(LPVOID*)&pPic)))
  144.             {//附:如果video_data这个数组包含的图像有错,则OleLoadPicture 容易产生读写内存错误
  145.             //  pPic->Release();
  146.             //  pStm->Release();
  147.                 return ;
  148.             }
  149.             Image img(pStm,0);
  150.             Graphics mGraphics(GetDC(pWnd->m_hWnd));
  151.             mGraphics.DrawImage(&img, 0, 0, Video_Width, Video_Height);
  152.             img.~Image();//会出错
  153.             mGraphics.~Graphics();
  154.             pPic->Release();
  155.             pStm->Release();
  156.         }
  157.         catch(CException * e)
  158.         {}
  159.         memset(video_data,0,Video_Width*Video_Height);
  160.         index=0;
  161.      }
  162.     //=====================================================================
  163.     //  语法格式:   static void OnRecording(char *data,int length,DWORD userdata)
  164.     //  实现功能:   释放音频
  165.     //  参数:     data表示数据,length表示数据长度,userdata暂时没用
  166.     //  返回值:    无
  167.     //=====================================================================
  168.     static void OnRecording(char *data,int length,DWORD userdata)
  169.     {
  170.         memcpy(pin,g_pIn->buffer,AudioData_Size);   
  171.         g726_Encode((unsigned char*)pin,pout);
  172.         m_CEUdp.SendData(pout,Compr_AudioData_Size);
  173.     }
  174. };
posted @ 2009-01-15 09:26  Jade  阅读(1012)  评论(3编辑  收藏  举报