您当前的位置: 首页 >  html5

qianbo_insist

暂无认证

  • 0浏览

    0关注

    399博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

rtsp,rtp,gb28181直接转化为html5播放(二)

qianbo_insist 发布时间:2022-03-08 12:54:11 ,浏览量:0

承接上一节 udp服务器接流转html5播放

效果

先看效果:

ws flv播放 服务器拉rtsp流,直接转成flv,不经过推流,浏览器使用ws直接连接写得我们写得服务器播放,因为websocket本身是跨域得,可以直接使用文件进行播放。http协议支持得时候注意要启动http服务支持,否则会跨域截断。

rtsp,rtp截流hack

上一章我们使用了udpserver来接流,也就是接得纯rtp,拼接成帧就行,而rtsp,和GB28181都使用rtp协议,【GB28181会使用ps流】,原理都一样,同样获取rtp流以后,可以直接转化成单帧再进行html5传输,如fmp4,或者转化成flv格式,使用flv.js来直接播放,flv内部也是转成了fmp4,区别不是很大。

1.1 截流描述

上一次我们使用了live555来接rtsp,live555接rtsp流,这样加上udp接就比较实用了,可以使用ffmpeg发送测试,也可以使用rtsp直接接入,rtsp连接写入配置文件中:in.txt

#id(int):address(varchar):width(int):height(int) #418511899@qq.com author:钱波 qianbo 2021-02-28 1 rtsp://admin:hk123456@192.168.0.55/h264/ch1/sub/av_stream 1280 720 live/1001 #2 rtsp://127.0.0.1/2.264 1280 720 live/1001 #3 rtsp://127.0.0.1/1.264 640 360 live/1002

带# 得是注释,文件引入了一路海康流,输出流为live/1001

1.2流程

流程为: 1 线程rtsp client 拉到流后发送给flv服务器 2 flv服务器接收到连接后将连接放入到hub中,待接到rtsp sps pps等信息后开始发送meta信息,启动推流协程,并同步每个人得时间戳。注意每个人得时间戳是不一样得。 3 ws浏览器播放端退出在推流协程里面同步判断,退出则在hub中删除该端。

1.3 读配置文件类
struct s_file
{
	std::vector v_sources;

	int func_read()
	{
		std::ifstream infile("in.txt", std::ios::in); //以文本模式打开in.txt备读
		if (!infile) { //打开失败
			std::cout > s.d_width >> s.d_height >>s.v_liveflv;
				v_sources.push_back(s);
			}
			else
			{
				//数据库解析字段
			}
		}
		return 0;
	}
};

isnote函数只是判断是否带有注释,注释则不加入其中,比较简单,读者可以自行写出。

1.4 websocket服务器

websocket服务器我们自行编写,使用协程方式来制作,这个在我得其他文章里面也提供了全部源代码,读者可以找一下。

1.5 flv服务器

显然flv服务器建立在websocket服务器之上,加了flv格式得封装,class 图如下图所示

class c_flvserver
{
private:
	//websocket or http
	asio::io_context &v_io_con_webs;
	//calculate the number of coroutine
	std::atomic v_coroutine_num = 0;



public:
	/*int process_spspps(uint8_t * sps, uint32_t spslen,
		uint8_t * pps,
		uint32_t ppslen,
		uint32_t timestamp,
		int width, int height, int fps,
		uint32_t key
	);*/
	
#if 0
	//关键帧再次同时写入sps和pps,特殊场合使用
	int process_h264_spspps(
		uint8_t *sps, int spslen,
		uint8_t *pps, int ppslen,
		uint8_t * nal,
		uint32_t nal_len,
		uint32_t timestamp,
		bool IsIframe);
#endif
	void storage_meta(uint32_t key,
		ptr_s_memory mem);

	int create_metadata(uint8_t* buf, int width, int height, int frameRate);


	int process_spspps(uint8_t * sps, uint32_t spslen,
		uint8_t * pps,
		uint32_t ppslen,
		uint32_t timestamp,
		uint32_t key
	);

	int process_spspps_sp(uint8_t * sps, uint32_t spslen,
		uint8_t * pps,
		uint32_t ppslen,
		uint32_t timestamp,
		uint32_t key
	);
	int process_h264(uint8_t * nal,
		uint32_t nal_len,
		uint32_t timestamp,
		bool IsIframe,
		uint32_t key
	);
	int process_aac(uint8_t* data, 
		uint32_t data_len, 
		uint32_t timestamp);

