H5播放之Rtsp转Websocket点播录像抓拍

2023-11-17

H5播放之Rtsp转Websocket点播录像抓拍

广大网友们,很久没上CSDN了,暨上次RTSP转HLS文章发布以来,一直还有一个问题没有解决:如何避免HLS切片带来的不可避免的高延时

HLS的延时

广大网友们,很久没上CSDN了,暨上次RTSP转HLS文章发布以来,一直还有一个问题没有解决:如何避免HLS切片带来的不可避免的高延时

HLS因为协议本身问题,所以会带来较高的延时,延时引起原因可能包括
(1)HLS的TS切片必须要等到关键帧才能切下一个片,导致TS跨度大延时大。
(2)不同厂家的GOP图片分组序列不一样,如海康为50,导致切片较大,延时大。
(3)HLS协议实际是浏览器定期下载TS文件,定期间隔为1s左右,导致下载到播放产生延时
当然,以上可能是HLS的延时的主要部分,这里不一一列举,HLS的延时一般在10秒左右。

当我们意识到HLS延时无法避免时,就需要想办法去解决对应问题!

秋秋:961179337 wx:lixiang6153

websocket播放

由于之前写过一篇文章:web端视频点播之websocket播放。大家可以自行查看我的博客进行翻阅。不过我之前写的文章是通过Java端实现websocket,通过读取H264文件一帧一帧的将视频数据发送喂给wfs库,然后通过video标签显示出来。
这种实现方式或Demon的弊端是:
(1)Demon只能播放H264文件,无法做实时编解码或者实现难度很大
(2)Java端实现RTSP视频领取转码无法实现
(3)web端只能播放H264格式视频,无法播放H265,而目前大多数摄像机为H265模式

基于以上几点,我们可以看到我之前给的demon仅仅是示例性的了解web端是可以播放实时视频的,也就是存在技术实现可行性!那么如何将web端视频用到实际生产中呢?如果要用到生产环境中就需要解决一下几点问题
(1)支持H264和H265播放
(2)无插件播放,不需要flash
(3)低延时播放,特别是PTZ云台控制对低延时要求非常高
(4)方便录像录制、异常情况报警图片留图抓取
websocket就很好的解决了以上几个问题的(2)(3)(4)和(1)的H264播放问题,如果要播放H265在web端目前还没有开源的库区解码播放(可以通过ecms将c/c++代码转为js代码来实现–这个我之前的文章也有提及,不过这种方式难度太大),这里我用一种简单的方式实现:H265解压编码为H264即可。

实现思路

因为Java在音视频行业的弱势,所以还是需要c++来实现,它解决了rtsp视频拉去、H265解码、H264编码、websocket发送以及http协议提供等若干问题,Java能做的C++都能做!具体的实现流程如下所示

操作员 媒体服务 摄像机 web端 http添加设备 登录 rtsp协议 解码 编码 请求视频 H264帧 渲染 录像 抓拍 操作员 媒体服务 摄像机 web端

按照以上思路,我实现的核心步骤
(1)设备的动态添加删除
在这里插入图片描述
(2)视频的接入
在这里插入图片描述
(3)websocket视频点播
我们通过设备id、通道、码流类型(主码流和子码流)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="description" content="jMuxer - a simple javascript mp4 muxer for non-standard streaming communications protocol">
    <meta name="keywords" content="h264 player, mp4 player, mse, mp4 muxing, jmuxer, aac player">
    <title>rtsp转websocket播放</title>
	<style type="text/css">
		#time{
			position: relative;
			display: block;
			top: -100px;
			text-align: center;
			color: red;
			z-index: 1000;
			font-size: 40px;
		}
		button {
			border: 2px solid blue;
			width: 150px;
			height: 35px;
			font-weight: bold;
			font-size: 20px;
		}
	</style>
