ZLMediaKit中的RingBuffer

前面的文章讲到ZLMediaKit转流,提到过RingBuffer,它是比较核心的数据结构。这篇文章就来讲讲RingBuffer的用法。

RingBuffer的类体系

RingBuffer是由多个类组成,分为两大功能:存储和数据分发。
存储功能由类RingStorage实现,分发功能由RingReaderDispactcher,RingDelegateRingReader)。下面是它们的类图:
ZLMediaServer RingBuffer (1).jpg
RingBuffer类是"大总管",封装整个体系的功能,提供对外的接口。

数据存储

RingStorage是数据存储类,它是一个循环队列,有最大容量定义,从尾部插入最新数据,当队列满了,从头部删除老数据。

它的对所存储的数据的定义,借用了视频GOP的概念。
将GOP视为一个元素(一个视频GOP中包含多个视频nalu)。
RingStorage中将GOP称为更适合,里有包含的更基本的元素,下面是它的定义:

template <typename T>
class _RingStorage 

基本元素为模板类型,可以存入任意类型。

它包含了一个类型为GopType容器,如下:
using GopType = List<List<std::pair<bool, T>>>;
GopType _data_cache;
它是一个List,元素也是一个List。可见是以组为单位存储数据。

在视频数据中,GOP包含的是两个关键帧之间的的nalu数据,所以它的write接口有一个是否为key的参数,如下:

void write(T in, bool is_key = true)

它的构造函数如下:

_RingStorage(size_t max_size, size_t max_gop_size)

max_size是指最大元素个数,就是GOP的数量*GOP的大小。
max_gop_size是指最大GOP的个数。

下面是一个使用示例:

//RingBuffer是_RingStorage的封装
//最大size为100,GOP最大个数为1
RingBuffer<int>::Ptr g_ringBuf(new RingBuffer<int>(100,nullptr,1));
//GOP 011
g_ringBuf->write(0,true);
g_ringBuf->write(1,false);
g_ringBuf->write(1,false);
//GOP 022
g_ringBuf->write(0,true);
g_ringBuf->write(2,false);
g_ringBuf->write(2,false);
//GOP 033
g_ringBuf->write(0,true);
g_ringBuf->write(3,false);
g_ringBuf->write(3,false);

上面的例子将0作为key(当然可以是任意值),两个key之间就是GOP的数据(GOP的长度可以是任意长度)。
因为定义的GOP个数为1,所以buffer最终缓存的是0,3,3。前面的0,1,1,0,2,2都被删除了。
对视频nalu数据来说,**RingStorage**就是一个GOP buffer,缓存最少一个GOP的数据。这样可以保证快速出图。

数据分发

先看RingBuffer的整体结构图

RingBuffer结构图.jpg

RingBuffer中的数据结构std::unordered_map<EventPoller::Ptr, typename RingReaderDispatcher::Ptr, HashOfPtr> _dispatcher_map,是以EventPoller对象为key,所以它可以跨线程的分发数据。

RingReaderDispatcher内有多个RingReader对象,是数据流向的目的端。

RingReader就是数据的出口,调用RingBuffer类的attch方法获取一个RingReader对象,再调用setReadCB方法设置数据回调,就可以取到数据了。

attch有一个EventPoller类型的形参,需要传入的是目的对象所在的线程。

在这篇文章中提到过,MediaSource对象作为数据源,内部都有一个RingBuffer,通过它拿到一个RingReader对象后就可以取到这个MediaSource的源了。

比如,以rtmp推流,http-flv拉流时,那么连接rtmp源和flv的基本代码结构如下:

//poller为_ring_reader对象所在的线程
_ring_reader = media->getRing()->attach(poller);
//获取源信息的回调
_ring_reader->setGetInfoCB(...);
//当源关闭时的回调
_ring_reader->setDetachCB(...);
//设置读取数据的回调
_ring_reader->setReadCB(...);

具体的代码见,void FlvMuxer::start方法。

下面是RingBuffer中数据流转图

ZLMediaKit RingBuffer数据分发图.jpg
通过write写入数据,数据从RingBufferRingReaderDispatcher,再到RingReader,再通过onReadCB回调至dst。

RingBuffer使用的例子