	c_flvserver(asio::io_context &iow) :v_io_con_webs(iow)
	{}
	void start(uint16_t port);



protected:
#ifdef SEND_META_SCRIPT
	int  create_metadata(uint8_t* buf, int width, int height, int frameRate);
#endif


protected:


	void storage_header(uint32_t key, int av, ptr_s_memory mem);

	void start_write_spawn(uint32_t key);
	

	bool func_set_head_send(tcp::socket &sock,
		uint8_t * buf, 
		size_t buflen /*payloadlen*/, 
		asio::yield_context &yield);
	//we must add the websockets head,use the func_set_head_send
	int func_send_2session_webs(s_flvhub &flvhub,
		ptr_session_webs ptr,
		ptr_s_memory mem,
		asio::yield_context &yield);


	//we do not add ,just send the flv data
	int func_send_2session_http(s_flvhub &flvhub,
		ptr_session_webs ptr,
		ptr_s_memory mem,
		asio::yield_context &yield);

};
1.6 下面给出关键代码

只有一个流容易,只有一个客户也容易,我们面对得一定是多路流和多个客户并发连接,所以要注意得事项比较多,下面看具体代码:

int c_flvserver::func_send_2session_webs(s_flvhub &flvhub,ptr_session_webs ptr, ptr_s_memory mem,
		asio::yield_context &yield)
{

	if (!ptr->v_socket.is_open())
		return -2;
	DEFINE_EC
	uint8_t * data = mem->v_data;
	size_t len = mem->v_len;
#if 1
	if (!ptr->v_has_send_meta){
		uint8_t * _data = flvhub.v_meta->v_data;
		size_t _len = flvhub.v_meta->v_len;
		if (func_set_head_send(ptr->v_socket, _data, _len, yield) == false)
			return -1;
		ptr->v_has_send_meta = 1;
	}
#endif
	if (flvhub.v_need_video && !ptr->v_has_send_video_head)
	{
		uint8_t * _data = flvhub.v_cache_headerv->v_data;
		size_t _len = flvhub.v_cache_headerv->v_len;
			if (func_set_head_send(ptr->v_socket, _data, (int)_len, yield) == false)
				return -1;
		ptr->v_has_send_video_head = 1;
	}
	if (flvhub.v_need_audio && !ptr->v_has_send_audio_head)
	{
		uint8_t * _data = flvhub.v_cache_headera->v_data;
		size_t _len = flvhub.v_cache_headera->v_len;
		if (func_set_head_send(ptr->v_socket, _data, (int)_len, yield) == false)
			return -1;
		ptr->v_has_send_audio_head = 1;
	}
	if (!ptr->v_has_sent_key_frame)
	{
		switch (*data) //the first byte
		{
		case 8://audio
		{
			cout v_socket, data, (int)len, yield) == false)
		return -1;

	return 0;
}

以上是关键代码,下面给出一个代码结构,读者可以参考:

在这里插入图片描述 整个工程得代码并不多,比较简单,包含了flvserver,websocketserver,httpserver, rtp over udpserver,都使用协程制作,这样流程简单。包含rtsp得拉流客户端,比较实用,包含了analyse分析模块,可以对流进行图像分析等等,读者可以自行制作。

时间戳问题

如下图所示,明显vlc和ws连接得时间显然是不一样,ws连接时间很长,显示得时间竟然和vlc 相差20秒,这就是时间戳得问题了,所以要进一步处理该问题 在这里插入图片描述 如图,修正以后,时间戳趋于正常,flv播放比vlc rtsp 相差无几,快1秒左右,应该还有余地,可以在进行优化。 在这里插入图片描述

总结

本文并不使用拉流后再推流到服务端,而是将服务端直接集成,减少了部署内容,这是优点之一,第二个优点是并没有使用rtmp协议,而是直接转化成浏览器可播放得流,这是优点之二,缺点也很明显,读者可以提出讨论,或者有不明白得地方也可以讨论,知无不言言无不尽,第三篇我们可以加上rtsp服务,待续…

html测试代码

忘了html flv测试代码,加上了

关注
打赏
1663161521
查看更多评论
立即登录/注册

微信扫码登录

0.2572s