一项目中需要实现SipPhone终端与多个其他设备的用户视频互通,由于Sip终端只能显示一路视频,所以模块A采用轮询方式将多个用户视频发送到sip终端上。模块A同时允许手动打开/关闭其中一个用户的视频,当该用户视频打开时候同时选送到sip终端显示。用户视频采用H264编码,Sip终端接收H263编码视频,所以用户视频需要经过H264解码再编成H263视频的转码过程。

 调试过程中偶尔出现Sip终端显示的视频卡死现象,该问题低概率出现,通过查找模块A输出的日志文件,发现Sip终端显示的视频卡死后,模块A不再响应用户发过来的消息。经多方尝试最后定位为模块A主线程堵塞导致问题出现,而主线程堵塞原因是是多个线程访问同一个对象的发生锁死导致。

将数据流程用下面示意图表示:

 

当主线程和定时轮询线程同时调用C264VideoDeCode的Reset函数时候,在Reset内部死锁。

先对发生问题的C264VideoDeCode类做个说明:

C264VideoDeCode是一个H264视频解码封装类,用来完成H264解码并输出解码后的数据,里面有一个线程函数ProcessThreadFunc,通过StartVideoThread启动线程,通过StopVideoThread停止。每次启动C264VideoDeCode解码时调用的Reset函数。 

 
  
  1. BOOL C264VideoDeCode::Reset() 
  2.     ... 
  3.     StopVideoThread(); 
  4.     StartVideoThread(); 
  5.     .... 
  6.  
  7. BOOL C264VideoDeCode::StopVideoThread() 
  8.       if(m_hProcessThread !=NULL) 
  9.       { 
  10.            if(!m_bStop) 
  11.            { 
  12.                  ResetEvent(m_hEvent); 
  13.                  m_bStop    = TRUE; 
  14.                  while(WaitForSingleObject(m_hEvent,1000) !=WAIT_OBJECT_0); 
  15.            } 
  16.            ::CloseHandle(m_hProcessThread); 
  17.            m_hProcessThread = NULL; 
  18.       } 
  19.       return TRUE; 
  20.  
  21. BOOL C264VideoDeCode::StartVideoThread() 
  22.   if(m_hProcessThread ==NULL) 
  23.   { 
  24.       m_hProcessThread =CreateThread(NULL,0,C264VideoDeCode::ProcessThreadFunc,this,0,&m_hProcessThreadID); 
  25.   } 
  26.    return (m_hProcessThread !=NULL); 
  27.  
  28.  
  29.  
  30. DWORD WINAPI C264VideoDeCode::ProcessThreadFunc(LPVOID lpParam) 
  31.       C264VideoDeCode *pthis=( C264VideoDeCode *)lpParam; 
  32.       return pthis->ProcessFunc(lpParam); 
  33.  
  34. DWORD C264VideoDeCode::ProcessFunc(LPVOIDlpParam) 
  35.       HRESULT    hr                    =S_OK; 
  36.       DWORD dwSleep               = 0; 
  37.       m_bStop    = FALSE; 
  38.       ::ResetEvent(m_hEvent); 
  39.       while(!m_bStop) 
  40.       { 
  41.            dwSleep    = 9; 
  42.            if(!m_bReset) 
  43.            { 
  44.                  C264VideoDeCode::Process(); 
  45.                  ... 
  46.            } 
  47.            WaitForSingleObject(m_hEvent,dwSleep); 
  48.       } 
  49.       ::SetEvent(m_hEvent); 
  50.       return 0; 

 

该类的同一个对象被两个线程调用时候,卡死就发送在上面一段代码上。

 

卡死原因:

在主线程调用Rest()执行StopVideoThread函数过程中到WaitForSingleObject时,被第二个线程切换进来调用Rest()运行到StopVideoThread执行了::CloseHandle(m_hProcessThread); m_hProcessThread     = NULL,接着第二个线程调用StartVideoThread并创建ProcessThreadFunc线程成功,导致第一个线程在WaitForSingleObject卡死。

解决方法:

1、::CloseHandle(m_hProcessThread);和m_hProcessThread   =NULL;需要放在if(!m_bStop)括弧内执行。

在StartVideoThread和StopVideoThread加互斥锁。

2、将线程2的操作方法放入主线程中操作,使C264VideoDeCode只支持一个线程的命令。

总结:

1、要清楚工程中进程和线程模型、功能与生命周期,以及信令和数据流程情况。

2、对于视频故障问题,要善于通过日志信息查找原因。

3、编程时候要有线程安全意识,高质量的代码是程序低故障的保证。