点对点视频会议程序VideoNet开发例解
Easy Digital Camera Connection

VideoNet

利用VC++实现局域网实时视频传输

VC++6.0平台下利用DirectShow技术实现DV设备的视频处理

CAviCap and CFrameGrabber - wrappers for AVICap Window

Tracking an object from a live video input

http://tag.csdn.net/tag/directshow/2.html  CSDN上很多例子
http://search.csdn.net/search/DirectShow/4/dev/

在VC中调用DirectShow全屏播放视频

Directshow(SDK)学习笔记九_视频捕捉

用DirectShow实现QQ的音视频聊天功能


用.NET 2.0压缩/解压功能处理大型数据
DirectShowNet Library
http://directshownet.sourceforge.net/

室外5公里的802.11B无线远程连接


用Delphi开发视频聊天软件
基于VFW的视频应用程序开发 

Visual C++视频会议开发技术与实例(附光盘)

我去年做了一段时间局域网上的实时视频传输,如果是服务器对多用户进行实时的视频传输,以我的经验:  
        1.服务器软件用多线程:  
            (1)主线程:读出(看你的图象具体怎么上PC机了)一帧视频数据,送给拆分线程。  
            (2)拆分线程:接到一帧视频,开始拆包、做帧标记、打序列号,送给发送线程。  
            (3)发送线程:用RTP   socket把封装好的数据包发给客户端。此socket是点对多点、单向  
                    有根方式的组播套接字,实际上是基于UDP派生的,但他用到了RTP和RTCP(实时传输  
                    协议和实时传输控制协议),如果你传输的不是实时数据,直接用UDP就行了。  
   
        2.客户端软件结构一般用多线程,线程间用事件对象进行同步,而共享数据区用临界区对象进  
  行同步。  
          (1)主线程:接收网络数据包的线程,优先级最高,可以尽量保证不丢数据,也采用RTP协  
                    议,用网络事件来触发。主线程接收到视频数据包后,将数据放入一个链表中,然后  
                    用事件对象触发组装线程。  
          (2)组装线程:从链表中读出数据包,要进行帧确认、排序等工作,当把一帧图象的所有  
                  包都取到时,再调用组装模块(可以是一个函数),将这些数据包组装成完整的一个  
                  帧,然后送到解压线程。  
          (3)若干解压播放线程。主要考虑到如果你客户端软件想同时播放多画面,比如说4画面图  
                    象,就要用4个解压播放线程。  
          (4)至于图象存储,要看你的客户需要怎么存了,如果是手工存当然不需要单开线程,如果  
                  是规定定时存或在某个事件发生时自动存盘,就需要单开一个线程,由定时器到时消息  
                  或此事件发生来触发。  


将每帧视频数据按某种编码格式编码(如mpg4,   h263等)压缩通过rtp传输到另一机子上,用rtcp控制调整传输速率。  
  你可以先用udp传一下看看。  
   
  http://www.dlc.sjtu.edu.cn/newspost/newsdetail.asp?newsno=1546  
   
  目前,互联网上用于多媒体数据流的技术协议有实时传输协议RTP、实时传输控制协议RTCP、实时流协议RTSP、资源预订协议RSVP等。RTP主要处理一对一、一对多的多媒体数据流传输任务,可以按照UDP、TCP及ATM等协议传输数据,负责提供时间信息和控制流同步。RTCP的作用则在于与RTP一起解决非常关键的流量控制和拥塞控制问题,两种协议配合使用可以大大提高传输效率,所以是在线实时数据传送的主要方式。RTSP协议定义一对多程序有效地通过IP网络传送多媒体数据的方式,在体系结构中位于RTP和RTCP之上。与HTTP协议相比,RTSP的特点在于客户端和服务器端都可以发出请求,是一种双向的传输协议。另外,由于多媒体数据的流式传输对网络延时非常敏感,所以需要预先为流媒体的传输预留一部分网络带宽,这项功能可以通过资源预订协议RSVP获得实现。    
   
  文件格式    
  与传输协议的相对统一不同,流媒体文件的格式现在相当繁杂,如视频文件就有MPEG、AVI、RM、DVI、QuickTime等多种格式,其它还有ASF、应用于Flash动画的SWF等格式。不过总的来说,在互联网上得到广泛应用的还是微软的ASF、RealNetworks的Real   system和苹果公司的QuickTime。ASF在微软深厚的市场应用和技术实力支持下,不仅可以以任何编码方式进行压缩和解压缩,还适应于所有的底层网络传输协议,所以推出后迅速成为主要的流媒体文件格式。RealSystem则包括了RealAudio、RealVideo、RealFlash和RealPresentation四种文件格式,分别用于制作不同类型的流媒体文件。RealSystem是几乎每个网络用户都曾经接触过的流媒体技术,性能表现非常稳定,大量的流媒体服务网站都采用了这项技术,中国网络用户看到的多数在线影视节目也都是RealSystem格式的。QuickTime和RealSystem一样,也是出现较早的流媒体文件格式之一,目前已经发展到支持实时和快速启动两种类型的数据流,应用方式相当灵活。这三种流媒体技术都有厂商提供的成熟的支持系统,系统中一般包括流媒体文件的制作、发布、客户端播放等多种工具,便于技术的推广应用。    
  不过互联网上的主流技术不一定能充分满足广电行业的需求,一些专家在谈及广电领域的流媒体应用时,就更为看好MPEG-4。MPEG-4的突出优点是具有交互功能,可以使影视节目具有类似网站和视频游戏的交互性,而这正是新一代广电系统的发展方向之一。再加上无线视频传输通常采用MPEG-4格式,所以MPEG-4的发展潜力是相当可观的。  

