超分服务的分量保存

news/2024/12/22 16:15:26/

分量说明

    分量的概念主要是对于显卡解码,编码和网络传输而言,显卡可以同时进行几个线程,多个显卡可以分布式计算,对分量进行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 也必须进行修改,适应编码


http://www.ppmy.cn/news/1533264.html

相关文章

Linux 进程的基本概念及描述

目录 0.前言 1. 什么是进程 1.1 进程的定义与特性 1.2 进程与线程的区别 2.描述进程 2.1 PCB (进程控制块) 2.2 task_struct 3.查看进程 3.1 查看进程信息 3.1.1 /proc 文件系统 3.1.2 ps 命令 3.1.2 top 和 htop 命令 3.2 获取进程标识符 3.2.1使用命令获取PID 3.2.2 使用C语言…

书生大模型实战训练营 第三期 入门岛

1.Linux 任务一 完成SSH连接与端口映射并运行hello_world.py vscode自带的端口设置功能很方便 2.Python 任务一 实现wordcount函数 任务二 vscode 单步调试

基于SSM+微信小程序的校园二手数码交易平台系统(二手3)(源码+sql脚本+视频导入教程+文档)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于ssm微信小程序的校园二手数码交易平台满足了不同用户的功能需求&#xff0c;包括用户、卖家以及管理员&#xff0c;下面对这不同用户的功能需求进行简介。 &#xff08;1&#xff09…

01.useStateWithLabel

在使用 React 进行开发时&#xff0c;特别是处理多个 useState() 钩子的情况下&#xff0c;调试过程可能会变得复杂。幸运的是&#xff0c;我们可以使用 useDebugValue() 钩子创建一个自定义的 useStateWithLabel 钩子&#xff0c;从而轻松地为这些值添加标签。这种方法可以显著…

828华为云征文|使用Flexus X实例创建FDS+Nginx服务实现图片上传功能

一、Flexus X实例 什么是Flexus X实例呢&#xff0c;这是华为云最新推出的云服务器产品&#xff0c;如下图&#xff1a; 华为云推出的Flexus云服务器X系列&#xff0c;是在华为顶尖技术团队&#xff0c;特别是荣获国家科技进步奖的领军人物顾炯炯博士及其团队的主导下精心研发…

Selenium(1)-webUI自动化环境部署,基本元素定位

web自动化测试环境部署 在正式开启自动化测试之前&#xff0c;我们需要给自动化提供完备的测试环境&#xff0c;需要我们搭建一套完整的运行环境&#xff0c;以便能模拟用户的行为。 首先&#xff0c;我们需要安装python编译器&#xff0c;一个编写代码的工具&#xff0c;如py…

【论文阅读】基于真实数据感知的模型功能窃取攻击

摘要 目的 模型功能窃取攻击是人工智能安全领域的核心问题之一&#xff0c;目的是利用有限的与目标模型有关的信息训练出性能接近的克隆模型&#xff0c;从而实现模型的功能窃取。针对此类问题&#xff0c;一类经典的工作是基于生成模型的方法&#xff0c;这类方法利用生成器…

【JAVA开源】基于Vue和SpringBoot的美容院管理系统

本文项目编号 T 055 &#xff0c;文末自助获取源码 \color{red}{T055&#xff0c;文末自助获取源码} T055&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…