gusucode.com > 用于语音视频聊天的类源码程序 > 用于语音视频聊天的类源码程序/TVideo.cpp

    //---------------------------------------------------------------------------


#pragma hdrstop

#include "TVideo.h"
#include <vcl.h>
//---------------------------------------------------------------------------
LRESULT CALLBACK VideoStreamCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr);
extern TStrings *DebugMsg;
unsigned int FirstTime=0;
int FrameCount=0;
int DataCount=0;
int MaxFrameData=0;
int MaxUseBuf=0;

//---------------------------------------------------------------------------
#pragma package(smart_init)
TVideo::TVideo(HDC InRemoteDC,HDC InLocateDC,HWND InHwndRecvVideo)
{
  FirstTime=0;
  FrameCount=0;
  DataCount=0;
  MaxFrameData=0;

  RemoteDC=InRemoteDC;
  LocateDC=InLocateDC;
  for(int i=0;i<FrameBufCount;i++)
    FrameBufLens[i]=0;
  FirstFrame=0;
  LastFrame=0;
  HwndRecvVideo=InHwndRecvVideo;
 //设置视频格式,当本地无视频设备时还可以看对方的视频,所以要在初始化视频失败之前初始化 m_BmpU
 //将视频格式固定(这是非常常用的格式,一般的视频捕捉设备应该会支持)
  ZeroMemory(&m_BmpU,sizeof(BITMAPINFO));
  m_BmpU.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  m_BmpU.bmiHeader.biWidth=176;
  m_BmpU.bmiHeader.biHeight=144;
  m_BmpU.bmiHeader.biPlanes=1;
  m_BmpU.bmiHeader.biBitCount=24;
  m_BmpU.bmiHeader.biSizeImage=76032;
//  InitializeCriticalSection(&CriticalSection);
}
TVideo::~TVideo()
{
//  DeleteCriticalSection(&CriticalSection);
  int Secs=time(NULL)- FirstTime;
  if(Secs && DebugMsg)
  { DebugMsg->Add("总数据量: "+IntToStr(DataCount));
    DebugMsg->Add("总时间(秒): "+IntToStr(Secs));
    DebugMsg->Add("平均每秒帧: "+IntToStr(FrameCount/Secs));
    DebugMsg->Add("平均每秒数据量: "+IntToStr(DataCount/Secs));
    if(FrameCount)
      DebugMsg->Add("平均每帧数据量: "+IntToStr(DataCount/FrameCount));
    DebugMsg->Add("最大帧数据量: "+IntToStr(MaxFrameData));
    DebugMsg->Add("缓冲最大使用量: "+IntToStr(MaxUseBuf));
  }
  DestroyCap();
  DestroyCodecV();
}

//初始化视频捕捉设备
bool TVideo::InitCap()//
{
  hWndCap=capCreateCaptureWindow(0,0,0,0,0,0,0,0);
  if(hWndCap==NULL)
    return(false);
  if(!capSetUserData(hWndCap,this))
    return(false);
 //连接到驱动
/*
  capCaptureAbort(hWndCap);
  char szName[2],szVer[2];
  int DriverIndex=0;
  for(DriverIndex=0;DriverIndex<MAX_VFW_DEVICES;DriverIndex++)
    if(capGetDriverDescription(DriverIndex,szName,2,szVer,2))
    	break;
  */
  int i=0;
  for(i=0;i<MAX_VFW_DEVICES;i++)
    if(capDriverConnect(hWndCap,i))
       break;
  if(i>=MAX_VFW_DEVICES)
    return(false);
  CAPTUREPARMS CapParms = {0};
  if(!capCaptureGetSetup(hWndCap,&CapParms,sizeof(CAPTUREPARMS)))
    return(false);
//  CapParms.dwRequestMicroSecPerFrame=100000;
//  Memo2->Lines->Add("每秒帧数 "+IntToStr(1000000/CapParms.dwRequestMicroSecPerFrame));
  CapParms.vKeyAbort=0;
  CapParms.fAbortLeftMouse = FALSE;
  CapParms.fAbortRightMouse = FALSE;
  CapParms.fYield = TRUE;
  CapParms.fCaptureAudio=FALSE;
  CapParms.wPercentDropForError = 100;
  if(!capCaptureSetSetup(hWndCap,&CapParms,sizeof(CAPTUREPARMS)))
    return(false);
  if(!capSetVideoFormat(hWndCap,&m_BmpU,sizeof(BITMAPINFO)))
    return(false);
 //设置视频流回调函数
  if(!capSetCallbackOnVideoStream(hWndCap,VideoStreamCallbackProc))
    return(false);
//  if(!capSetCallbackOnFrame(hWndCap, VideoStreamCallbackProc))
//    return(false);
 //开始取视频流
  if(!capCaptureSequenceNoFile(hWndCap))
    return(false);
  return(true);
}