</head>
<body>
	<div id="container" style="margin: 0 auto;">
		<div>
			<label style="display: inline-block;text-align: left; width: 150px;">websocket视频地址: </label>
			<input type="input" style="width: 640px;height: 25px; color: red; font-size: 16px;" id="url" value="ws://localhost:8090/10/1/1"></input>
		</div>
		<div>
			<video style="border: 1px solid #333; width: 800px;height: 600px;" controls autoplay poster="images/loader-thumb.jpg" id="player">
			</video>
			<canvas style="display:none" id="canvas"></canvas>
			<label id="time"></label>
		</div>
		<div>
			<button onclick="record()">开始录制</button>
			<button onclick="record()">停止录制</button>
			<button onclick="capture()">视频抓拍</button>
		</div>
	</div>
	<script>
	var jmuxer;
	window.onload = function() {
		var socketURL = document.getElementById("url").value;
		if(socketURL === "") {
			alert("请输入websocket视频播放地址");
			return;
		}
		jmuxer = new JMuxer({
			node: 'player',
			mode: 'video',
			flushingTime: 1000,
			fps: 25,
			debug: true,
			onError: function(data) {
				if (/Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor)) {
					jmuxer.reset();
				}
			}
		 });

		 var ws = new WebSocket(socketURL);
		 ws.binaryType = 'arraybuffer';
		 ws.addEventListener('message',function(event) {
			  jmuxer.feed({
				video: new Uint8Array(event.data)
			  });
		 });

		 ws.addEventListener('error', function(e) {
			console.log('Socket Error');
		 });
		 
		 record = function(){
			window.screen_record.startRecord("canvas", "player");
			if(window.screen_record.isRecord()) {
				window.setInterval(()=>{
					document.getElementById("time").innerText = window.screen_record.getVideoTime();
				}, 1000);
			}
		 }
		 
		 capture = function(){
			window.screen_record.captureImage("player")
		 }
		 
		 window.screen_record = new ScreenRecord();
	 }
	</script>
	<script type="text/javascript" src="jmuxer.min.js"></script>
	<script type="text/javascript" src="screenRecord.js"></script>
</body>
</html>

这里采用的是JMuxer播放,没有使用wfs库播放,原因是经过测试JMuxer性能和播放效果要优于wfs库,而且JMuxer支持帧率控制,播放不会卡顿,而wfs则偶尔会卡顿!

(4)服务端线性平稳发送
服务端根据帧率编码并线性发送h264视频帧到web端

#include "stdafx.h"
#include "WsClient.h"
#include "TextUtil.h"

CWsClient::CWsClient(client_connc conc, conc_hdl client, std::string device, int channel, int stream)
	: m_server(conc)
	, m_client(client)
	, m_device(device)
	, m_channel(channel)
	, m_stream(stream)
	, m_type(CLIENT_TYPE_RTSP)
	, m_stop(false)
	, m_play(false)
{
	m_buffer.AllocateBuffer(50, 1024);
	m_manager = websocketpp::lib::make_shared<con_msg_man_type>();
}

CWsClient::~CWsClient()
{
	Stop();
}

conc_hdl CWsClient::GetId()
{
	return m_client;
}

std::string CWsClient::GetDeviceId()
{
	return m_device;
}

int CWsClient::GetChannel()
{
	return m_channel;
}

void CWsClient::SetChannel(int channel)
{
	m_channel = channel;
}

void CWsClient::SetPlay(bool value)
{
	m_play = value;
}

bool CWsClient::IsPlay()
{
	return m_play;
}

int CWsClient::GetStream()
{
	return m_stream;
}

int CWsClient::GetType()
{
	return m_type;
}

void CWsClient::Print()
{
	ATXTRACE0("设备:%s,码流:%d", m_device.c_str(), m_type);
}

bool CWsClient::Start()
{
	// 启动文件写线程
	m_stop = false;
	m_thread = boost::thread(&CWsClient::Write, this);
	return true;
}

void CWsClient::Stop()
{
	// 停止写线程
	m_stop = true;
	if (m_thread.joinable())
	{
		m_thread.join();
	}
}

void CWsClient::Notify(bool video, unsigned char* data, int length, bool keyFrame, unsigned __int64 time, unsigned __int64 duration)
{
	BufferPtr buffer = m_buffer.GetEmptyBuffer();
	if (buffer)
	{
		buffer->m_reserver.push_back(video);
		buffer->m_reserver.push_back(time);
		buffer->m_reserver.push_back(duration);
		buffer->FillData(data, length);
		m_buffer.AddFullBuffer(buffer);
	}
}

void CWsClient::Send(unsigned char* data, int length)
{
	// 创建client::message_ptr对象,容量初始大小为1024(可随便设置),用于设置数据帧的基本参数
	client::message_ptr msg = m_manager->get_message(websocketpp::frame::opcode::BINARY, length);
	// 是否最后一帧数据:true:最后一帧数据,false:还有下一帧数据
	msg->set_fin(true);
	// 数据格式是二进制,  CONTINUATION=0x0,TEXT=0x1,BINARY=0x2
	msg->set_opcode(websocketpp::frame::opcode::value::BINARY);
	// 发送数据的内容
	msg->set_payload(data, length);
	// 调用发送接口,发送数据帧
	m_server->send(msg);
}

