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

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

  • HLS的延时
  • 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)设备的动态添加删除
H5播放之Rtsp转Websocket点播录像抓拍_第1张图片
(2)视频的接入
H5播放之Rtsp转Websocket点播录像抓拍_第2张图片
(3)websocket视频点播
我们通过设备id、通道、码流类型(主码流和子码流)




    
    
    
    rtsp转websocket播放
	


	

这里采用的是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();
}

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)最后献上视频播放效果

你可能感兴趣的:(视频编解码,web,websocket,rtsp,h265,视频编解码)