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);
    //在线程中设置reader
    poller1->async([&]{
        //通过attach方法获取一个RingReader,设置为使用cache
        auto reader = g_ringBuf->attach(poller1,true);
        //设置数据读取回调
        reader->setReadCB([](int i){
            onReadEvent1(i);
        });
    	//设置src关闭时的回调
        reader->setDetachCB([](){
            onDetachEvent();
        });
    });

    //在主线程中写入数据
    //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);

    std::this_thread::sleep_for(std::chrono::seconds(10));
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图形图像处理车牌识别系统设计matlab

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;车牌识别 获取完整源码源文件论文报告 一、 摘要: 随这图形图像技术的发展&#xff0c;现在的车牌识别技术准确率越来越高&#xff0c;识别速度越来越快。无论何种形式的车牌识别系统&#xff0c;它们都是由触发、图像采…

【JavaWeb学习笔记】15 - jQuery

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/jquery 目录 零、官方文档 一、jQuery基本介绍 1.基本介绍 2.原理图 二、JQuery入门使用 1.下载JQuery 2.jQuery快速入门 三、jQuery对象 1.什么是jQuery对象? 2.DOM对象转换成jQuery对象 …

电子电器架构(E/E)演化 —— 主流主机厂域集中架构概述

电子电器架构(E/E)演化 —— 主流主机厂域集中架构概述 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。…

2008-2021年商业银行数据(农商行、城商行、国有行、股份制银行)

2008-2021年商业银行数据&#xff08;农商行、城商行、国有行、股份制银行&#xff09; 1、时间&#xff1a;2008-2021年 2、范围&#xff1a;1700银行 3 、指标&#xff1a;证券简称、year、证券代码、资产总计、负债合计、所有者权益合计、利润总额、净利润、贷款总额、存…

【六大排序详解】中篇 :选择排序 与 堆排序