include   <dshow.h>  
  #include   <streams.h>  
  #include   <windows.h>  
  #include   <mmsystem.h>  
  #include   <stdio.h>  
   
  class   CMemStream   :   public   CAsyncStream  
  {  
  public:  
          CMemStream(LPBYTE   pbData,   LONGLONG   llLength,   DWORD   dwKBPerSec   =   INFINITE)   :  
                  m_pbData(pbData),  
                  m_llLength(llLength),  
                  m_rllPosition(0),  
                  m_dwKBPerSec(dwKBPerSec)  
          {  
                  m_dwTimeStart     =   timeGetTime();  
   
  clearPlayBuf();  
   
  m_packetsize   =   1024*32;  
   
  m_packetnum   =   32; //1M  
   
  m_playbufsize     =   m_packetsize   *   m_packetnum;  
   
  m_playbuf             =   new   BYTE[m_playbufsize];  
   
  m_llLength   =   4000000000000;  
   
  m_fileheadsize   =   m_packetsize   *   10;  
   
  hMutex   =   CreateMutex(NULL,FALSE,"protect   buf");  
   
  m_bheadover   =   FALSE;  
   
  m_breadnum           =   0;  
   
  hLogFile   =   fopen("log.txt","w+");  
   
          }  
       
  ~CMemStream()  
      {  
  clearPlayBuf();  
   
  delete   m_playbuf;  
   
  fclose(hLogFile);  
      }  
   
          HRESULT   Read(PBYTE   pbBuffer,  
                                    DWORD   dwBytesToRead,  
                                    BOOL   bAlign,  
                                    LPDWORD   pdwBytesRead)  
          {  
                  CAutoLock   lck(&m_csLock);  
                  DWORD   dwReadLength;  
   
   
  dwReadLength   =   dwBytesToRead;  
                   
  //当没有处理完播放头的时候处于等待状态  
  if(!m_bheadover)  
  {  
  while(m_a_fw < m_fileheadsize)  
  {  
  fprintf(hLogFile,"Wait   Head\n");  
  Sleep(100);  
  }  
  }  
   
  m_bheadover =   TRUE;  
   
   
  while(CanR()==false)  
  {  
  fprintf(hLogFile,"CanR=false");  
  Sleep(100);  
  }  
  fprintf(hLogFile,"\n");  
   
  m_rllPosition = m_a_fr   %   m_playbufsize;  
   
  //fprintf(hLogFile,"Read:m_rllPosition=%d\n",m_rllPosition);  
  //fprintf(hLogFile,"Read:m_wllPosition=%d\n\n",m_wllPosition);  
   
  //读取缓冲区数据并播放  
  WaitForSingleObject(hMutex,INFINITE);    
   
  CopyMemory((PVOID)pbBuffer,  
    (PVOID)(m_playbuf+m_rllPosition),  
                                        dwReadLength);  
   
  ReleaseMutex(hMutex);  
   
  m_a_fr   +=   dwReadLength;  
   
  *pdwBytesRead       =   dwReadLength;  
                   
  return   S_OK;  
          }  
   
  bool   Write(PBYTE   buf)  
      {  
  if(CanW()==false)  
  {  
  fprintf(hLogFile,"CanW=false");  
  return   false;  
  }  
  fprintf(hLogFile,"\n");  
   
  m_wllPosition = m_a_fw   %   m_playbufsize;  
   
  //fprintf(hLogFile,"Write:m_rllPosition=%d\n",m_rllPosition);  
  //fprintf(hLogFile,"Write:m_wllPosition=%d\n\n",m_wllPosition);  
   
  //添加新的数据进缓冲区  
  WaitForSingleObject(hMutex,INFINITE);    
       
  CopyMemory((PVOID)(m_playbuf+m_wllPosition),  
    (PVOID)buf,  
    m_packetsize);    
   
  ReleaseMutex(hMutex);  
   
  m_a_fw   +=   m_packetsize;  
   
  return   true;  
      }  
       
  ULONG getPacketSize()  
  {  
  return   m_packetsize;  
  }  
   
  HRESULT   SetPointer(LONGLONG   llPos)  
          {  
                  if   (llPos   <   0   ||   llPos   >   m_llLength)   {  
                          return   S_FALSE;  
                  }   else   {  
  //关键,过滤器要对流预读,会有一次跳回0.  
  //害的我调试了两天,发发牢骚,哈哈~  
                          m_a_fr   =   llPos;  
                          return   S_OK;  
                  }  
          }  
   
  void   setFileHeadSize(ULONG   fileheadsize)  
  {  
  if(fileheadsize > m_packetsize   *10)  
  {  
  m_fileheadsize = fileheadsize;  
  }else{  
  m_fileheadsize   =   m_packetsize*10;  
  }  
  }  
   
   
          LONGLONG   Size(LONGLONG   *pSizeAvailable)  
          {  
                  LONGLONG   llCurrentAvailable   =  
                          Int32x32To64((timeGetTime()   -   m_dwTimeStart),m_dwKBPerSec);  
                  *pSizeAvailable   =   min(m_llLength,   llCurrentAvailable);  
                  return   m_llLength;  
          }  
   
  void   clearPlayBuf()  
  {  
  m_rllPosition     =   0;  
   
  m_wllPosition     =   0;  
   
  m_a_fr   =   0;  
   
  m_a_fw   =   0;  
   
  }  
   
  bool   CanR()  
  {  
      if   ((m_a_fw - m_a_fr) >=   m_packetsize)  
  {  
  return   true;  
  }else{  
  return   false;  
  }  
  }  
   
  bool   CanW()  
  {  
  if   ((m_a_fw - m_a_fr) <=   (m_playbufsize - m_packetsize))  
  {  
  return   true;  
  }else{  
  return   false;  
  }  
  }  
   
   
   
          DWORD   Alignment()  
          {  
                  return   1;  
          }  
          void   Lock()  
          {  
                  m_csLock.Lock();  
          }  
          void   Unlock()  
          {  
                  m_csLock.Unlock();  
          }  
   
  private:  
          CCritSec m_csLock;  
          const   PBYTE m_pbData;  
          LONGLONG m_llLength;  
          DWORD m_dwKBPerSec;  
          DWORD m_dwTimeStart;  
   
  PBYTE m_playbuf; //自定义缓冲区  
  ULONG m_playbufsize;         //自定义缓冲区大小  
  ULONG m_packetsize; //一帧的大小  
  ULONG m_packetnum; //缓冲区中帧的数量  
   
  ULONG m_wllPosition;         //相对写地址  
      ULONG m_rllPosition;         //相对读地址  
   
          LONGLONG m_a_fr; //绝对读地址  
  LONGLONG m_a_fw; //绝对写地址  
   
  HANDLE hMutex; //关键段保护  
   
  ULONG m_fileheadsize; //要预读的文件头大小  
  BOOL m_bheadover; //时候读完头  
   
  ULONG m_breadnum; //流指针跳回0的次数  
   
  FILE *hLogFile;  
  };  
   
  class   CMemReader   :   public   CAsyncReader  
  {  
  public:  
  STDMETHODIMP   Register()  
          {  
                  return   S_OK;  
          }  
          STDMETHODIMP   Unregister()  
          {  
                  return   S_OK;  
          }  
          CMemReader(CMemStream   *pStream,   CMediaType   *pmt,   HRESULT   *phr)   :  
                  CAsyncReader(NAME("Mem   Reader"),   NULL,   pStream,   phr)  
          {  
                  m_mt   =   *pmt;  
          }  
  };  


