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

相关文章

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

MybatisPlus service接口功能介绍

《MybatisPlusservice接口功能介绍》:本文主要介绍MybatisPlusservice接口功能介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录Service接口基本用法进阶用法总结:Lambda方法Service接口基本用法MyBATisP

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

Pytorch介绍与安装过程

《Pytorch介绍与安装过程》PyTorch因其直观的设计、卓越的灵活性以及强大的动态计算图功能,迅速在学术界和工业界获得了广泛认可,成为当前深度学习研究和开发的主流工具之一,本文给大家介绍Pyto... 目录1、Pytorch介绍1.1、核心理念1.2、核心组件与功能1.3、适用场景与优势总结1.4、优

Java实现本地缓存的常用方案介绍

《Java实现本地缓存的常用方案介绍》本地缓存的代表技术主要有HashMap,GuavaCache,Caffeine和Encahche,这篇文章主要来和大家聊聊java利用这些技术分别实现本地缓存的方... 目录本地缓存实现方式HashMapConcurrentHashMapGuava CacheCaffe

Jvm sandbox mock机制的实践过程

《Jvmsandboxmock机制的实践过程》:本文主要介绍Jvmsandboxmock机制的实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景二、定义一个损坏的钟1、 Springboot工程中创建一个Clock类2、 添加一个Controller