websocket flv 客户端解封包
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了websocket flv 客户端解封包相关的知识,希望对你有一定的参考价值。
目的
在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 << FLV_AUDIO_SAMPLESSIZE_OFFSET,
;
enum
FLV_SAMPLERATE_SPECIAL = 0, /**< signifies 5512Hz and 8000Hz in the case of NELLYMOSER */
FLV_SAMPLERATE_11025HZ = 1 << FLV_AUDIO_SAMPLERATE_OFFSET,
FLV_SAMPLERATE_22050HZ = 2 << FLV_AUDIO_SAMPLERATE_OFFSET,
FLV_SAMPLERATE_44100HZ = 3 << FLV_AUDIO_SAMPLERATE_OFFSET,
;
typedef enum
MEDIA_CODEC_UNKOWN = 0,
MEDIA_CODEC_H264 = 1,
MEDIA_CODEC_H265,
MEDIA_CODEC_VP8,
MEDIA_CODEC_VP9,
MEDIA_CODEC_AAC = 100,
MEDIA_CODEC_OPUS,
MEDIA_CODEC_MP3
MEDIA_CODEC_TYPE;
typedef enum
MEDIA_FORMAT_UNKOWN = 0,
MEDIA_FORMAT_RAW,
MEDIA_FORMAT_FLV,
MEDIA_FORMAT_MPEGTS,
MEDIA_FORMAT_TYPE;
typedef enum
MEDIA_UNKOWN_TYPE = 0,
MEDIA_VIDEO_TYPE = 1,
MEDIA_AUDIO_TYPE,
MEDIA_METADATA_TYPE
MEDIA_PKT_TYPE;
typedef int(*av_callback)(uint8_t* pkt_ptr, int len);
#define FLV_RET_NEED_MORE 1
#define FLV_HEADER_LEN 9
#define FLV_TAG_PRE_SIZE 4
#define FLV_TAG_HEADER_LEN 11
struct webso_flv_data
uint8_t* pkt = 0;
int len;
MEDIA_PKT_TYPE av_type_ = MEDIA_UNKOWN_TYPE;
MEDIA_CODEC_TYPE codec_type_ = MEDIA_CODEC_UNKOWN;
MEDIA_FORMAT_TYPE fmt_type_ = MEDIA_FORMAT_UNKOWN;
int64_t dts_ = 0;
int64_t pts_ = 0;
bool is_key_frame_ = false;
bool is_seq_hdr_ = false;
uint32_t ssrc = 0;
;
封装class
class flv_demuxer
public:
flv_demuxer(av_callback cb);
~flv_demuxer();
public:
int input_packet(webso_flv_data* pkt_ptr);
bool has_video() return has_video_;
bool has_audio() return has_audio_;
private:
int handle_packet(webso_flv_data* data);
private:
av_format_callback* callback_ = nullptr;
uint8_t* buffer_;
uint32_t ssrc;
bool has_video_ = false;
bool has_audio_ = false;
private:
bool flv_header_ready_ = false;
bool tag_header_ready_ = false;
private:
uint8_t tag_type_ = 0;
uint32_t tag_data_size_ = 0;
uint32_t tag_timestamp_ = 0;
;
#include "flv_demux.hpp"
//#include "logger.hpp"
#include "byte_stream.hpp"
flv_demuxer::flv_demuxer(av_callback cb):callback_(cb)
flv_demuxer::~flv_demuxer()
int flv_demuxer::handle_packet(webso_flv_data* data)
uint8_t* p = NULL;
uint32_t ts_delta = 0;
int plen = 0;
if (!flv_header_ready_)
if (data->len < (FLV_HEADER_LEN + FLV_TAG_PRE_SIZE))
return FLV_RET_NEED_MORE;
p = (uint8_t*)data->pkt;
plen = data->len;
if ((p[0] != 'F') || (p[1] != 'L') || (p[2] != 'V'))
return -1;
if ((p[4] & 0x01) == 0x01)
has_video_ = true;
if ((p[4] & 0x04) == 0x04)
has_audio_ = true;
if ((p[5] != 0) || (p[6] != 0) || (p[7] != 0) || (p[8] != 9))
return -1;
p += FLV_HEADER_LEN + FLV_TAG_PRE_SIZE;
plen -= FLV_HEADER_LEN + FLV_TAG_PRE_SIZE;
flv_header_ready_ = true;
if (!tag_header_ready_)
if (plen < FLV_TAG_HEADER_LEN)
return FLV_RET_NEED_MORE;
//p = (uint8_t*)buffer_.data();
tag_type_ = p[0];
p++;
tag_data_size_ = read_3bytes(p);
p += 3;
tag_timestamp_ = read_3bytes(p);
p += 3;
tag_timestamp_ |= ((uint32_t)p[0]) << 24;
tag_header_ready_ = true;
p += FLV_TAG_HEADER_LEN;
plen -= FLV_TAG_HEADER_LEN;
if (plen < tag_data_size_ + FLV_TAG_PRE_SIZE)
return FLV_RET_NEED_MORE;
bool is_ready = true;
int header_len = 0;
data->fmt_type_ = MEDIA_FORMAT_RAW;
data->ssrc = ssrc;
if (tag_type_ == FLV_TAG_AUDIO)
header_len = 2;
output_pkt_ptr->av_type_ = MEDIA_AUDIO_TYPE;
if ((p[0] & 0xf0) == FLV_AUDIO_AAC_CODEC)
output_pkt_ptr->codec_type_ = MEDIA_CODEC_AAC;
else if ((p[0] & 0xf0) == FLV_AUDIO_OPUS_CODEC)
output_pkt_ptr->codec_type_ = MEDIA_CODEC_OPUS;
else
is_ready = false;
printf("does not suport audio codec type:0x%02x", p[0]);
return -1;
if (p[1] == 0x00)
output_pkt_ptr->is_seq_hdr_ = true;
else
output_pkt_ptr->is_key_frame_ = false;
output_pkt_ptr->is_seq_hdr_ = false;
else if (tag_type_ == FLV_TAG_VIDEO)
header_len = 2 + 3;
data->av_type_ = MEDIA_VIDEO_TYPE;
if ((p[0]&0x0f) == FLV_VIDEO_H264_CODEC)
data->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 直播服务器简单实现 http_flv和hls 内网直播桌面