void TVideo::DestroyCap()
{
	if (hWndCap)
	{
              if(IsWindow(hWndCap))
		capCaptureAbort(hWndCap);
              capSetUserData(hWndCap,NULL);
	      capSetCallbackOnVideoStream(hWndCap, NULL);
              capSetCallbackOnFrame(hWndCap, NULL);
              capDriverDisconnect(hWndCap);
	}
	DestroyWindow(hWndCap);
	hWndCap = NULL;
}
/*------------------------------------------------------------------------------*/
//初始化视频编码解码器
bool TVideo::InitCodecV()
{
  ZeroMemory(&m_cv,sizeof(COMPVARS));
  m_cv.cbSize=sizeof(COMPVARS);
  m_cv.dwFlags=ICMF_COMPVARS_VALID ;
  m_cv.hic=m_hIC;
  m_cv.fccType=ICTYPE_VIDEO ;
  m_cv.fccHandler=mmioFOURCC('m','2','6','3'); //859189837;
  m_cv.lpbiOut=0;
  m_cv.lKey=10;
  m_cv.lDataRate=6;
  m_cv.lQ=10000;

  m_hIC=ICOpen(ICTYPE_VIDEO,m_cv.fccHandler,ICMODE_COMPRESS|ICMODE_DECOMPRESS);
  if(m_hIC==NULL)
    return(false);
  m_cv.hic=m_hIC; //必须的

  int dwFormatSize = ICCompressGetFormatSize(m_hIC, &m_BmpU);
  HGLOBAL h = GlobalAlloc(GHND, dwFormatSize);
  LPBITMAPINFOHEADER  lpbiOut;
  lpbiOut = (LPBITMAPINFOHEADER)GlobalLock(h);
  int Ret=ICCompressGetFormat(m_hIC, &m_BmpU, lpbiOut);
  if( ICERR_OK!=Ret)
   { GlobalFree(lpbiOut);
     return(false);
   }
  else
   { memcpy(&m_BmpC, lpbiOut, dwFormatSize>sizeof(BITMAPINFOHEADER)?sizeof(BITMAPINFOHEADER):dwFormatSize);
     GlobalFree(lpbiOut);
   }
//  ICCompressGetFormat(m_hIC,&m_BmpU,&m_BmpC);
  //微软的H.263 codec需要消息确认,得到这个不容易
  ICSendMessage(m_hIC,0x60c9,0xf7329ace,0xacdeaea2);

  m_cv.dwFlags=ICMF_COMPVARS_VALID; //必须的
  //star sequence of frames compression
  if(!ICSeqCompressFrameStart(&m_cv,&m_BmpU))
    return(false);
  //star decompression;
  if(ICDecompressBegin(m_hIC,&m_BmpC,&m_BmpU)!=ICERR_OK)
    return(false);
  return(true);
}

/*------------------------------------------------------------------------------*/
void TVideo::DestroyCodecV()
{
  if(m_hIC)
    {
      ICDecompressEnd(m_hIC);
      ICSeqCompressFrameEnd(&m_cv);
      ICCompressorFree(&m_cv);
      ICClose(m_hIC);
      m_hIC=NULL;
    }
}
/*------------------------------------------------------------------------------*/
//视频流回调函数
LRESULT CALLBACK  VideoStreamCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
  TVideo *Video=(TVideo*)capGetUserData(hWnd);
  if(Video==NULL)
    return(false);
//  Sleep(80);
  SetDIBitsToDevice(Video->LocateDC,0,0, Video->m_BmpU.bmiHeader.biWidth, Video->m_BmpU.bmiHeader.biHeight,
         0, 0, 0, Video->m_BmpU.bmiHeader.biHeight,lpVHdr->lpData, &Video->m_BmpU, 0);
//  EnterCriticalSection(&Video->CriticalSection);
  long CompOutLen;
  void *EncodeP=Video->Compress(lpVHdr->lpData,lpVHdr->dwBytesUsed,&CompOutLen);
//  DebugMsg->Add("dwBytesUsed: "+IntToStr(lpVHdr->dwBytesUsed)+" CompOutLen: "+IntToStr(CompOutLen));
//  DebugMsg->Add("Out: ");
  if(DebugMsg->Count>500)
    DebugMsg->Clear();
  DebugMsg->Add("Out: "+IntToStr(CompOutLen));
//奇特的问题:如果没有 DebugMsg->Add("Out: "+IntToStr(CompOutLen));,大帧将没有发送出去,导致对方显示的不清楚
//单独的 DebugMsg->Add("Out: "); 或者 DebugMsg->Add(IntToStr(CompOutLen)); 都不行

