超分服务的分量保存
分量说明
分量的概念主要是对于显卡解码,编码和网络传输而言,显卡可以同时进行几个线程,多个显卡可以分布式计算,对分量进行AI识别,比如我们有cuda的显卡,cuda的核心量可以分给不同的分片视频,第一步先将视频减小,第二部分割视频。对于小视频片而言,不同的智能盒子也可以接收网络传输来进行接收数据,进行并行识别服务。这就是我说的分量服务的概念。
采样
在超分服务中,上采样和下采样是两个重要的操作,分别用于增加和减少图像的分辨率。我们在下采样后存储文件和传输,可以显著减少存储的量,同时减少网络的传输量,而接收端收到以后再进行上采样放大,同时进行AI 分析
超分服务说明
实现超分服务,将实时视频能够缩小并且传输和保存,同时需要将文件分成片,同时保证每个文件的最后一帧和下一个文件的第一帧相同,还需要解决两个问题
文件切片是问了能够分布式传输出去,同时本地如果有多个显卡,可以同时进行文件的搜索,比如再多个文件中同时启动AI服务,搜索同一个人脸。
采用rtsp,rtmp服务器接收,rtmp服务器在接收文件时保存为flv文件,为了不污染任何代码,不采用开源的各种服务,使用c++20 去写rtmp和rtsp服务器,这个花了两天时间,协议没啥问题,结果反而时卡在了文件保存上面,下面具体说几个问题,主要体现在时间戳上面。
以下为flv文件保存的要素,首先时flv 头部,在头部中查找0x17 和 0x00 0x01
*/
//0x17 key 00(AVCPacketType ) 00 表示 是AVCDecoderConfigurationRecord
//0x17 key 01(AVCPacketType ) 01 表示 raw data ->nalu
//0x27 not key 01 01 表示 raw data ->nalu
//FLV head 9 bytes
//4 bytes previous tag
//tag data —>11 bytes head -> 5 bytes video head -> nalu data
//4 bytes previous tag
//tag data
熟悉flv文件格式的人一看就懂,无需多言。
1 关键帧问题
2 时间戳问题
关键帧问题
必须保证一个文件第一帧一定为关键帧,所以在分割视频的时候必须能够拿到关键帧的时候才能分割,为了能够保证未丢失文件,上一个文件的最后一帧为下一个文件的第一帧,否则会有很多依靠关键帧解码的p帧b帧无法解码,变成比较难受的绿色,也有可能为绿加黑。
分量保存的时间戳
看下图,

显然除了第一个文件是正确的,但除了第一个分量文件,其他文件第一帧的时间戳是不对的,这是因为AVC sequence header 总是零,vlc播放的时候计算的时间就不正确了,那么就有两个方法:
1 是修改 sequence header的时间戳,
2 是修改每一帧时间戳,
这里有一个问题要说明,就是整体直播出去的时候关键帧的时间戳肯定是对的,为了让文件比较正常,采取修改每一帧时间戳。
总结一下flv头部, 11 个字节头部后,如果是视频,加5个字节的扩展,后面就是nalu数据,如果是音频,加2个字节的扩展,视频5个字节里面第一个就是判别是关键帧和非关键帧的紧要,这里简单一点先用0x17 0x27来判别,注意实际上不是这样,只有h264才是这个值,先找定时间戳,假定我们从协议里面获取的时间戳是正确的,看下面的代码
//11 个字节头部
static void pack_tag_header(uint8_t *buf, uint8_t type, uint32_t data_size, uint32_t timestamp) {//8 audio ; 9 video ; 18 script//8 is the most if (type == 8 || type == 9 || type == 18){*buf++ = type;//one bytesbuf = write_be_ui24(buf, data_size); //three bytesbuf = write_be_ui24(buf, timestamp & 0xffffff);//three bytes*buf++ = timestamp >> 24; //one bytesbuf = write_be_ui24(buf, 0); //three bytes}
}//flv header length is 11
//type 8:audio, 9:video, 18:script meta
static void pack_tag(uint8_t* header, ptr_s_memory mem, uint8_t type, uint32_t timestamp) {pack_tag_header(header, type, (uint32_t)mem->v_len, timestamp);uint8_t* p = mem->v_data_r + mem->v_len;//last write the frame length ,it must include the header lengthwrite_be_ui32(p, FLV_TAG_HEADER_LEN + (uint32_t)mem->v_len);
}
1 2 3 4 5 6 7 8 9 10 11
09 xx xx xx 00 7c 79 00 00 00 00
11 个flv字节头部里面有四个字节包含了时间戳,第5个字节到第8,也是我们自己的代码写入的,我们要做的就是重写时间戳,但是不能修改传入的tag数组,这是外面传输出去要用的
0 和 1 之间无缝衔接,同时每个文件的时间长度和时间戳都保证正确


开头和结尾衔接