选择排序 与 堆排序 选择排序 选择排序 与 堆排序1 选择排序1.1 选择排序原理1.2 排序步骤1.3 代码实现 2 堆排序2.1 堆排序原理2.1.1 大堆与小堆2.1.2 向上调整算法2.1.3 向下调整算法 2.2 排序步骤2.3 代码实现 3 时间复杂度分析 Thanks♪(&#xff65;ω&#xff65;)&#…

你真的理解了阻塞和非阻塞、同步和异步吗?

阻塞和非阻塞是一种状态&#xff0c;关键要看调用线程有没有被挂起。以处理I/O为例&#xff0c;如果是调用线程处理阻塞型I/O&#xff0c;那么调用线程会被挂起&#xff0c;此时调用线程就是阻塞的&#xff1b;如果调用线程处理的是非阻塞I/O&#xff0c;调用线程开启了I/O之后…

【Spring】15 MessageSourceAware 接口

文章目录 1. 简介2. 功能3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 资源文件3.4 创建启动类3.5 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点&#xff0c;其中之一是 Bean 生命周期中的回调接口。本文将专注介绍一个与国际化相关的接口 MessageSourceAw…

运筹视角下,体系化学习机器学习算法原理的实践和总结

文章目录 引言目标设计目标实践文章汇总经验总结一则预告 引言 上两周总结了我在体系化学习运筹学基础知识方面的个人经验&#xff0c;看过那篇文章的人可能知道&#xff0c;今年我还花了很多时间学习机器学习中各种模型的算法原理。 在工业应用中&#xff0c;机器学习和运筹…

Spark中使用scala完成数据抽取任务 -- 总结

如题 任务二&#xff1a;离线数据处理&#xff0c;校赛题目需要使用spark框架将mysql数据库中ds_db01数据库的user_info表的内容抽取到Hive库的user_info表中&#xff0c;并且添加一个字段设置字段的格式 第二个任务和第一个的内容几乎一样。 在该任务中主要需要完成以下几个阶…

【python】python课设 天气预测数据分析及可视化(完整源码)

目录 1. 前言2. 项目结构3. 详细介绍3.1 main.py3.2 GetModel.py3.3 GetData.py3.4 ProcessData.py3.5天气网.html 4. 成果展示 1. 前言 本文介绍了天气预测数据分析及可视化的实现过程使用joblib导入模型和自定义模块GetModel获取模型&#xff0c;输出模型的MAE。使用pyechart…

鸿蒙应用开发 自定义下拉刷新动画

1 概述 属性动画&#xff0c;是最为基础的动画&#xff0c;其功能强大、使用场景多&#xff0c;应用范围较广。常用于如下场景中&#xff1a; 一、页面布局发生变化。例如添加、删除部分组件元素。二、页面元素的可见性和位置发生变化。例如显示或者隐藏部分元素&#xff0c;…

基于 Webpack 插件体系的 Mock 服务

背景 在软件研发流程中&#xff0c;对于前后端分离的架构体系而言&#xff0c;为了能够更快速、高效的实现功能的开发&#xff0c;研发团队通常来说会在产品原型阶段对前后端联调的数据接口进行结构设计及约定&#xff0c;进而可以分别同步进行对应功能的实现&#xff0c;提升研…

LINUX系统安装和管理

目录 一.应用程序 对比应用程序与系统命令的关系 典型应用程序的目录结构 常见的软件包装类型 二.RPM软件包管理 1.RPM是什么&#xff1f; 2.RPM命令的格式 查看已安装的软件包格式 查看未安装的软件包 3.RPM安装包从哪里来&#xff1f; 4.挂载的定义 挂载命令moun…

C语言蛇形矩阵

文章目录 每日一言题目解题思路全部代码结语 每日一言 山有榛&#xff0c;隰有苓。云谁之思&#xff1f;西方美人。 --邶风简兮 题目 解题思路 话不多说&#xff0c;直接看图 通过观察图表&#xff0c;我想到了这种方法&#xff1a; 我将数字放置的位置分为两大类&#xff…

Python深度学习028:神经网络模型太多,傻傻分不清?

文章目录 深度学习网络模型常见CNN网络深度学习网络模型 在深度学习领域,有许多常见的网络模型,每种模型都有其特定的应用和优势。以下是一些广泛使用的深度学习模型: 卷积神经网络(CNN): 应用:主要用于图像处理,如图像分类、物体检测。 特点:利用卷积层来提取图像特…

【UML】第12篇 序列图(1/2)——基本概念和构成

目录 一、什么是序列图&#xff08;Sequence Diagram&#xff09; 1.1 定义 1.2 主要用途 1.3 序列图和BPMN的区别和联系 二、序列图的构成 2.1 对象 2.2 生命线 2.3 消息 2.4 激活 序列图&#xff0c;是我个人认为的用处最多的一种图。产品和研发的同学&#xff0c;都…

WorkPlus一站式协同解决方案,助力企业降本增效

在企业数字化转型的过程中&#xff0c;很多企业都会遇到一个共同问题&#xff1a;重复建设基础功能&#xff0c;耗费大量时间和资源。为解决这一难题&#xff0c;WorkPlus已经将一些通用、基础且有技术门槛的功能进行了集成与开发&#xff0c;如IM&#xff08;即时通讯&#xf…

截断整型提升算数转换

文章目录 &#x1f680;前言&#x1f680;截断&#x1f680;整型提升✈️整型提升是怎样的 &#x1f680;算术转换 &#x1f680;前言 大家好啊&#xff01;这里阿辉补一下前面操作符遗漏的地方——截断、整型提升和算数转换 看这一篇要先会前面阿辉讲的数据的存储否则可能看不…

“C语言“——scanf()、getchar() 、putchar()、之间的关系

scanf函数说明 scanf函数是对来自于标准输入流的输入数据作格式转换&#xff0c;并将转换结果保存至format后面的实参所指向的对象。 而const char*format 指向的字符串为格式控制字符串&#xff0c;它指定了可输入的字符串以及赋值时转换方法。 简单来说给一个打印格式(输入…

css radial-gradient 径向渐变基本语法与使用

在之前的文章《深入理解Css linear-gradient线性渐变》我们了解了CSS中的线性渐变&#xff0c;本文将介绍CSS中的另一种渐变———径向渐变&#xff08;Radial Gradient&#xff09;&#xff1a; CSS中的径向渐变&#xff08;Radial Gradient&#xff09;允许你创建从一个颜色…