当前,在Windows 平台下开发视频应用程序一般采用两种方式:一种是基于视频采集卡所附带的二次软件开发包SDKSoftware development kit)进行。这种方式的优点是应用方便,容易上手,缺点是对硬件的依赖性较强,灵活性差,且功能参差不齐,不能充分满足各种视频应用程序的开发需要;

另一种方式是基于VFW(Video for Windows)进行的。VFW Microsoft公司为开发Windows平台下的视频应用程序提供的软件工具包,提供了一系列应用程序编程接口(API),用户可以通过它们很方便地实现视频捕获[1]、视频编辑及视频播放等通用功能,还可利用回调函数开发更复杂的视频应用程序。它的特点是播放视频时不需要专用的硬件设备,而且应用灵活,可以满足视频应用程序开发的需要。Windows操作系统自身就携带了VFW,系统安装时,会自动安装VFW的相关组件。VC++4.0以来就支持VFW,大大简化了视频应用程序的开发。目前,PC机上多媒体应用程序的视频部分,大都是利用VFW API开发的。

1 VFW 的体系结构

VFW以消息驱动方式对视频设备进行存取,可以很方便地控制设备数据流的工作过程。目前,大多数的视频采集卡驱动程序都支持VFW接口,它主要包括多个动态连接库,通过这些组件间的协调合作,来完成视频的捕获、视频压缩及播放功能。VFW体系结构如图1所示。