void CWsClient::Write()
{
	int sleep_time = 31;
	__int64 previouse = GetTickCount64(), send_frame = 0;
	while (!m_stop)
	{
		// 平稳发送
		__int64 current = GetTickCount64();
		__int64 gap = current - previouse;

		// 当前应当发送数量与实际发送数量
		__int64 count = (gap / sleep_time);
		if (count <= send_frame)
		{
			boost::this_thread::sleep_for(boost::chrono::microseconds(1));
			continue;
		}

		// 获取一帧数据
		BufferPtr buffer = m_buffer.GetFullBuffer();
		if (!buffer)
		{
			boost::this_thread::sleep_for(boost::chrono::microseconds(1));
			continue;
		}

		// 上一次发送:平稳发送		
		send_frame += 1;

		// 平稳发送数据
		this->Send(buffer->m_pData, buffer->m_nDataSize);

		// 循环利用缓冲区
		m_buffer.AddEmptyBuffer(buffer);
	}
}

这里的核心代码保证了web端的平稳播放
(1)使用了环形双缓冲区
(2)使用了按时间增量线性发送视频帧

(5)最后献上视频播放效果
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

H5播放之Rtsp转Websocket点播录像抓拍 的相关文章

  • 当url中有空格时htaccess重定向

    我想从仍然出现在谷歌搜索中的旧网址重定向到新网址 旧的网址是这样的 http www marionettecolla org file 20 mostra milano mostra marionette milano htm 我想将其重定
  • 在cypress中捕获websocket请求

    我试图捕获测试期间发生的所有请求 我的应用程序使用 WebSocket 并且使用拦截命令我无法捕获 wss 请求 有什么办法可以做到这一点吗 我认为拦截命令不能直接捕获网络套接字 一种方法是观察 ws 通信的结果 如下所示使用 Cypres
  • Netty:正确关闭 WebSocket

    如何从服务器端正确关闭 WebSocket 通道 连接 如果我使用一个ctx getChannel close the onerror在浏览器 Firefox 9 中抛出 页面加载时与 ws localhost 8080 websocket
  • 电话链接在 iframe 中不起作用,但在 iOS 9 Web 中的 div 中起作用。如何使电话链接在 iOS 9 safari 中正常工作?

    您好 我正在尝试 iOS9 中 iframe 内的电话链接 iOS9 中的 safari 中无法打开手机应用程序 当我在里面尝试相同的链接时 它就在那里工作 我正在尝试下面的锚标记 将此代码放入 div 中时会打开手机应用程序 但同样的代码
  • 使用服务器发送事件进行双向客户端-服务器通信(而不是 WebSocket)的缺点

    最近 我发现服务器发送事件是 WebSocket 的一种更简单的替代方案 用于从服务器进行推送 大多数比较它们的地方 例如here https stackoverflow com questions 5195452 websockets v
  • gstreamer gst-rtsp-server 是否接受 udpsrc (RTP)?

    我想将 RTP 流输入到 gstreamer gst rtsp server 中 对于初始测试 我使用来自 github 的 test launch c 示例 https github com GStreamer gst rtsp serv
  • 单击链接时启动本地应用程序

    我正在开发一个内部 Web 应用程序 它允许我为客户存储远程控制凭据 每次我想要连接到客户计算机时 我都需要启动远程支持软件 复制并粘贴用户名和密码 然后单击 开始 按钮 该软件将具有可用的命令行参数 允许我立即启动会话 但是 我不知道如何
  • 在 Apache 上设置 websocket?

    所以我正在对 websockets 进行一些研究 我有几个问题似乎找不到明确的答案 如何在 Linux 服务器上设置 Web 套接字 有 Apache 模块吗 我可以吗have使用第 3 方 PHP 代码或类似代码 除了浏览器兼容性之外 问
  • Django - 缺少 1 个必需的位置参数:'request'

    我收到错误 get indiceComercioVarejista 缺少 1 个必需的位置参数 要求 当尝试访问 get indiceComercioVarejista 方法时 我不知道这是怎么回事 views from django ht
  • 为什么使用HTTP协议时需要指定端口号?

    即使我们使用HTTP协议 为什么还需要用IP地址指定端口号 例如 http xyz 8080 这到底是什么意思 我们已经知道 在使用 HTTP 时 请求将在端口 80 上提供服务 那么为什么我们要显式指定端口呢 HTTP 的默认端口为 80
  • 如何通过 libwebsocket 发送异步数据?

    我正在将 Warmcat 的 libwebsocket C 库用于小型 Websocket 服务器 我已经启动并运行了这些示例 并且可以发送数据以响应从 websocket 接收数据 例如回显发送的反向字节 但是 我无法弄清楚如何在不使用
  • Web Api - 不允许捕获 405 方法

    截至目前 Web api 应用程序针对 405 方法不允许错误返回以下响应正文 我正在尝试更改响应正文 但我不知道如何使用委托处理程序 ApiControllerActionSelector 或过滤器 谁能帮我捕获服务器端的 405 错误
  • System.Web.HttpException 无法加载类型“[命名空间].???”

    这开始于无法加载类型 全局 错误 在我尝试了一些方法后 没有找到删除 Global asax 文件的位置 现在错误是无法加载类型 namespace 在哪里 是我尝试加载的每个页面的类名 该网站 在 VS2008 本地开发计算机中执行时 工
  • php 中的 stackoverflow 上有这样的成就系统吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 从概念上讲 如何使用 PHP 和 MySQL 为网站编写一个成就系统 唯一真正的方法是不断执行 MySQL 查询来测试成绩等吗 您有两
  • Jetty、websocket、java.lang.RuntimeException:无法加载平台配置器

    我尝试在 Endpoint 中获取 http 会话 我遵循了这个建议https stackoverflow com a 17994303 https stackoverflow com a 17994303 这就是我这样做的原因 publi
  • 由 Servlet 容器提供服务的 WebSocket

    上周我研究了 WebSockets 并对如何使用 Java Servlet API 实现服务器端进行了一些思考 我没有花费太多时间 但在使用 Tomcat 进行一些测试时遇到了以下问题 如果不修补容器或至少对 HttpServletResp
  • WebSocket 无法与 DNS 配合使用

    我有一个网络应用程序 我正在使用 websockets 我在使用 ws myDomain com 作为 websocket 服务器的地址时遇到问题 这个问题很奇怪 因为我使用许多 PC 测试了连接 似乎这适用于大约 1 5 的 PC 当我将
  • Chrome:网络元素自动移动

    我正在开发一个网络项目 在过去的两周里 我正在进行学期考试 所以我要休息一下 现在 既然他们已经结束了 我又继续我的工作了 但我发现我的一些Web 元素 如按钮 span 在 Chrome 上移动了一些像素 在 IE 和 Mozilla 上
  • 发送变量后的 wsdl 服务响应,php

    我是 SOAP WSDL 函数的新手 我有一位客户从一家从事汽车测试的公司获得了 wsdl 文件 我的客户是他们的分包商 他们告诉我们上传有关车牌 类别等信息 一旦详细信息发送完毕 服务器就会做出成功或失败的响应 请您协助 浏览不同的信息
  • Web 应用程序使用 API 来完成一切?

    我即将开始为我的公司规划一个内部项目管理工具 API 一直让我疑惑 首先创建 API 并使用这些 API 调用构建实际站点而不是实施两次 是否会被视为不好的做法 效率太低 让我知道你的想法 我完全同意开发 API 将为您提供解耦的架构 并且

