关于c++:SkeyePlayer-RTSP播放器源码解析系列之效率优化方案

40次阅读

共计 4241 个字符,预计需要花费 11 分钟才能阅读完成。

测试发现,通过 SkeyePlayer 拉取网络摄像机的流, 其音频可能是 G711,G726 等,而写 MP4 或者转推 RTMP 等都不反对这些音频格式,那么咱们就须要将其音频转码成 AAC,能够应用 libSkeyeAACEncoder 库进行转码,而后写 MP4 或者推送;然而,在理论利用中,咱们发现转码过程其实还是比拟耗时的,它甚至会导致解码线程来不及从而使直播延时增大,所以,咱们采纳队列缓存 + 线程的形式来优化录像和抓图。

实现如下:

  1. 录像优化
    1> 开启录像

             if (pThread->manuRecording == 0x01 && NULL==pThread->m_pMP4Writer && frameinfo.type==Skeye_SDK_VIDEO_FRAME_I)// 开启录制
             {//EnterCriticalSection(&pThread->critRecQueue);                
                 if (!pThread->m_pMP4Writer)
                 {pThread->m_pMP4Writer = new SkeyeMP4Writer();
                 }
                 unsigned int timestamp = (unsigned int)time(NULL);
                 time_t tt = timestamp;
                 struct tm *_time = localtime(&tt);
                 char szTime[64] = {0,};
                 strftime(szTime, 32, "%Y%m%d%H%M%S", _time);
    
                 int nRecordPathLen = strlen(pThread->manuRecordingPath);
                 if (nRecordPathLen==0 || (pThread->manuRecordingPath[nRecordPathLen-1] != '/' && pThread->manuRecordingPath[nRecordPathLen-1] != '\\') )
                 {pThread->manuRecordingPath[nRecordPathLen] = '/';
                 }
                 
                 char sFileName[512] = {0,};
                 sprintf(sFileName, "%sch%d_%s.mp4", pThread->manuRecordingPath, pThread->channelId, szTime);
                  
                 if (!pThread->m_pMP4Writer->CreateMP4File(sFileName, ZOUTFILE_FLAG_FULL))
                 {
                     delete pThread->m_pMP4Writer;
                     pThread->m_pMP4Writer = NULL;
                     //return -1;
                 }        
                 else
                 { }
                 //LeaveCriticalSection(&pThread->critRecQueue);
             }

    2> 录像数据写缓存

                     if (NULL != pThread->pRecAVQueue)
                     {SSQ_AddData(pThread->pRecAVQueue, channelid, MEDIA_TYPE_VIDEO, (MEDIA_FRAME_INFO*)&frameinfo, pbuf);
                     }

    3> 录像线程解决

    LPTHREAD_START_ROUTINE CChannelManager::_lpRecordThread(LPVOID _pParam)
    {PLAY_THREAD_OBJ *pThread = (PLAY_THREAD_OBJ*)_pParam;
     if (NULL == pThread)            return 0;
    
     pThread->recordThread.flag    =    0x02;
    
    #ifdef _DEBUG
     _TRACE("录像线程 [%d] 已启动. ThreadId:%d ...\n", pThread->channelId, GetCurrentThreadId());
    #endif
    
     SkeyeAACEncoder_Handle m_pAACEncoderHandle = NULL;
     int buf_size = 1024*1024;
    
     char *pbuf = new char[buf_size];
     if (NULL == pbuf)
     {
         pThread->recordThread.flag    =    0x00;
         return 0;
     }
    
     char* m_pAACEncBufer = new char[buf_size];
     memset(m_pAACEncBufer, 0x00, buf_size);
    
     //#define AVCODEC_MAX_AUDIO_FRAME_SIZE    (192000)
    #define AVCODEC_MAX_AUDIO_FRAME_SIZE    (64000)
     int audbuf_len = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2;
     unsigned char *audio_buf = new unsigned char[audbuf_len+1];
     memset(audio_buf, 0x00, audbuf_len);
    
     MEDIA_FRAME_INFO frameinfo;
     unsigned int channelid = 0;
     unsigned int mediatype = 0;
    
     while (1)
     {if (pThread->recordThread.flag == 0x03)        break;
    
         int ret = SSQ_GetData(pThread->pRecAVQueue, &channelid, &mediatype, &frameinfo, pbuf);
         if (ret < 0)
         {_VS_BEGIN_TIME_PERIOD(1);
             __VS_Delay(1);
             _VS_END_TIME_PERIOD(1);
             continue;
         }
    
         long long nTimeStamp = frameinfo.timestamp_sec*1000+frameinfo.timestamp_usec/1000;
         byte*pdata=NULL;
         int datasize=0;
         bool keyframe=false;
         try
         {if (mediatype == MEDIA_TYPE_VIDEO)
             {pdata = (byte*)pbuf;// 获取到的编码数据
                 datasize = frameinfo.length;
                 int nVideoWidth     = frameinfo.width;
                 int nVideoHeight    = frameinfo.height;
                 keyframe = frameinfo.type==Skeye_SDK_VIDEO_FRAME_I?true:false;
                 if (pThread->m_pMP4Writer)
                 {pThread->m_pMP4Writer->WriteMp4File((unsigned char*)pdata, datasize, keyframe,  nTimeStamp, nVideoWidth, nVideoHeight);
                 }
             }
             else // 音频
             {pdata = (byte*)pbuf;// 获取到的编码数据
                 datasize = frameinfo.length;
                 int bits_per_sample = frameinfo.bits_per_sample;
                 int channels = frameinfo.channels;
                 int sampleRate = frameinfo.sample_rate;
    
                 if (Skeye_SDK_AUDIO_CODEC_G711U == frameinfo.codec
                     || Skeye_SDK_AUDIO_CODEC_G726 == frameinfo.codec 
                     || Skeye_SDK_AUDIO_CODEC_G711A == frameinfo.codec ) 
                 {if (!m_pAACEncoderHandle)
                     {
                         InitParam initParam;
                         initParam.u32AudioSamplerate=frameinfo.sample_rate;
                         initParam.ucAudioChannel=frameinfo.channels;
                         initParam.u32PCMBitSize=frameinfo.bits_per_sample;
                         if (frameinfo.codec == Skeye_SDK_AUDIO_CODEC_G711U)
                         {initParam.ucAudioCodec = Law_ULaw;} 
                         else if (frameinfo.codec == Skeye_SDK_AUDIO_CODEC_G726)
                         {initParam.ucAudioCodec = Law_G726;}
                         else if (frameinfo.codec == Skeye_SDK_AUDIO_CODEC_G711A)
                         {initParam.ucAudioCodec = Law_ALaw;}
                         m_pAACEncoderHandle = Skeye_AACEncoder_Init(initParam);
                     }
                     unsigned int out_len = 0;
                     int nRet = Skeye_AACEncoder_Encode(m_pAACEncoderHandle, 
                         (unsigned char*)pbuf, frameinfo.length, (unsigned char*)m_pAACEncBufer, &out_len) ;
                     if (nRet>0&&out_len>0)
                     {pdata = (byte*)m_pAACEncBufer;
                         datasize = out_len;
                         frameinfo.codec = Skeye_SDK_AUDIO_CODEC_AAC;
                     } 
                     else
                     {continue;}
                 }
    
                 if (pThread->m_pMP4Writer)
                 {if (pThread->m_pMP4Writer->CanWrite())
                     {pThread->m_pMP4Writer->WriteAACToMp4File((unsigned char*)pdata, 
                             datasize, nTimeStamp, sampleRate, channels, bits_per_sample);
                     }
                 }
             }
         }
         catch (...)
         {continue;}
     }
    
         pThread->recordThread.flag    =    0x00;
    
    #ifdef _DEBUG
     _TRACE("录像线程 [%d] 已退出 ThreadId:%d.\n", pThread->channelId, GetCurrentThreadId());
    #endif
    
     return 0;
    }
  2. 抓图优化

抓图原理同录像,惟一区别是间接数据传入线程,进行 jpg 编码存文件,详见代码。

正文完
 0