#include <iostream>
#include "Util/logger.h"
#include "Util/util.h"
#include "Util/RingBuffer.h"using namespace std;
using namespace toolkit;//创建一个RingBuffer对象,存储int元素
//最大size为100,缓存(max_gop_size)为1
RingBuffer<int>::Ptr g_ringBuf(new RingBuffer<int>(100,nullptr,1));//数据回调
void onReadEvent1(int i) {std::cout<<i<<std::endl;
}//src释放时的回调
void onDetachEvent(){WarnL;
}int main() {//初始化日志auto fileChannel = std::make_shared<toolkit::FileChannel>("FileChannel", toolkit::exeDir());Logger::Instance().add(fileChannel);Logger::Instance().setWriter(std::make_shared<toolkit::AsyncLogWriter>());//RingBuffer reader线程auto poller1 = EventPollerPool::Instance().getPoller(false);//在线程中设置readerpoller1->async([&]{//通过attach方法获取一个RingReader,设置为使用cacheauto reader = g_ringBuf->attach(poller1,true);//设置数据读取回调reader->setReadCB([](int i){onReadEvent1(i);});//设置src关闭时的回调reader->setDetachCB([](){onDetachEvent();});});//在主线程中写入数据//GOP 011g_ringBuf->write(0,true);g_ringBuf->write(1,false);g_ringBuf->write(1,false);//GOP 022g_ringBuf->write(0,true);g_ringBuf->write(2,false);g_ringBuf->write(2,false);//GOP 033g_ringBuf->write(0,true);g_ringBuf->write(3,false);g_ringBuf->write(3,false);std::this_thread::sleep_for(std::chrono::seconds(10));
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔试强化】Day 8

文章目录 一、单选1.2.3.4.5.6.7.8.9.10. 二、编程1. 求最小公倍数解法&#xff1a;代码&#xff1a; 2. 两种排序方法解法&#xff1a;代码&#xff1a; 一、单选 1. 正确答案&#xff1a;B2. 正确答案&#xff1a;A继承具有传递性 3. 正确答案&#xff1a;C数组不是原生类&…

LaTex中参考文献引用

一、引用参考文献 这里我们使用的是BibTeX的引用格式&#xff0c;因此文件中应包括两个文件&#xff08;.bib-参考文献 和 .bst-文献格式&#xff09;。 有了这两个文件后&#xff0c;我们在bib文件中创建参考文献&#xff1a;&#xff08;注意&#xff0c;作者的名字是逗号前…

4.2 媒资管理模块 - 项目搭建、minio文件系统

文章目录 一、搭建媒资服务工程1.1 media-api 工程1.1.1 bootstrap.yaml1.1.2 Maven1.1.3 Nacos 1.2 media-service 工程1.2.1 bootstrap.yaml1.2.2 Maven1.2.3 Nacos1.2.4 分页插件 1.3 media-model 工程1.3.1 QueryMediaParamsDto1.3.2 MediaFiles1.3.3 MediaProcess1.3.4 Me…

nodejs微信小程序+python+PHP汽车租赁管理网站-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

springboot+vue项目如何在linux上部署

在linux上部署项目&#xff0c;是我们实训项目作业的最后一步&#xff0c;此时我们的项目编码测试已经完成&#xff0c;接下来就需要在服务器上部署上线&#xff0c;那么如何部署上线&#xff0c;接下来我会在虚拟机上的CentOS7系统上实现部署&#xff0c; 一.下载JDK 因为我…

搭建一个高效的Python开发环境

“工欲善其事&#xff0c;必先利其器”&#xff0c;这里我们来搭建一套高效的 Python 开发环境&#xff0c;为后续的数据分析做准备。 关于高效作业&#xff0c;对于需要编写 Python 代码进行数据分析的工作而言&#xff0c;主要涉及两个方面。 1. 一款具备强大的自动完成和错…

Unity-Shader-渲染队列,ZTest,ZWrite

Unity-Shader-渲染队列&#xff0c;ZTest&#xff0c;ZWrite ZTest&#xff08;深度测试&#xff09;和ZWrite&#xff08;深度写入&#xff09;ZTest Less&#xff08;深度小于当前缓存则通过&#xff09;ZTest Greater&#xff08;深度大于当前缓存则通过&#xff09;ZTest L…

Linux--Shell脚本应用实战

实验环境 随着业务的不断发展&#xff0c;某公司所使用的Linux服务器也越来越多。在系统管理和维护过程中&#xff0c;经 常需要编写一些实用的小脚本&#xff0c;以辅助运维工作&#xff0c;提高工作效率。 需求描述 > 编写一个名为getarp.sh的小脚本&#xff0c;记录局域…

宝塔面板Linux服务器CentOS 7数据库mysql5.6升级至5.7版本教程

近段时间很多会员问系统更新较慢&#xff0c;也打算上几个好的系统&#xff0c;但几个系统系统只支持MYSQL5.7版本&#xff0c;服务器一直使用较低的MYSQL5.6版本&#xff0c;为了测试几个最新的系统打算让5.6和5.7并存使用&#xff0c;参考了多个文档感觉这种并存问题会很多。…

第十一节TypeScript Array(数组)

1、描述 数组对象是使用单独的变量名来存储一系列的值。 比如&#xff0c;你现在有一组数据&#xff0c;存单独变量如下&#xff1a; var data1"Android"; var data2"Java"; var data3"Harmony"; 那如果有10、100个这种变量呢&#xff0c;那…

指法练习软件TT

1、说明 这个是90年代后期读书时写的C语言练习软件&#xff0c;模仿当时的打字练习软件。 在技能上使用屏幕直接输出&#xff0c;支持彩色&#xff0c;能够在DOS和Windows98的窗口下运行。 2、主要界面 支持多用户档案&#xff0c;以键盘操作。 进入具体用户档案后&#xff0c…

路由器介绍和命令操作

先来回顾一下上次的内容&#xff1a; ip地址就是由32位二进制数组 二进位数就是只有数字0和1组成 网络位&#xff1a;类似于区号&#xff0c;表示区域作用 主机位&#xff1a;类似于号码&#xff0c;表示区域中编号 网络名称&#xff1a;网络位不变&#xff0c;主机位全为0 …