四、多线程编程技术的应用
我在前面说过,为了实现基于TCP/IP下的可视电话,就必须“并行”地执行语音采集、语音编解码、图象采集、图象编解码以及码流数据的接收与发送。语音与图象的采集由硬件采集卡进行,我们的程序只需初始化该硬件采集卡,然后实时读取采集数据即可,但语音和图象数据的编解码以及码流数据的传输都必须由程序去协调执行,决不能在某一件事件上处理过长,必须让CPU轮流的为各个事件服务,Windows95下的线程正是满足这种要求的编程技术。
下面我给出了利用Windows95 环境下的多线程编程技术实现的基于TCP/IP的可视电话的部分源码,其中包括主窗口过程函数,以及主叫端与被叫端的TCP/IP接收线程函数和语音编解码的线程函数。由于图象编解码的实时性比语音处理与传输模块的实时性的 要求要低些,所以我以语音编解码为事件去查询图象数据,然后进行图象编解码,而没有为图象编解码去单独实现一个线程。
在主窗口初始化时, 我用CREATE_SUSPENDED标志创建了两个线程hThreadG7231和hThreadTCPRev。一个用于语音编解码,它的线程函数为G723Proc, 该线程不断查询本地有无编好码的语音和图象的码流,如有,则进行H.223打包,然后通过TCP的端口发送给对方。另外一个线程用于TCP/IP的接收,它的线程函数为AcceptThreadProcRev,该线程不断侦 测TCP/IP端口有无对方传来的码流,如有,就接收码流,进行H.223解码后送入相应的缓冲区。该缓冲区的内容,由语音编解码线程G723Proc查询,并送入相应的解码器。由于使用了多线程的编程技术,使得操作系统定时去服务语 音编解码模块和传输模块,从而保证了通信的不中断。
五、程序源码
//基于TCP/IP可视电话主窗口的窗口过程 LONG APIENTRY MainWndProc(HWND hWnd,UINT message,UINT wParam, LONG lParam) { static HANDLE hThreadG7231,hThreadTCPListen,hThreadTCPRev; DWORDThreadIDG7231,ThreadIDTCPListen,ThreadIDTCPRev; static THREADPACK tp; static THREADPACK tp1; unsigned char Buf[80]; CAPSTATUS capStatus; switch (message) { case WM_CREATE: Init_Wsock(hWnd); //初始化一些数据结构 Init_BS(2,&bs); vd_tx_pdu.V_S = 0;vd_tx_pdu.N_S = 0; vd_rx_pdu.V_R = 0;vd_tx_sdu.bytes = 0; if( dnldProg ( hWnd, "h324g723.exe") ) { //装入语音编解码的DSP核心 MessageBox(hWnd,"Load G.723.1 Kernel Error","Error",MB_OK); PostQuitMessage(0); } else MessageBox(hWnd,"Load G.723.1 Kernel OK!","Indication",MB_OK); //创建语音编解码的线程 parag7231.hWnd = hWnd; hThreadG7231=CreateThread (NULL, 0,(LPTHREAD_START_ROUTINE)G723Proc, (G7231DATA *)?g7231, CREATE_SUSPENDED,(LPDWORD)&ThreadIDG7231); if (!hThreadG7231) { wsprintf(Buf, "Error in creating G7231 thread: %d",GetLastError()); MessageBox (hWnd, Buf, "WM_CREATE", MB_OK);} //创建TCP/IP接收线程 tp1.hWnd = hWnd; hThreadTCPRev = CreateThread (NULL, 0,(LPTHREAD_START_ROUTINE)AcceptThreadProcRev, (G7231DATA *)&tp1,CREATE_SUSPENDED, (LPDWORD)&ThreadIDTCPRev); if (!hThreadTCPRev) { wsprintf(Buf, "Error in creating TCP Receive thread: %d",GetLastError()); MessageBox (hWnd, Buf, "WM_CREATE", MB_OK);} //开始侦听网络 SendMessage(hWnd,WM_COMMAND,IDM_LISTEN,NULL); break; case WM_VIDEO_ENCODE: //图象编码 if(needencode)EncodeFunction(hWnd); needencode = SendVideoToBuff(&vd_tx_sdu, buff); frameMode=TRUE; capPreview(capWnd,FALSE); capOverlay(capWnd,FALSE); capGrabFrameNoStop(capWnd); break; case WM_VIDEO_DECODE: //图象解码 Video_Decod_begin = 1; play_movie(); Video_Decod_begin = 0; break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_CONNECT: //响应对方的呼叫,接通可视电话 WskConnect( hWnd ); ResumeThread(hThreadTCPRev); //运行TCP/IP接收线程 ResumeThread(hThreadG7231); //运行语音编解码线程 BeginG7231Codec(); //初始化图象采集卡,并开始采集图象 frameMode = FALSE; capWnd = capCreateCaptureWindow((LPSTR)"Capture Window", WS_CHILD | WS_VISIBLE, 100, 100, 176,144 , (HWND) hWnd, (int) 0); capSetCallbackOnError(capWnd, (FARPROC)ErrorCallbackProc) ; capSetCallbackOnStatus(capWnd, (FARPROC)StatusCallbackProc) ; capSetCallbackOnFrame(capWnd, (FARPROC)FrameCallbackProc) ; capDriverConnect(capWnd, 0); CenterCaptureWindow(hWnd, capWnd); capDlgVideoSource(capWnd); capDlgVideoFormat(capWnd); capDlgVideoCompression(capWnd); capGetStatus(capWnd,&capStatus,sizeof(CAPSTATUS)); StartNewVideoChannel(hWnd, capWnd) ; image = image_one; frameMode = TRUE; capPreview(capWnd,FALSE); capOverlay(capWnd,FALSE); capGrabFrameNoStop(capWnd); break; case IDM_LISTEN: //拨对方号码,呼叫对方 sock = socket( AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { MessageBox(hWnd, "socket() failed", "Error", MB_OK); closesocket(sock); break;} if (!FillAddr(hWnd, &local_sin, FALSE )) //获取TCP/IP地址和端口号 break; EnableMenuItem(GetMenu( hWnd ), IDM_LISTEN, MF_GRAYED); SetWindowText( hWnd, "Waiting for connection.."); bind ( sock , (struct sockaddr FAR *)&local_sin,sizeof(local_sin); if (listen( sock, MAX_PENDING_CONNECTS ) <0) { sprintf(szBuff, "%d is the error", WSAGetLastError()); MessageBox(hWnd, szBuff, "listen(sock) failed", MB_OK); break;} tp.hWnd="hWnd; //开始本地的TCP/IP接收线程" _beginthread(AcceptThreadProc,0,&tp); ResumeThread(hThreadG7231); // 开始本地语音编解码的线程 break; case IDM_DISCONNECT: //挂断可视电话 CloseG7231Codec(); SuspendThread(hThreadG7231); SuspendThread(hThreadTCPRev); WSACleanup(); Init_Video_Decod_Again(); capSetCallbackOnError(capWnd, NULL); capSetCallbackOnStatus(capWnd, NULL); InvalidateRect(hWnd,NULL,1); capSetCallbackOnFrame(capWnd, NULL); capSetCallbackOnVideoStream(capWnd, NULL); capDriverDisconnect(capWnd); Init_Wsock(hWnd); MessageBox(hWnd, "Now closing the Video telephone","",MB_OK); SetDisConnectMenus(hWnd); SendMessage(hWnd, WM_COMMAND,IDM_LISTEN,NULL); break; case IDM_EXIT: CloseG7231Codec(); SendMessage(hWnd, WM_CLOSE, 0, 0l); break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } break; case WM_CLOSE: if (IDOK !="MessageBox(" hWnd, "OK to close window?", gszAppName, MB_ICONQUESTION | MB_OKCANCEL ))break ; case WM_DESTROY: WSACleanup(); CloseG7231Codec(); TerminateThread(hThreadG7231,0); TerminateThread(hThreadTCPRev,0); capSetCallbackOnError(capWnd, NULL); capSetCallbackOnStatus(capWnd, NULL); capSetCallbackOnFrame(capWnd, NULL); capSetCallbackOnVideoStream(capWnd, NULL); capDriverDisconnect(capWnd); FreeAll(); PostQuitMessage(0); break; default: /* Passes it on if unproccessed */ return (DefWindowProc(hWnd, message, wParam, lParam)); } return (0); }
//主叫方TCP/IP接收线程 DWORD WINAPI AcceptThreadProc( PTHREADPACK ptp ) { SOCKADDR_IN acc_sin; /* Accept socket address internet style */ int acc_sin_len; /* Accept socket address length */ int status; acc_sin_len="sizeof(acc_sin);" //调用阻塞函数accept,一直到远端响应为止 sock="accept(" sock,(struct sockaddr FAR *) &acc_sin,(int FAR *) &acc_sin_len ); if (sock < 0) { sprintf(szBuff, "%d is the error", WSAGetLastError()); MessageBox(ptp->hWnd, szBuff, "accept(sock) failed", MB_OK); return (1); } SetConnectMenus( ptp->hWnd ); //远端提机,可视电话接通 BeginG7231Codec(); while (1) { beg1: status = recv((SOCKET)sock, r_mux_buf,MY_MSG_LENGTH, NO_FLAGS_SET ); if (status == SOCKET_ERROR) { status = WSAGetLastError(); if( status == 10054 ){ MessageBox(ptp->hWnd,"对方挂断电话","Indication", MB_OK); SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL); _endthread(); return (1); } goto beg1; } if (status) { r_mux_buf[ status ] = '/0'; if ( r_mux_buf_filled == 1 ) r_mux_buf_overwrite = 1; else r_mux_buf_filled = 1; r_mux_buf_length = status; } else { MessageBox( hWnd, "Connection broken", "Error", MB_OK); SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL); _endthread(); return (2); } demux(); //线路码流H.223解码 } return (0); }
//被叫方TCP/IP接收线程 DWORD WINAPI AcceptThreadProcRev( PTHREADPACK ptp ) { int status; while (1) { beg2: status = recv((SOCKET)sock, r_mux_buf,MY_MSG_LENGTH, NO_FLAGS_SET ); if (status == SOCKET_ERROR) { status =WSAGetLastError(); if( status == 10054 ) { MessageBox(ptp->hWnd,"对方挂断电话","Indication", MB_OK); SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL); return (1); } goto beg2; } if (status) { r_mux_buf[ status ] = '/0'; if( r_mux_buf_filled == 1 ) r_mux_buf_overwrite = 1; else r_mux_buf_filled = 1; r_mux_buf_length = status; } else { MessageBox( hWnd, "Connection broken", "Error", MB_OK); SendMessage( ptp->hWnd, WM_COMMAND,IDM_DISCONNECT,NULL); return (2); } demux(); } /* while (forever) */ return (0); }
//语音编解码线程 DWORD WINAPI G723Proc(G7231DATA *data) { int i,len; Audio_tx_pduad_tx_pdu; unsigned char mux[MAX_MUX_PDU_SIZE]; do { len = 0; //检测本地有无语音,图象码流要传输 i = DetectAudioVideoData(); switch(i) { case AUDIO_ONLY: //只有语音码流 AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu); //H.223打包 len = AL2_To_MUX(&ad_tx_pdu, mux); break; case VIDEO_ONLY: //只有图象码流 SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu); tx_AL3_I_PDU(&vd_tx_pdu ,&bs , 1); //H.223打包 len = AL3_To_MUX(&vd_tx_pdu,mux); break; case AUDIO_VIDEO: //语音和图象码流 AL2_CRC_coder(&ad_tx_sdu,&ad_tx_pdu); SDU_To_PDU(&vd_tx_sdu,&vd_tx_pdu); tx_AL3_I_PDU(&vd_tx_pdu ,&bs , 1); //H.223打包 len = AL2_AL3_To_MUX(&ad_tx_pdu,&vd_tx_pdu,mux); break; case NO_AUDIO_VIDEO: //此刻无码流要传输 break; } //TCP/IP发送码流 if(len != 0) send((SOCKET)sock,mux,len,0); //是否接收到待解码的码流,有就调用解码器 PutVideoStreamToDecod(); } while(1); return (0); }
|
|