随机推荐

  • SQL操作

    一 查询语句 1 基本查询 SELECT FROM lt 表名 gt 查询表的所有行 SELECT FROM students 2 条件查询 SELECT FROM lt 表名 gt WHERE lt 条件表达式 gt 查询分数在80分以上
  • vscode+phpstudy连接使用mysql(解决phpstudy中mysql无法启动的问题)

    vscode phpstudy连接使用mysql 解决phpstudy中mysql无法启动的问题 使用vscode phpstudy配置php开发环境网上很文章都是挺好的 都成功解决了我的问题 但是对于使用mysql方面始终找不到很系统的文
  • 数据结构系列——先进先出队列queue

    本期主题 先进先出队列实现 目录 1 队列定义 2 实现一个简单的队列以及分析 1 代码实现分析 2 code 3 优缺点分析 3 循环队列实现 1 循环队列原理 2 循环队列实现分析 3 code 1 队列定义 队列是什么 定义 一个先进
  • unity,网格碰撞器(Mesh Collider)

    介绍 网格碰撞器 Mesh Collider 在实现物理碰撞检测时 可以自动检测凸面 但是它并不总是能够准确地生成凸多面体 这是因为在将一个网格模型转换为凸多面体的过程中 可能会出现模型内部空洞或者交叉的情况 这些情况会导致凸多面体的生成失
  • H5页面长按识别二维码

    vue 写的H5 内嵌在小程序上 img src 图片路径 style width 200px height 200px 直接在微信访问长按就可以实现 微信原生直接写长按识别二维码 aaa e let img e target datase
  • 八. springboot 的指标监控 (3、定制 Endpoint )

    3 定制 Endpoint 3 1 定制 Health 信息 import org springframework boot actuate health Health import org springframework boot act
  • http、https以及状态码超全详解

    文章目录 HTTP 概念 作用 http 请求 http 头部 http 连接 各版本的区别 http1 0 http1 1 http2 0 http3 0 HTTPS 概念 作用 工作原理 优点 缺点 http 和 https 区别 ht
  • [网络安全自学篇] 八.Web漏洞及端口扫描之Nmap、ThreatScan和DirBuster原理详解

    Web渗透技术的核心是发现Web漏洞 发现漏洞有手工和软件自动化扫描两种方式 对于用户验证漏洞 用户凭证管理问题 权限特权及访问控制漏洞 缓存漏洞 跨站脚本漏洞 加密漏洞 路径切换漏洞 代码注入漏洞 配置漏洞 数据和信息泄露 输入验证码漏洞
  • CA证书服务搭建

    准备一 域服务搭建 步骤一 搭建CA证书服务器 设置固定IP地址 192 168 0 与主机名 DC 打开服务器管理器 gt 点击添加角色和功能 步骤二 一路回车到达选择服务器角色位置 选中Active Directory域服务与Activ
  • 动态库装载及 dlsym的RTLD_NEXT参数详解

    在看公司spp框架代码的时候发现了如下一段宏定义 其中的dlsym函数及其RTLD NEXT参数的含义不是很明白 于是网上搜了下这里做个记录 define mt hook syscall name do if g mt syscall ta
  • 扔掉工具类!MyBatis 一个简单配置搞定加密、解密,好用!

    程序员的成长之路 互联网 程序员 技术 资料共享 关注 阅读本文大概需要 3 5 分钟 来自 https juejin cn post 6963811586184052767 目录 一 背景 二 解决方案 三 需求 四 实现思路 五 实现代
  • spring cloud alibaba 学习(二十五)nacos 服务地址动态更新

    目录 前言 服务地址动态更新 前言 通过配置endpoint来实现服务地址的动态更新 服务地址动态更新 1 在 NacosConfigBootstrapConfiguration 创建 NacosConfigManager 的过程中 会创建
  • Unity PlayerPrefs、JsonUtility

    Unity中有两个常用的数据存储方式 PlayerPrefs和JsonUtility PlayerPrefs PlayerPrefs是Unity内置的一种轻量级数据存储方式 可用于存储少量的游戏数据 如分数 解锁状态等 使用PlayerPr
  • LPDDR4协议规范之 (六)刷新

    LPDDR4协议规范之 六 刷新 刷新命令 刷新计数器 刷新时序 刷新前时序 刷新后时序 全存储体刷新时序 tRFCab tRFCpb 自刷新 自刷新期间进入掉电模式 自刷新中止 刷新命令 REFRESH命令在时钟的第一个上升沿以CS HI
  • GDI+ 中 Pen 使用总结

    背景 图形类 Graphics 是 GDI 的核心 它提供绘制图形 图像和文本的各种方法 Graphics 中使用 DrawString 方法在指定位置绘制文本或者在一个指定矩形内绘制文本 所有的 Graphics 类的绘制方法都得配合 P
  • MXNet简介

    MXNet是一个十分优秀的深度学习框架 目前包含了许多语言接口 如Python C Scala R等 目前 MXNet版本已经更新到1 3 0 本系列文章主要使用Python接口 在MXNet官网 1 上 官方建议新手使用Python接口
  • 网站代理是什么?有什么需要注意的?

    如今 网站代理已经成为一种不可或缺的经营方式 无论是企业还是个人 都需要通过代理来获得更多的流量和市场份额 一 网站代理的优势 网站代理的优势在于能够为您提供更加专业 周到的服务 这些优势包括 1 丰富的内容资源 能够满足客户对不同领域信息
  • Java-按照指定小时分割时间段

    按照指定小时分割时间段 param dateType 类型 M D H N gt 每月 每天 每小时 每分钟 param dBegin开始时间 param dEnd结束时间 param time 指定小时 如 1 2 3 4 return
  • 变分推断学习

    https zhuanlan zhihu com p 401456634 变分推断 1 变分推断的背景 在机器学习中 有很多求后验概率的问题 求后验概率的过程被称为推断 Inference 推断分为精确推断和近似推断 精确推断一般主要是根据
  • H5播放之Rtsp转Websocket点播录像抓拍

    H5播放之Rtsp转Websocket点播录像抓拍 HLS的延时 websocket播放 实现思路 广大网友们 很久没上CSDN了 暨上次RTSP转HLS文章发布以来 一直还有一个问题没有解决 如何避免HLS切片带来的不可避免的高延时 HL