1VICAP.DLL:主要实现视频捕获功能,包含了用于视频捕获的函数,为音像交错AVI (Audio video interleaved)格式文件和视频、音频设备程序提供一个高级接口。

2MSVIDEO.DLL:能够将视频捕获窗口与获驱动设备连接起来,支持ICM视频编码服务。

3MCIAVI.DRV:包含MCIMedia control interface)命令解释器,实现回放功能。

4AVIFILE.DLL:提供对AVI文件的读写操作等文件管理功能。

5ICM ( Installable compression manager ):即压缩管理器,提供对存储在AVI文件中视频图像数据的压缩、解压缩服务。

       6ACM ( Audio Compression Manager ):即音频压缩管理器,提供实时音频压缩及解压缩功能。

Capture Application

Playback Application

Edit Application

AVICAP.DLL

 AVICAP

MSVIDEO.DLL

MCIWnd

ACM

MCIAVI.DRV

MCI Command

Interpreter

MSVIDEO.DLL

Video in     ICM

Channel

AVIFILE.DLL

File / Stream

Handler

MSVIDEO.DLL

Drawdib ICM 

Capture Application

Playback Application

Edit Application

AVICAP.DLL

 AVICAP

MSVIDEO.DLL

MCIWnd

ACM

MCIAVI.DRV

MCI Command

Interpreter

MSVIDEO.DLL

Video in     ICM

Channel

AVIFILE.DLL

File / Stream

Handler

MSVIDEO.DLL

Drawdib ICM 

Capture Application

Playback Application

Edit Application

AVICAP.DLL

 

 

AVICAP

MSVIDEO.DLL

 

 

MCIWnd

ACM

MCIAVI.DRV

 

 

MCI Command

Interpreter

MSVIDEO.DLL

 

 

Video in     ICM

Channel

 

AVIFILE.DLL

 

File / Stream

Handler

 

MSVIDEO.DLL

 

Drawdib ICM 

 视频捕获

视频数据的实时采集,主要通过AVICAP模块中的消息、宏函数、结构以及回调函数来完成。视频捕获的一般过程如下:

2.1 建立捕获窗口

利用AVICAP 组件函数 capCreateCaptureWindow() 建立视频捕获窗口,它是所有捕获工作及设置的基础,其主要功能包括: 动态地同视频和音频输入器连接或断开;设置视频捕获速率;提供视频源、视频格式以及是否采用视频压缩的对话框;设置视频采集的显示模式为Overlay或为Preview; ⑤ 实时获取每一帧视频数据;将一视频流和音频流捕获并保存到一个AVI文件中;捕获某一帧数字视频数据,并将单帧图像以DIB格式保存;指定捕获数据的文件名,并能将捕获的内容拷贝到另一文件。

2.2 登记回调函数[2]