// TStringList *TmpList=new TStringList;
// TmpList->Add("Out: "+IntToStr(CompOutLen));
// delete TmpList;

// AnsiString Tmp=IntToStr(CompOutLen);

 //网络是视频传输中的瓶颈,所以要用循环队列缓冲机制,否则数据会在这里挤压太多

//找个空闲buf
  int P=-1;
  for(int i=Video->FirstFrame;i<FrameBufCount;i++)
    if(Video->FrameBufLens[i]==0)
      { P=i;
        break;
      }
  if(P==-1)
    for(int i=0;i<Video->FirstFrame;i++)
      if(Video->FrameBufLens[i]==0) //没有使用的缓冲
        { P=i;
          break;
        }
  if(P==-1) //没有空闲缓冲就冲掉最老的
   { P=Video->FirstFrame;
     Video->FirstFrame++;
     if(Video->FirstFrame >= FrameBufCount)
       Video->FirstFrame=0;
   }
/*
  if(Video->FirstFrame==-1)
    Video->FirstFrame=Video->LastFrame;
  else if(Video->LastFrame==Video->FirstFrame)
   {// DebugMsg->Add("覆盖:"+IntToStr(Video->FirstFrame));
     Video->PreFirstFrame();
   }
*/
  CompOutLen=CompOutLen > MaxFrameBufSize ? MaxFrameBufSize : CompOutLen;
  Video->FrameBufLens[P]=CompOutLen;
  memcpy(Video->FrameBufs[P],EncodeP,CompOutLen);
/*
  if(Video->LastFrame>=FrameBufCount-1)
    Video->LastFrame=0;
  else
    Video->LastFrame++;
*/
 //调试
   int UseBufCount=0;
   for(int i=0;i<FrameBufCount;i++)
     if(Video->FrameBufLens[i]!=0)
      UseBufCount++;
   if(UseBufCount > MaxUseBuf)
     MaxUseBuf=UseBufCount;
   if(FirstTime==0)
     FirstTime=time(NULL);
   FrameCount++;
   DataCount+=CompOutLen;
   if(CompOutLen > MaxFrameData)
    { MaxFrameData=CompOutLen;
//      DebugMsg->Add("大帧:"+IntToStr(MaxFrameData));
    }
  PostMessage(Video->HwndRecvVideo,ON_VIDEO_IN,0,0);
//  LeaveCriticalSection(&Video->CriticalSection);
  return(true);
}
void TVideo::PreFirstFrame(int OldFirstFrame)
{
//  EnterCriticalSection(&CriticalSection);
  if(FirstFrame!=OldFirstFrame)
    return;
  if(FirstFrame>-1 && FirstFrame<FrameBufCount)
    FrameBufLens[FirstFrame]=0;
  if(FirstFrame>=FrameBufCount-1)
    FirstFrame=0;
  else
    FirstFrame++;
//  LeaveCriticalSection(&CriticalSection);
}
int TVideo::GetFirstFrame()
{
  for(int i=FirstFrame;i<FrameBufCount;i++)
    if(FrameBufLens[i]!=0)
      return(i);
  for(int i=0;i<FirstFrame;i++)
    if(FrameBufLens[i]!=0) //没有使用的缓冲
      return(i);
  return(-1);
}
void TVideo::ShowVideo(BYTE *Data,int DataLen)
{
   if(FirstTime==0)
     FirstTime=time(NULL);
   FrameCount++;
   DataCount+=DataLen;
   if(DataLen > MaxFrameData)
     MaxFrameData=DataLen;
//  DebugMsg->Add(IntToStr(DataLen));
  if(Decompress(Data,0))
    SetDIBitsToDevice(RemoteDC,0,10, m_BmpU.bmiHeader.biWidth, m_BmpU.bmiHeader.biHeight,
         0, 0, 0, m_BmpU.bmiHeader.biHeight,DecompVideo, &m_BmpU, 0);
}
//压缩视频帧
void *TVideo::Compress(void* pIn,int InLen,long *OutLen)
{
  if(pIn==NULL || InLen!=(int)m_BmpU.bmiHeader.biSizeImage)
    return(NULL);
  int Key;
  return(ICSeqCompressFrame(&m_cv,0,pIn,&Key,OutLen));
}
/*------------------------------------------------------------------------------*/
//解压缩视频帧
bool TVideo::Decompress(void *pIn,DWORD flag)
{
  if(ICDecompress(m_hIC,flag,&m_BmpC.bmiHeader,pIn,&m_BmpU.bmiHeader,DecompVideo)!=ICERR_OK)
    return(false);
  DecompVideoLen=m_BmpU.bmiHeader.biSizeImage;
  return(true);
}
void TVideo::StopIn()
{
  capCaptureAbort(hWndCap);
}