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); }