SkeyeLive libSkeyePlayer中的回调机制介绍

2024-03-02 01:08

本文主要是介绍SkeyeLive libSkeyePlayer中的回调机制介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

经常我们会在流媒体推送端提到“数据回调”这个词,在多媒体编程中,我们会比较常用到线程数据回调,在SkeyeClient管理类代码中用到了两个数据回调函数,分别是DShow原始音视频数据采集回调函数和SkeyeRTSPClient网络接收线程中回调音视频编码数据回调函数;虽然两者采集到的数据不同,但是我们的用途是一致的,都是用来推送,所以我们通常会用一个数据回调管理函数来进行统一管理。

int CSourceManager::SourceManager(int _channelId, int *_channelPtr, int _frameType, char *pBuf, RTSP_FRAME_INFO* _frameInfo)

一、DirectShow采集库中的回调
DirectShow采集库中的回调机制在我的另一篇文章SkeyeDarwin SkeyeLive中DirectShow采集音视频流程及几种采集方式介绍中第三点提到过,两种模式都是通过统一的设置回调函数接口函数实现:

	virtual void WINAPI SetDShowCaptureCallback(RealDataCallback realDataCalBack, void* pMaster) = 0;

回调函数的设置函数通常都带有一个设置参数,该设置参数通常是一个指针变量,主要用于在回调函数体中进行调用控制;最常用的做法是:将其设置为当前类的实例指针this,通过该指针调用不同的实例类的处理函数对回调数据进行处理。

二、libSkeyePlayer库中的回调

libSkeyePlayer库提供的设置回调函数的接口主要来自其所依赖的库SkeyeRTSPClient,该回调函数主要是回调网络接收的Rtsp流解析的音视频编码流数据,用于转发或者解码播放;由于libSkeyePlayer库(及其依赖库)均不是本人的作品(libSkeyePlayer库及其依赖库的作者是SkeyeDarwin团队的Gavin大神,向大神致敬~~~!!!),所以,我对这个库也只有大致的了解,如果有理解不对或者不合理的地方,欢迎指正,大家相互学习!

1、网络Rtsp流回调
流回调函数在SkeyeClient中提供了设置接口函数,底层用libSkeyePlayer提供的接口函数中进行设置,对应SkeyeRTSPClient库提供的接口函数进行设置,三者对应的程序代码如下:
SkeyeLive中的回调设置函数接口:

int SkeyePlayerManager::Start(char* szURL, HWND hShowWnd, RENDER_FORMAT eRenderFormat, int rtpovertcp, const char *username, const char *password, MediaSourceCallBack callback, void *userPtr) 
{//Stopif (m_sSourceInfo.rtspSourceId > 0){Close();return -1;}m_sSourceInfo.rtspSourceId = SkeyePlayer_OpenStream(szURL, hShowWnd, eRenderFormat, rtpovertcp, username, password, callback, userPtr);return	m_sSourceInfo.rtspSourceId ;
}

libSkeyePlayer中主要的功能在类CChannelManager中实现,该类提供了回调设置函数接口:

