zoukankan      html  css  js  c++  java
  • 基于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. };
  • 相关阅读:
    【面试】前端基本
    【echart】学习笔记
    【Django】url传递参数
    【HTML】section
    【jquery】获取元素高度
    【ztree】ztree例子
    【CSS3】块级元素与行内元素的区别
    Vue 项目中应用
    Vue 指令
    Vue 组件
  • 原文地址:https://www.cnblogs.com/Jade2009/p/1376062.html
Copyright © 2011-2022 走看看