目的
在GB28181 传输过程中,为了达到最大效率的结果,只制作webscoket传输和flv传输,本身flv是可以在web端展示的,如果是h265等等编码,可能在web端解码效率不够的情况下,我们需要客户端来连接服务器,并且
websocket 对比http协议webscoket协议确实只是http协议的升级,问题就是websocket协议本身加入了字节协议,也就是数据帧的概念,这个协议推出很有必要,为什么呢? 前端html使用javascript的程序员本身很难对tcp协议理解,http协议大家都能理解,文本协议,所以他以\r\n\r\n来结束协议。websocket的特点除了长链接,还有可以传输二进制的特点,注意http协议本身可以传输二进制,如 1、图像 2、视频 以content-length来标识数据长度,也可以断电续传。http协议本身也是一个非常值得研究的协议。 以Range:byte=来 标识 如 Range:byte=1000- 代表从1000字节开始传输
2、flv 解封包这样,websocket 的帧头字节数标识可以让websocket 协议获取单帧二进制内容
2.1 flv 数据结构定义下面定义数据结构,我们应该都知道8 和 9 的含义,8代表音频,9代表视频,和rtmp协议是一致的,实际上,rtmp协议本身内部是flv格式封装
#define FLV_TAG_AUDIO 0x08
#define FLV_TAG_VIDEO 0x09
#define FLV_VIDEO_KEY_FLAG 0x10
#define FLV_VIDEO_INTER_FLAG 0x20
#define FLV_VIDEO_AVC_SEQHDR 0x00
#define FLV_VIDEO_AVC_NALU 0x01
#define FLV_VIDEO_H264_CODEC 0x07
#define FLV_VIDEO_H265_CODEC 0x0c
#define FLV_VIDEO_AV1_CODEC 0x0d
#define FLV_VIDEO_VP8_CODEC 0x0e
#define FLV_VIDEO_VP9_CODEC 0x0f
#define FLV_AUDIO_OPUS_CODEC 0x90
#define FLV_AUDIO_AAC_CODEC 0xa0
/* offsets for packed values */
#define FLV_AUDIO_SAMPLESSIZE_OFFSET 1
#define FLV_AUDIO_SAMPLERATE_OFFSET 2
#define FLV_AUDIO_CODECID_OFFSET 4
enum {
FLV_MONO = 0,
FLV_STEREO = 1,
};
enum {
FLV_SAMPLESSIZE_8BIT = 0,
FLV_SAMPLESSIZE_16BIT = 1 codec_type_ = MEDIA_CODEC_H264;
} else if ((p[0]&0x0f) == FLV_VIDEO_H265_CODEC) {
data->codec_type_ = MEDIA_CODEC_H265;
} else if ((p[0]&0x0f) == FLV_VIDEO_VP8_CODEC) {
data->codec_type_ = MEDIA_CODEC_VP8;
} else if ((p[0]&0x0f) == FLV_VIDEO_VP9_CODEC) {
data->codec_type_ = MEDIA_CODEC_VP9;
} else {
is_ready = false;
printf("does not support codec type:0x%02x.", p[0]);
return -1;
}
if ((p[0] & 0xf0) == FLV_VIDEO_KEY_FLAG) {
if (p[1] == 0x00) {
data->is_seq_hdr_ = true;
} else if (p[1] == 0x01) {
data->is_seq_hdr_ = false;
data->is_key_frame_ = true;
} else {
is_ready = false;
printf("unkown key byte:0x%02x.", p[1]);
return -1;
}
}
ts_delta = read_3bytes(p + 2);
}
else {
is_ready = false;
tag_header_ready_ = false;
printf("does not suport tag type:0x%02x", tag_type_);
return 0;
}
int ret = 0;
if (is_ready && ((int)tag_data_size_ > header_len)) {
data->dts_ = tag_timestamp_;
data->pts_ = tag_timestamp_ + ts_delta;
p += header_len;
int len = tag_data_size_ - header_len;
if(callback_!=NULL)
ret = callback_(p,len);
}
tag_header_ready_ = false;
return ret;
}
读写封装
在flv 格式容器读写的时候需要一些封装
double av_int2double(uint64_t i)
{
union av_intfloat64 v;
v.i = i;
return v.f;
}
uint64_t av_double2int(double f)
{
union av_intfloat64 v;
v.f = f;
return v.i;
}
uint64_t read_8bytes(const uint8_t* data) {
uint64_t value = 0;
uint8_t* output = (uint8_t*)&value;
output[7] = *data++;
output[6] = *data++;
output[5] = *data++;
output[4] = *data++;
output[3] = *data++;
output[2] = *data++;
output[1] = *data++;
output[0] = *data++;
return value;
}
uint32_t read_4bytes(const uint8_t* data) {
uint32_t value = 0;
uint8_t* output = (uint8_t*)&value;
output[3] = *data++;
output[2] = *data++;
output[1] = *data++;
output[0] = *data++;
return value;
}
uint32_t read_3bytes(const uint8_t* data) {
uint32_t value = 0;
uint8_t* output = (uint8_t*)&value;
output[2] = *data++;
output[1] = *data++;
output[0] = *data++;
return value;
}
uint16_t read_2bytes(const uint8_t* data) {
uint16_t value = 0;
uint8_t* output = (uint8_t*)&value;
output[1] = *data++;
output[0] = *data++;
return value;
}
void write_8bytes(uint8_t* data, uint64_t value) {
uint8_t* p = data;
uint8_t* pp = (uint8_t*)&value;
*p++ = pp[7];
*p++ = pp[6];
*p++ = pp[5];
*p++ = pp[4];
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
}
void write_4bytes(uint8_t* data, uint32_t value) {
uint8_t* p = data;
uint8_t* pp = (uint8_t*)&value;
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
}
void write_2bytes_le(uint8_t* data, uint32_t value) {
uint8_t* p = data;
uint8_t* pp = (uint8_t*)&value;
*p++ = pp[0];
*p++ = pp[1];
}
void write_4bytes_le(uint8_t* data, uint32_t value) {
uint8_t* p = data;
uint8_t* pp = (uint8_t*)&value;
*p++ = pp[0];
*p++ = pp[1];
*p++ = pp[2];
*p++ = pp[3];
}
void write_3bytes(uint8_t* data, uint32_t value) {
uint8_t* p = data;
uint8_t* pp = (uint8_t*)&value;
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
}
void write_2bytes(uint8_t* data, uint16_t value) {
uint8_t* p = data;
uint8_t* pp = (uint8_t*)&value;
*p++ = pp[1];
*p++ = pp[0];
}
bool bytes_is_equal(const char* p1, const char* p2, size_t len) {
for (size_t index = 0; index > text;
// Send the message
ws.write(net::buffer(ansi_to_utf8(text)));
// This buffer will hold the incoming message
beast::flat_buffer buffer;
// Read a message into our buffer
ws.read(buffer);
std::string out;
out = beast::buffers_to_string(buffer.cdata());
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?