海康大华摄像头高起播低延时RTSP网页无插件流媒体播放器EasyPlayer-RTSP-Win录像和抓图实现线程优化方案分享

本文主要是介绍海康大华摄像头高起播低延时RTSP网页无插件流媒体播放器EasyPlayer-RTSP-Win录像和抓图实现线程优化方案分享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

EasyPlayer-RTSP播放器是一套RTSP专用的播放器,包括有:Windows(支持IE插件,npapi插件)、Android、iOS三个平台,是由青犀TSINGSEE开放平台开发和维护的区别于市面上大部分的通用播放器,EasyPlayer-RTSP系列从2014年初发展至今得到了各行各业(尤其是安防行业)的广泛应用,其主要原因是EasyPlayer-RTSP更加精炼、更加专注,具备低延时和高RTSP协议兼容性。

EasyPlayerRTSP.png

EasyPlayer-RTSP-Win录像和抓图实现线程优化

测试发现,通过EasyPlayer-RTSP-Win拉取网络摄像机的流, 其音频可能是G711,G726等,而写MP4或者转推RTMP等都不支持这些音频格式,那么我们就需要将其音频转码成AAC,可以使用libEasyAACEncoder库进行转码,然后写MP4或者推送;

然而,在实际应用中,我们发现转码过程其实还是比较耗时的,它甚至会导致解码线程来不及从而使直播延时增大,所以,我们采用队列缓存+线程的方式来优化录像和抓图。

实现如下:

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

if (pThread->manuRecording == 0x01 && NULL==pThread->m_pMP4Writer && frameinfo.type==EASY_SDK_VIDEO_FRAME_I)//开启录制{//EnterCriticalSection(&pThread->critRecQueue);				if (!pThread->m_pMP4Writer){pThread->m_pMP4Writer = new EasyMP4Writer();}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());
#endifEasyAACEncoder_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==EASY_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 (EASY_SDK_AUDIO_CODEC_G711U == frameinfo.codec|| EASY_SDK_AUDIO_CODEC_G726 == frameinfo.codec || EASY_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 == EASY_SDK_AUDIO_CODEC_G711U){initParam.ucAudioCodec = Law_ULaw;} else if (frameinfo.codec == EASY_SDK_AUDIO_CODEC_G726){initParam.ucAudioCodec = Law_G726;}else if (frameinfo.codec == EASY_SDK_AUDIO_CODEC_G711A){initParam.ucAudioCodec = Law_ALaw;}m_pAACEncoderHandle = Easy_AACEncoder_Init( initParam);}unsigned int out_len = 0;int nRet = Easy_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 = EASY_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());
#endifreturn 0;
}

2、抓图原理同录像,唯一区别是直接数据传入线程,进行jpg编码存文件,详见EasyPlayer-RTSP的代码(这个代码大部分是开源的)。

这篇关于海康大华摄像头高起播低延时RTSP网页无插件流媒体播放器EasyPlayer-RTSP-Win录像和抓图实现线程优化方案分享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/930874

相关文章

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

小白也能轻松上手! 路由器设置优化指南

《小白也能轻松上手!路由器设置优化指南》在日常生活中,我们常常会遇到WiFi网速慢的问题,这主要受到三个方面的影响,首要原因是WiFi产品的配置优化不合理,其次是硬件性能的不足,以及宽带线路本身的质... 在数字化时代,网络已成为生活必需品,追剧、游戏、办公、学习都离不开稳定高速的网络。但很多人面对新路由器

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

基于Python实现简易视频剪辑工具

《基于Python实现简易视频剪辑工具》这篇文章主要为大家详细介绍了如何用Python打造一个功能完备的简易视频剪辑工具,包括视频文件导入与格式转换,基础剪辑操作,音频处理等功能,感兴趣的小伙伴可以了... 目录一、技术选型与环境搭建二、核心功能模块实现1. 视频基础操作2. 音频处理3. 特效与转场三、高

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

Python实现中文文本处理与分析程序的示例详解

《Python实现中文文本处理与分析程序的示例详解》在当今信息爆炸的时代,文本数据的处理与分析成为了数据科学领域的重要课题,本文将使用Python开发一款基于Python的中文文本处理与分析程序,希望... 目录一、程序概述二、主要功能解析2.1 文件操作2.2 基础分析2.3 高级分析2.4 可视化2.5