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

相关文章

Spring使用@Retryable实现自动重试机制

《Spring使用@Retryable实现自动重试机制》在微服务架构中,服务之间的调用可能会因为一些暂时性的错误而失败,例如网络波动、数据库连接超时或第三方服务不可用等,在本文中,我们将介绍如何在Sp... 目录引言1. 什么是 @Retryable?2. 如何在 Spring 中使用 @Retryable

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。