int	CChannelManager::OpenStream(const char *url, HWND hWnd, RENDER_FORMAT renderFormat, int _rtpovertcp, const char *username, const char *password, MediaSourceCallBack callback, void *userPtr)
{if (NULL == pRealtimePlayThread)			return -1;if ( (NULL == url) || (0==strcmp(url, "\0")))		return -1;int iNvsIdx = -1;EnterCriticalSection(&crit);do{for (int i=0; i<MAX_CHANNEL_NUM; i++){if (NULL == pRealtimePlayThread[i].nvsHandle){iNvsIdx = i;break;}}if (iNvsIdx == -1)		break;SkeyeRTSP_Init(&pRealtimePlayThread[iNvsIdx].nvsHandle);if (NULL == pRealtimePlayThread[iNvsIdx].nvsHandle)		break;	//退出while循环unsigned int mediaType = MEDIA_TYPE_VIDEO;mediaType |= MEDIA_TYPE_AUDIO;		//换为NVSource, 屏蔽声音SkeyeRTSP_SetCallback(pRealtimePlayThread[iNvsIdx].nvsHandle, __RTSPSourceCallBack);SkeyeRTSP_OpenStream(pRealtimePlayThread[iNvsIdx].nvsHandle, iNvsIdx, (char*)url, _rtpovertcp==0x01?RTP_OVER_TCP:RTP_OVER_UDP, mediaType, (char*)username, (char*)password, (int*)&pRealtimePlayThread[iNvsIdx], 1000, 0);pRealtimePlayThread[iNvsIdx].pCallback = callback;pRealtimePlayThread[iNvsIdx].pUserPtr = userPtr;pRealtimePlayThread[iNvsIdx].hWnd = hWnd;pRealtimePlayThread[iNvsIdx].renderFormat = (D3D_SUPPORT_FORMAT)renderFormat;CreatePlayThread(&pRealtimePlayThread[iNvsIdx]);}while (0);LeaveCriticalSection(&crit);if (iNvsIdx >= 0)	iNvsIdx += CHANNEL_ID_GAIN;return iNvsIdx;
}

其中,调用的SkeyeRTSPClient库函数接口SkeyeRTSP_SetCallback(pRealtimePlayThread[iNvsIdx].nvsHandle, __RTSPSourceCallBack);实现网络流编码数据的回调函数的设置;在以上代码中,作者除了初始化SkeyeRTSPClient库的操作以及设置回调函数外,还创建了两个线程,分别用于解码和播放;

void CChannelManager::CreatePlayThread(PLAY_THREAD_OBJ	*_pPlayThread)
{if (NULL == _pPlayThread)		return;if (_pPlayThread->decodeThread.flag == 0x00){//_pPlayThread->ch_tally = 0;_pPlayThread->decodeThread.flag = 0x01;_pPlayThread->decodeThread.hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_lpDecodeThread, _pPlayThread, 0, NULL);while (_pPlayThread->decodeThread.flag!=0x02 && _pPlayThread->decodeThread.flag!=0x00)	{Sleep(100);}if (NULL != _pPlayThread->decodeThread.hThread){SetThreadPriority(_pPlayThread->decodeThread.hThread, THREAD_PRIORITY_HIGHEST);}}if (_pPlayThread->displayThread.flag == 0x00){_pPlayThread->displayThread.flag = 0x01;_pPlayThread->displayThread.hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_lpDisplayThread, _pPlayThread, 0, NULL);while (_pPlayThread->displayThread.flag!=0x02 && _pPlayThread->displayThread.flag!=0x00)	{Sleep(100);}if (NULL != _pPlayThread->displayThread.hThread){SetThreadPriority(_pPlayThread->displayThread.hThread, THREAD_PRIORITY_HIGHEST);}}
}

需要重要说明的是,在类CChannelManager中一个重要的成员结构体指针变量pRealtimePlayThread,该变量是贯穿整个程序流程,涉及到网络流数据接收线程->编码数据回调||缓存->写文件||解码线程->播放线程 整个流程,在其中扮演了重要的角色,结构体原型如下:

typedef struct __PLAY_THREAD_OBJ
{THREAD_OBJ		decodeThread;		//解码线程THREAD_OBJ		displayThread;		//显示线程Skeye_RTSP_Handle		nvsHandle;HWND			hWnd;				//显示视频的窗口句柄int				channelId;			//通道号int				showStatisticalInfo;//显示统计信息int				frameCache;		//帧缓存(用于调整流畅度),由上层应用设置int				initQueue;		//初始化队列标识SS_QUEUE_OBJ_T	*pAVQueue;		//接收rtsp的帧队列int				frameQueue;		//队列中的帧数int				findKeyframe;	//是否需要查找关键帧标识int				decodeYuvIdx;DWORD			dwLosspacketTime;	//丢包时间DWORD			dwDisconnectTime;	//断线时间DECODER_OBJ		decoderObj[MAX_DECODER_NUM];D3D_HANDLE		d3dHandle;		//显示句柄D3D_SUPPORT_FORMAT	renderFormat;	//显示格式int				ShownToScale;		//按比例显示int				decodeKeyFrameOnly;	//仅解码显示关键帧unsigned int	rtpTimestamp;LARGE_INTEGER	cpuFreq;		//cpu频率_LARGE_INTEGER	lastRenderTime;	//最后显示时间int				yuvFrameNo;		//当前显示的yuv帧号YUV_FRAME_INFO	yuvFrame[MAX_YUV_FRAME_NUM];CRITICAL_SECTION	crit;bool			resetD3d;		//是否需要重建d3dRenderRECT			rcSrcRender;D3D9_LINE		d3d9Line;char			manuRecordingFile[MAX_PATH];int				manuRecording;MP4C_Handler	mp4cHandle;int				vidFrameNum;MediaSourceCallBack pCallback;void			*pUserPtr;
}PLAY_THREAD_OBJ;

其中,编码数据和解码数据分别缓存在队列结构pAVQueue和数组yuvFrame中,程序中用这个结构做了2级缓存,保证接收和解码播放过程的流畅性,其中,解码数据缓存只有3帧,确保播放的实时性,当然在机器性能或者网络资源不够的情况下可能出现卡帧或者花屏的情况,当然,程序中采用了先进的丢帧机制,确保花屏的情况最大限度的减少。

此外,pRealtimePlayThread这个结构指针,创建的是一个最大64的数组,也就是相当于的64个CChannelManager类的实例的Player对应的处理(当然,类实例只有一个CChannelManager *g_pChannelManager);对于libSkeyePlaer库的这种处理方式可能部分程序员不太理解(包括我~~~哈哈哈),但是其中确实有很多处理精妙的地方值得我们去学习,所以,要读懂libSkeyePlaer库中的代码,熟悉和理解pRealtimePlayThread这个变量的处理妙用即可!

这篇关于SkeyeLive libSkeyePlayer中的回调机制介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/764450

相关文章

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Spring Boot 中的默认异常处理机制及执行流程

《SpringBoot中的默认异常处理机制及执行流程》SpringBoot内置BasicErrorController,自动处理异常并生成HTML/JSON响应,支持自定义错误路径、配置及扩展,如... 目录Spring Boot 异常处理机制详解默认错误页面功能自动异常转换机制错误属性配置选项默认错误处理

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

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

zookeeper端口说明及介绍

《zookeeper端口说明及介绍》:本文主要介绍zookeeper端口说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、zookeeper有三个端口(可以修改)aVNMqvZ二、3个端口的作用三、部署时注意总China编程结一、zookeeper有三个端口(可以

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

Python中win32包的安装及常见用途介绍

《Python中win32包的安装及常见用途介绍》在Windows环境下,PythonWin32模块通常随Python安装包一起安装,:本文主要介绍Python中win32包的安装及常见用途的相关... 目录前言主要组件安装方法常见用途1. 操作Windows注册表2. 操作Windows服务3. 窗口操作