相邻两个文件开头和结尾为同一帧
code
主要就是需要重新改写时间戳,直接看代码
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <string>#include "c_hub.h"
#include "util_flv_pack.h"
//flv 文件读写
class c_flv_writer
{FILE* v_fp = NULL;int64_t v_num = 0;uint32_t v_hash = 0;std::string v_deviceurl;uint32_t v_record_timestamp = 0;
public:ptr_s_memory v_head_video = nullptr;ptr_s_memory v_head_audio = nullptr;int v_frame_count = 2000;int v_frame_record = 0;int v_inited = 0;protected:std::string GetFileName(){//判断v_deviceurl是否"/"结尾std::string name = v_deviceurl + std::to_string(v_hash);name +="_" + std::to_string(v_num);name += ".flv";v_num++;return name;}
public:void initStart(std::string deviceurl, uint32_t hash, ptr_s_memory v, ptr_s_memory a){v_hash = hash;v_deviceurl = deviceurl;v_head_video = v;v_head_audio = a;v_inited = 1;}static void modify_timestamp(uint8_t* buf, uint32_t timestamp) {buf = buf + 4; // write_be_ui24(buf, data_size); //three bytesbuf = write_be_ui24(buf, timestamp & 0xffffff);//three bytes*buf++ = timestamp >> 24; //one bytes}int writeStart(uint32_t ts){if (v_fp == NULL){v_frame_record = 0;std::string name = GetFileName();v_fp = fopen(name.c_str(), "wb+");if (v_fp == NULL)return -1;fwrite(FLV_HEADER_BUF_13, 13, 1, v_fp);//video headif (v_head_video != nullptr){uint8_t* data_v = v_head_video->v_data_h; //flvhub->v_cache_hv->v_data_h;size_t len_v = v_head_video->v_len + 11 + 4;fwrite(data_v, len_v, 1, v_fp);}//audio headif (v_head_audio != nullptr){uint8_t* data_a = v_head_audio->v_data_h;size_t len_a = v_head_audio->v_len + 11 + 4;fwrite(data_a, len_a, 1, v_fp);}}v_record_timestamp = ts;return 0;}void writeData(uint8_t* tag, int taglen, uint8_t* data, size_t len, uint32_t ts){if (v_fp == NULL)writeStart(ts);//遇到关键帧才能重新开始if (v_fp != NULL /*&& v_frame_record < v_frame_count*/){uint8_t a = *data;uint8_t b = *(data + 1);if (v_frame_record > v_frame_count && ((a == 0x17) && (b == 0x01))){//需要重复最后一帧放开uint8_t newtag[11];memcpy(newtag, tag, 11);uint32_t nowts = ts - v_record_timestamp;modify_timestamp(newtag, nowts);fwrite(newtag, taglen, 1, v_fp);fwrite(data, len, 1, v_fp);fclose(v_fp);v_fp = NULL;std::cout << "close the file now" << std::endl;writeStart(ts);}uint8_t newtag[11];memcpy(newtag, tag, 11);uint32_t nowts = ts - v_record_timestamp;modify_timestamp(newtag, nowts);fwrite(newtag, taglen, 1, v_fp);fwrite(data, len, 1, v_fp);v_frame_record++;std::cout << "write the number " << v_frame_record << std::endl;}}
};
调用
调用的时候放在音视频接收以后并且下采样结束的地方
if (flvhub->v_flv_w.v_inited == 0)
{flvhub->v_flv_w.initStart("./", hash, flvhub->v_cache_hv, flvhub->v_cache_ha);flvhub->v_flv_w.v_inited = 1;
}
flvhub->v_flv_w.writeData(tag,taglen, mem->v_data_r ,len, mem->v_ts);
其他编码
由于rtmp协议已经加入enhanced 扩展,rtmp/flv已经有统一支持H265的国际版本,我后面会修改rtmp server,加入对h265的支持,那么这边存储flv 也必须进行修改,适应编码
相关文章:
超分服务的分量保存
分量说明 分量的概念主要是对于显卡解码,编码和网络传输而言,显卡可以同时进行几个线程,多个显卡可以分布式计算,对分量进行AI识别,比如我们有cuda的显卡,cuda的核心量可以分给不同的分片视频,第…...
Windows11系统下SkyWalking环境搭建教程
目录 前言SkyWalking简介SkyWalking下载Agent监控实现启动配置SkyWalking启动Java应用程序启动Elasticsearch安装总结 前言 本文为博主在项目环境搭建时记录的SkyWalking安装流程,希望对大家能够有所帮助,不足之处欢迎批评指正🤝ᾑ…...
前端BOM常用操作
BOM操作常用命令详解及代码案例 BOM(Browser Object Model)是浏览器对象模型,是浏览器提供的JavaScript操作浏览器的API。BOM提供了与网页无关的浏览器的功能对象,虽然没有正式的标准,但现代浏览器已经几乎实现了Java…...
【Go】-viper库的使用
目录 viper简介 viper使用 通过viper.Set设置值 读取配置文件说明 读取配置文件 读取多个配置文件 读取配置项的值 读取命令行的值 io.Reader中读取值 写配置文件 WriteConfig() 和 SafeWriteConfig() 区别: viper简介 配置管理解析库,是由大神 Steve Fr…...
JavaWeb酒店管理系统(详细版)
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
C++ | 定长内存池 | 对象池
文章目录 C | 定长内存池 | 对象池一、内存池的引入二、代码中的内存池实现 - ObjectPool类(一)整体结构(二)内存分配 - New函数(三)内存回收 - Delete函数 三、内存池在TreeNode示例中的性能测试演示四、脱…...
python画图|自制渐变柱状图
在前述学习过程中,我们已经通过官网学习了如何绘制渐变的柱状图及其背景。 掌握一门技能的最佳检验方式就是通过实战,因此,本文尝试做一些渐变设计。 前述学习记录可查看链接: Python画图|渐变背景-CSDN博客 【1】柱状图渐变 …...
基于RPA+BERT的文档辅助“悦读”系统 | OPENAIGC开发者大赛高校组AI创作力奖
在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…...
K8S部署流程
一、war打包镜像(survey,analytics,trac系统) 代码打包成war准备tomcat的server.xml文件,修改connector中8080端口为项目的端口 修改前: <Connector port"8080" protocol"HTTP/1.1"connectionTimeout"20000"redirect…...
DevExpress WinForms中文教程:Data Grid - 如何添加或删除行?
本教程介绍DevExpress WinForm的Data Grid控件UI元素和API,它们使您和最终用户能够添加或删除数据行。您将首选学习如何启用内置的数据导航器,然后学习如何使用Microsoft Outlook启发的New Item行添加新记录。最后教程将向您展示基本的API,它…...
u盘格式化后数据能恢复吗?2024年Top4恢复神器来帮忙
在这个电脑和手机满天飞的时代,U盘是我们用来存东西和传文件的得力助手,特别重要。但是,有时候U盘可能会不小心被格式化了,里面的重要文件就不见了。那么,U盘格式化后的数据还能恢复吗?当然可以。今天会告诉…...
深度学习·Argparse
Argparse 命令行选项、参数和子命令解析器 ArgumentParser 命令行传参数->解析参数->获得对应参数 初始化:parser argparse.ArgumentParser(descriptionxxx)添加命令行参数: parser.add_argument("--training_filepath", typestr, he…...
制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析
制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析 新华社9月23日消息,据全国组织机构统一社会信用代码数据服务中心统计,我国制造业企业总量突破600万家。数据显示,2024年1至8月,我国制造业企业数量呈现稳…...
http协议中的header详细讲解
http协议中的header详细讲解 HTTP 协议和 TCP/IP 协议族内的其他众多的协议相同,用于客户端和服务器之间的通信。 请求访问文本或图像等资源的一端称为客户端,而提供资源响应的一端称为服务器端。 HTTP 协议规定,请求从客户端发出…...
探索后量子安全:基于格加密技术的未来密码学展望
在信息技术日新月异的今天,量子计算作为下一代计算技术的代表,正逐步从理论走向实践。量子计算的出现对现有的加密体系构成了严重威胁,尤其是基于大数分解和离散对数难题的传统密码学(如RSA和Diffie-Hellman协议)。为了…...
WPF之UI进阶--完整了解wpf的控件和布局容器及应用
前面三篇有关WPF的基础介绍,分别介绍了wpf与winform的异同,wpf的事件生成和使用以及数据绑定。但我们还缺乏一副好的“皮囊”,所以从这篇开始我们来开始学习wpf的UI相关的内容,首当其冲的就是布局容器。 其实我们知道,…...
unity一键注释日志和反注释日志
开发背景:游戏中日志也是很大的开销,虽然有些日志不打印但是毕竟有字符串的开销,甚至有字符串拼接的开销,有些还有装箱和拆箱的开销,比如Debug.Log(1) 这种 因此需要注释掉,当然还需要提供反注释的功能&am…...
VBA数据库解决方案第十五讲:Recordset集合中单个数据的精确处理
《VBA数据库解决方案》教程(版权10090845)是我推出的第二套教程,目前已经是第二版修订了。这套教程定位于中级,是学完字典后的另一个专题讲解。数据库是数据处理的利器,教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…...
甄选范文“论软件需求管理”,软考高级论文,系统架构设计师论文
论文真题 软件需求管理是一个对系统需求变更了解和控制的过程。需求管理过程与需求开发过程相互关联,初始需求导出的同时就要形成需求管理规划,一旦启动了软件开发过程,需求管理活动就紧密相伴。 需求管理过程中主要包含变更控制、版本控制、需求跟踪和需求状态跟踪等4项活…...
Android Studio Dolphin 中Gradle下载慢的解决方法
我用的版本Android Studio Dolphin | 2021.3.1 Patch 1 1.Gradle自身的版本下载慢 解决办法:修改gradle\wrapper\gradle-wrapper.properties中的distributionUrl 将https\://services.gradle.org/distributions为https\://mirrors.cloud.tencent.com/gradle dis…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