登记回调函数用来实现用户的一些特殊需要。在以一些实时监控系统或视频会议系统中,需要将数据流在写入磁盘以前就必须加以处理,达到实时功效。应用程序可用捕获窗来登记回调函数,以便及时处理以下情况:捕获窗状态改变、出错、使用视频或音频缓存、放弃控制权等,相应的回调函数分别为 capStatusCallback(), capErrorCallback(), capVideoStreamCallback(), capWaveStreamCallback(),capYieldCallback()

2.3 获取捕获窗口的缺省设置

通过宏capCaptureGetSetup(hWndCap,&m_Parms,sizeof(m_Parms))来完成。

2.4 设置捕获窗口的相关参数

通过宏capCaptureSetSetup(hWndCap,&m_Parms,sizeof(m_Parms))来完成。

2.5 连接捕获窗口与视频捕获卡

通过宏capDriveConnect(hWndCap,0)来完成。

2.6 获取采集设备的功能和状态

通过宏capDriverGetCaps(hWndCap,&m_CapDrvCapsizeof(CAPDRIVERCAPS))来获取

视频设备的能力,通过宏capGetStatus(hWndCap,&m_CapStatus,sizeof(m_CapStatus))

来获取视频设备的状态。

2.7 设置捕获窗口显示模式

视频显示有Overlay(叠加)Preview(预览)两种模式。在叠加模式下,捕获视频数据布展系统资源,显示速度快,视频采集格式为YUV格式,可通过capOverlay(hWndCap,TRUE)来设置;预览模式下要占用系统资源,视频由系统调用GDI函数在捕获窗显示,显示速度慢,它支持RGB视频格式。

2.8 捕获图像到缓存或文件并作相应处理

若要对采集数据进行实时处理,则应利用回调机制,由capSetCallbackOnFramehWndCap, FrameCall-

backProc)完成单帧视频采集;由capSetCallbackOnVideoStream(hWndCap, VideoCallbackProc)完成视频流采集。如果要保存采集数据,则可调用capCaptureSequencehWnd);要指定文件名,可调用capFileSetCap-

ture(hwnd, Filename)

2.9 终止视频捕获 断开与视频采集设备的连接

调用capCatureStop(hWndCap)停止采集,调用capDriverDisconnect(hWndCap), 断开视频窗口与捕获驱动程序的连接。

3 视频编辑和播放

利用VFW,不仅可以实现视频流的实时采集,还提供了编辑和播放功能,主要通过AVIFILEICMACMMCIWnd 等组件之间的协作来完成。

       1) AVIFileInit();//初始化;

2) AVIFileOpen(); //打开一个AVI文件并获文件的句柄;

3) AVIFileInfo(); //获取文件的相关信息,如图像的WidthHeight;

4) AVIFileGetStream(); //建立一个指向需要访问的数据流的指针;

5) AVIStreamInfo(); //获取存储数据流信息的AVISTREAMINFO结构;

6) AVIStreamRead(); //读取数据流中的原始数据, AVI文件进行所需的编辑处理;

7) AVIStreamRelease(); //释放指向视频流的指针;

8) AVIFileRelease();AVIFileExit(); //释放AVI文件。

若数据是压缩过的,则用AVIStreamGetFrameOpen(),AVIStreamGetFrame()AVIStreamGetFrameClose()来操作,可以完成对视频流的逐帧分解。

3.2 视频播放

对于实现视频流的播放,VFW提供了MCIWnd窗口类[4],主要用于创建视频播放区,控制并修改MCI窗口当前加载媒体的属性。一个由函数、消息和宏组成的库与MCIWnd相关联,通过它们可以进行AVI文件操作,很方便地使应用程序完成视频播放功能。

1)MCIWndCreate(); //注册MCIWnd窗口类,创建MCIWnd窗口,并指定窗口风格;

2)AVIFileInit(); //初始化;

3) AVIFileOpen(); //打开AVI文件;

4) AVIFileGetStream(); //获得视频流;

5)运用相关函数进行各种播放任务:MCIWndPlay()正向播放AVI文件内容,MCIWndPlayReverse()反向播放,MCIWndResume() 恢复播放,MCIWndPlayPause()暂停播放,MCIWndStop()停止播放等等。

6) AVIStreamRelease(); //释放视频流;

7)AVIFileRease();AVIFileExit(); //断开与AVI文件的连接,释放视频源。

由以上步骤可以看出,视频播放是视频编辑其中的一种操作。