Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
ZLMediaKit
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
张翔宇
ZLMediaKit
Commits
47add544
Unverified
Commit
47add544
authored
Jul 22, 2023
by
夏楚
Committed by
GitHub
Jul 22, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新增支持enhanced-rtmp h265 推流 (#2694)
parent
b44ca8fd
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
323 行增加
和
109 行删除
+323
-109
src/Extension/Factory.cpp
+1
-1
src/Extension/Factory.h
+10
-0
src/Extension/H264Rtmp.cpp
+0
-4
src/Extension/H265Rtmp.cpp
+129
-71
src/Extension/H265Rtmp.h
+11
-10
src/Rtmp/Rtmp.cpp
+49
-0
src/Rtmp/Rtmp.h
+67
-0
src/Rtmp/RtmpDemuxer.cpp
+39
-23
src/Rtmp/RtmpDemuxer.h
+3
-0
src/Rtmp/RtmpPlayer.cpp
+7
-0
src/Rtmp/RtmpPusher.cpp
+7
-0
没有找到文件。
src/Extension/Factory.cpp
查看文件 @
47add544
...
...
@@ -211,7 +211,7 @@ static CodecId getVideoCodecIdByAmf(const AMFValue &val){
return
CodecInvalid
;
}
Track
::
Ptr
getTrackByCodecId
(
CodecId
codecId
,
int
sample_rate
=
0
,
int
channels
=
0
,
int
sample_bit
=
0
)
{
Track
::
Ptr
Factory
::
getTrackByCodecId
(
CodecId
codecId
,
int
sample_rate
,
int
channels
,
int
sample_bit
)
{
switch
(
codecId
){
case
CodecH264
:
return
std
::
make_shared
<
H264Track
>
();
case
CodecH265
:
return
std
::
make_shared
<
H265Track
>
();
...
...
src/Extension/Factory.h
查看文件 @
47add544
...
...
@@ -21,6 +21,16 @@ namespace mediakit{
class
Factory
{
public
:
/**
* 根据codec_id 获取track
* @param codecId 编码id
* @param sample_rate 采样率,视频固定为90000
* @param channels 音频通道数
* @param sample_bit 音频采样位数
*/
static
Track
::
Ptr
getTrackByCodecId
(
CodecId
codecId
,
int
sample_rate
=
0
,
int
channels
=
0
,
int
sample_bit
=
0
);
////////////////////////////////rtsp相关//////////////////////////////////
/**
* 根据sdp生成Track对象
...
...
src/Extension/H264Rtmp.cpp
查看文件 @
47add544
...
...
@@ -164,12 +164,8 @@ bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
//not config
_rtmp_packet
->
buffer
[
1
]
=
true
;
int32_t
cts
=
pts
-
dts
;
if
(
cts
<
0
)
{
cts
=
0
;
}
//cts
set_be24
(
&
_rtmp_packet
->
buffer
[
2
],
cts
);
_rtmp_packet
->
time_stamp
=
dts
;
_rtmp_packet
->
body_size
=
_rtmp_packet
->
buffer
.
size
();
_rtmp_packet
->
chunk_id
=
CHUNK_VIDEO
;
...
...
src/Extension/H265Rtmp.cpp
查看文件 @
47add544
...
...
@@ -12,12 +12,12 @@
#include "H265Rtmp.h"
#ifdef ENABLE_MP4
#include "mpeg4-hevc.h"
#endif
//
ENABLE_MP4
#endif
//
ENABLE_MP4
using
namespace
std
;
using
namespace
toolkit
;
namespace
mediakit
{
namespace
mediakit
{
H265RtmpDecoder
::
H265RtmpDecoder
()
{
_h265frame
=
obtainFrame
();
...
...
@@ -30,11 +30,26 @@ H265Frame::Ptr H265RtmpDecoder::obtainFrame() {
}
#ifdef ENABLE_MP4
static
bool
decode_HEVCDecoderConfigurationRecord
(
uint8_t
*
extra
,
size_t
bytes
,
string
&
frame
)
{
struct
mpeg4_hevc_t
hevc
;
memset
(
&
hevc
,
0
,
sizeof
(
hevc
));
if
(
mpeg4_hevc_decoder_configuration_record_load
((
uint8_t
*
)
extra
,
bytes
,
&
hevc
)
>
0
)
{
uint8_t
*
config
=
new
uint8_t
[
bytes
*
2
];
int
size
=
mpeg4_hevc_to_nalu
(
&
hevc
,
config
,
bytes
*
2
);
if
(
size
>
4
)
{
frame
.
assign
((
char
*
)
config
+
4
,
size
-
4
);
}
delete
[]
config
;
return
size
>
4
;
}
return
false
;
}
/**
* 返回不带0x00 00 00 01头的sps
* @return
*/
static
bool
getH265ConfigFrame
(
const
RtmpPacket
&
thiz
,
string
&
frame
)
{
static
bool
getH265ConfigFrame
(
const
RtmpPacket
&
thiz
,
string
&
frame
)
{
if
(
thiz
.
getMediaType
()
!=
FLV_CODEC_H265
)
{
return
false
;
}
...
...
@@ -45,31 +60,78 @@ static bool getH265ConfigFrame(const RtmpPacket &thiz,string &frame) {
WarnL
<<
"bad H265 cfg!"
;
return
false
;
}
return
decode_HEVCDecoderConfigurationRecord
((
uint8_t
*
)
thiz
.
buffer
.
data
()
+
5
,
thiz
.
buffer
.
size
()
-
5
,
frame
);
}
#endif
auto
extra
=
thiz
.
buffer
.
data
()
+
5
;
auto
bytes
=
thiz
.
buffer
.
size
()
-
5
;
void
H265RtmpDecoder
::
inputRtmp
(
const
RtmpPacket
::
Ptr
&
pkt
)
{
if
(
_info
.
codec
==
CodecInvalid
)
{
// 先判断是否为增强型rtmp
parseVideoRtmpPacket
((
uint8_t
*
)
pkt
->
data
(),
pkt
->
size
(),
&
_info
);
}
struct
mpeg4_hevc_t
hevc
;
memset
(
&
hevc
,
0
,
sizeof
(
hevc
));
if
(
mpeg4_hevc_decoder_configuration_record_load
((
uint8_t
*
)
extra
,
bytes
,
&
hevc
)
>
0
)
{
uint8_t
*
config
=
new
uint8_t
[
bytes
*
2
];
int
size
=
mpeg4_hevc_to_nalu
(
&
hevc
,
config
,
bytes
*
2
);
if
(
size
>
4
)
{
frame
.
assign
((
char
*
)
config
+
4
,
size
-
4
);
if
(
_info
.
is_enhanced
)
{
// 增强型rtmp
parseVideoRtmpPacket
((
uint8_t
*
)
pkt
->
data
(),
pkt
->
size
(),
&
_info
);
if
(
!
_info
.
is_enhanced
||
_info
.
codec
!=
CodecH265
)
{
throw
std
::
invalid_argument
(
"Invalid enhanced-rtmp hevc packet!"
);
}
delete
[]
config
;
return
size
>
4
;
}
return
false
;
}
auto
data
=
(
uint8_t
*
)
pkt
->
data
()
+
5
;
auto
size
=
pkt
->
size
()
-
5
;
switch
(
_info
.
video
.
pkt_type
)
{
case
RtmpPacketType
:
:
PacketTypeSequenceStart
:
{
#ifdef ENABLE_MP4
string
config
;
if
(
decode_HEVCDecoderConfigurationRecord
(
data
,
size
,
config
))
{
onGetH265
(
config
.
data
(),
config
.
size
(),
pkt
->
time_stamp
,
pkt
->
time_stamp
);
}
#else
WarnL
<<
"请开启MP4相关功能并使能
\"
ENABLE_MP4
\"
,否则对H265-RTMP支持不完善"
;
#endif
break
;
}
void
H265RtmpDecoder
::
inputRtmp
(
const
RtmpPacket
::
Ptr
&
pkt
)
{
case
RtmpPacketType
:
:
PacketTypeCodedFramesX
:
case
RtmpPacketType
:
:
PacketTypeCodedFrames
:
{
auto
pts
=
pkt
->
time_stamp
;
if
(
RtmpPacketType
::
PacketTypeCodedFrames
==
_info
.
video
.
pkt_type
)
{
// SI24 = [CompositionTime Offset]
CHECK
(
size
>
7
);
int32_t
cts
=
(((
data
[
0
]
<<
16
)
|
(
data
[
1
]
<<
8
)
|
(
data
[
2
]))
+
0xff800000
)
^
0xff800000
;
pts
+=
cts
;
data
+=
3
;
size
-=
3
;
}
splitFrame
(
data
,
size
,
pkt
->
time_stamp
,
pts
);
break
;
}
case
RtmpPacketType
:
:
PacketTypeMetadata
:
{
// The body does not contain video data. The body is an AMF encoded metadata.
// The metadata will be represented by a series of [name, value] pairs.
// For now the only defined [name, value] pair is [“colorInfo”, Object]
// See Metadata Frame section for more details of this object.
//
// For a deeper understanding of the encoding please see description
// of SCRIPTDATA and SSCRIPTDATAVALUE in the FLV file spec.
// DATA = [“colorInfo”, Object]
break
;
}
case
RtmpPacketType
:
:
PacketTypeSequenceEnd
:
{
// signals end of sequence
break
;
}
default
:
break
;
}
return
;
}
// 国内扩展(12) H265 rtmp
if
(
pkt
->
isCfgFrame
())
{
#ifdef ENABLE_MP4
string
config
;
if
(
getH265ConfigFrame
(
*
pkt
,
config
))
{
onGetH265
(
config
.
data
(),
config
.
size
(),
pkt
->
time_stamp
,
pkt
->
time_stamp
);
if
(
getH265ConfigFrame
(
*
pkt
,
config
))
{
onGetH265
(
config
.
data
(),
config
.
size
(),
pkt
->
time_stamp
,
pkt
->
time_stamp
);
}
#else
WarnL
<<
"请开启MP4相关功能并使能
\"
ENABLE_MP4
\"
,否则对H265-RTMP支持不完善"
;
...
...
@@ -78,41 +140,42 @@ void H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
}
if
(
pkt
->
buffer
.
size
()
>
9
)
{
auto
total_len
=
pkt
->
buffer
.
size
();
size_t
offset
=
5
;
uint8_t
*
cts_ptr
=
(
uint8_t
*
)
(
pkt
->
buffer
.
data
()
+
2
);
uint8_t
*
cts_ptr
=
(
uint8_t
*
)(
pkt
->
buffer
.
data
()
+
2
);
int32_t
cts
=
(((
cts_ptr
[
0
]
<<
16
)
|
(
cts_ptr
[
1
]
<<
8
)
|
(
cts_ptr
[
2
]))
+
0xff800000
)
^
0xff800000
;
auto
pts
=
pkt
->
time_stamp
+
cts
;
while
(
offset
+
4
<
total_len
)
{
uint32_t
frame_len
;
memcpy
(
&
frame_len
,
pkt
->
buffer
.
data
()
+
offset
,
4
);
frame_len
=
ntohl
(
frame_len
);
offset
+=
4
;
if
(
frame_len
+
offset
>
total_len
)
{
break
;
}
onGetH265
(
pkt
->
buffer
.
data
()
+
offset
,
frame_len
,
pkt
->
time_stamp
,
pts
);
offset
+=
frame_len
;
splitFrame
((
uint8_t
*
)
pkt
->
data
()
+
5
,
pkt
->
size
()
-
5
,
pkt
->
time_stamp
,
pts
);
}
}
void
H265RtmpDecoder
::
splitFrame
(
const
uint8_t
*
data
,
size_t
size
,
uint32_t
dts
,
uint32_t
pts
)
{
auto
end
=
data
+
size
;
while
(
data
+
4
<
end
)
{
uint32_t
frame_len
=
load_be32
(
data
);
data
+=
4
;
if
(
data
+
frame_len
>
end
)
{
break
;
}
onGetH265
((
const
char
*
)
data
,
frame_len
,
dts
,
pts
);
data
+=
frame_len
;
}
}
inline
void
H265RtmpDecoder
::
onGetH265
(
const
char
*
pcData
,
size_t
iLen
,
uint32_t
dts
,
uint32_t
pts
)
{
if
(
iLen
==
0
)
{
inline
void
H265RtmpDecoder
::
onGetH265
(
const
char
*
data
,
size_t
size
,
uint32_t
dts
,
uint32_t
pts
)
{
if
(
size
==
0
)
{
return
;
}
#if 1
_h265frame
->
_dts
=
dts
;
_h265frame
->
_pts
=
pts
;
_h265frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
//
添加265头
_h265frame
->
_buffer
.
append
(
pcData
,
iLen
);
_h265frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
//
添加265头
_h265frame
->
_buffer
.
append
(
data
,
size
);
//写入环形缓存
//
写入环形缓存
RtmpCodec
::
inputFrame
(
_h265frame
);
_h265frame
=
obtainFrame
();
#else
//防止内存拷贝,这样产生的265帧不会有0x00 00 01头
auto
frame
=
std
::
make_shared
<
H265FrameNoCacheAble
>
((
char
*
)
pcData
,
iLen
,
dts
,
pts
,
0
);
//
防止内存拷贝,这样产生的265帧不会有0x00 00 01头
auto
frame
=
std
::
make_shared
<
H265FrameNoCacheAble
>
((
char
*
)
data
,
size
,
dts
,
pts
,
0
);
RtmpCodec
::
inputFrame
(
frame
);
#endif
}
...
...
@@ -123,16 +186,16 @@ H265RtmpEncoder::H265RtmpEncoder(const Track::Ptr &track) {
_track
=
dynamic_pointer_cast
<
H265Track
>
(
track
);
}
void
H265RtmpEncoder
::
makeConfigPacket
(){
void
H265RtmpEncoder
::
makeConfigPacket
()
{
if
(
_track
&&
_track
->
ready
())
{
//尝试从track中获取sps pps信息
//
尝试从track中获取sps pps信息
_sps
=
_track
->
getSps
();
_pps
=
_track
->
getPps
();
_vps
=
_track
->
getVps
();
}
if
(
!
_sps
.
empty
()
&&
!
_pps
.
empty
()
&&
!
_vps
.
empty
())
{
//获取到sps/pps
//
获取到sps/pps
makeVideoConfigPkt
();
_got_config_frame
=
true
;
}
...
...
@@ -175,20 +238,17 @@ bool H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if
(
!
_rtmp_packet
)
{
_rtmp_packet
=
RtmpPacket
::
create
();
//flags/not_config/cts预占位
//
flags/not_config/cts预占位
_rtmp_packet
->
buffer
.
resize
(
5
);
}
return
_merger
.
inputFrame
(
frame
,
[
this
](
uint64_t
dts
,
uint64_t
pts
,
const
Buffer
::
Ptr
&
,
bool
have_key_frame
)
{
//flags
//
flags
_rtmp_packet
->
buffer
[
0
]
=
FLV_CODEC_H265
|
((
have_key_frame
?
FLV_KEY_FRAME
:
FLV_INTER_FRAME
)
<<
4
);
//not config
//
not config
_rtmp_packet
->
buffer
[
1
]
=
true
;
int32_t
cts
=
pts
-
dts
;
if
(
cts
<
0
)
{
cts
=
0
;
}
//cts
// cts
set_be24
(
&
_rtmp_packet
->
buffer
[
2
],
cts
);
_rtmp_packet
->
time_stamp
=
dts
;
...
...
@@ -196,10 +256,10 @@ bool H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
_rtmp_packet
->
chunk_id
=
CHUNK_VIDEO
;
_rtmp_packet
->
stream_index
=
STREAM_MEDIA
;
_rtmp_packet
->
type_id
=
MSG_VIDEO
;
//输出rtmp packet
//
输出rtmp packet
RtmpCodec
::
inputRtmp
(
_rtmp_packet
);
_rtmp_packet
=
nullptr
;
},
&
_rtmp_packet
->
buffer
);
},
&
_rtmp_packet
->
buffer
);
}
void
H265RtmpEncoder
::
makeVideoConfigPkt
()
{
...
...
@@ -207,18 +267,16 @@ void H265RtmpEncoder::makeVideoConfigPkt() {
int8_t
flags
=
FLV_CODEC_H265
;
flags
|=
(
FLV_KEY_FRAME
<<
4
);
bool
is_config
=
true
;
auto
rtmpP
kt
=
RtmpPacket
::
create
();
//header
rtmpP
kt
->
buffer
.
push_back
(
flags
);
rtmpP
kt
->
buffer
.
push_back
(
!
is_config
);
//cts
rtmpP
kt
->
buffer
.
append
(
"\x0\x0\x0"
,
3
);
auto
p
kt
=
RtmpPacket
::
create
();
//
header
p
kt
->
buffer
.
push_back
(
flags
);
p
kt
->
buffer
.
push_back
(
!
is_config
);
//
cts
p
kt
->
buffer
.
append
(
"\x0\x0\x0"
,
3
);
struct
mpeg4_hevc_t
hevc
;
memset
(
&
hevc
,
0
,
sizeof
(
hevc
));
string
vps_sps_pps
=
string
(
"
\x00\x00\x00\x01
"
,
4
)
+
_vps
+
string
(
"
\x00\x00\x00\x01
"
,
4
)
+
_sps
+
string
(
"
\x00\x00\x00\x01
"
,
4
)
+
_pps
;
string
vps_sps_pps
=
string
(
"
\x00\x00\x00\x01
"
,
4
)
+
_vps
+
string
(
"
\x00\x00\x00\x01
"
,
4
)
+
_sps
+
string
(
"
\x00\x00\x00\x01
"
,
4
)
+
_pps
;
h265_annexbtomp4
(
&
hevc
,
vps_sps_pps
.
data
(),
(
int
)
vps_sps_pps
.
size
(),
NULL
,
0
,
NULL
,
NULL
);
uint8_t
extra_data
[
1024
];
int
extra_data_size
=
mpeg4_hevc_decoder_configuration_record_save
(
&
hevc
,
extra_data
,
sizeof
(
extra_data
));
...
...
@@ -226,17 +284,17 @@ void H265RtmpEncoder::makeVideoConfigPkt() {
WarnL
<<
"生成H265 extra_data 失败"
;
return
;
}
//HEVCDecoderConfigurationRecord
rtmpP
kt
->
buffer
.
append
((
char
*
)
extra_data
,
extra_data_size
);
rtmpPkt
->
body_size
=
rtmpP
kt
->
buffer
.
size
();
rtmpP
kt
->
chunk_id
=
CHUNK_VIDEO
;
rtmpP
kt
->
stream_index
=
STREAM_MEDIA
;
rtmpP
kt
->
time_stamp
=
0
;
rtmpP
kt
->
type_id
=
MSG_VIDEO
;
RtmpCodec
::
inputRtmp
(
rtmpP
kt
);
//
HEVCDecoderConfigurationRecord
p
kt
->
buffer
.
append
((
char
*
)
extra_data
,
extra_data_size
);
pkt
->
body_size
=
p
kt
->
buffer
.
size
();
p
kt
->
chunk_id
=
CHUNK_VIDEO
;
p
kt
->
stream_index
=
STREAM_MEDIA
;
p
kt
->
time_stamp
=
0
;
p
kt
->
type_id
=
MSG_VIDEO
;
RtmpCodec
::
inputRtmp
(
p
kt
);
#else
WarnL
<<
"请开启MP4相关功能并使能
\"
ENABLE_MP4
\"
,否则对H265-RTMP支持不完善"
;
#endif
}
}
//
namespace mediakit
}
//
namespace mediakit
src/Extension/H265Rtmp.h
查看文件 @
47add544
...
...
@@ -15,7 +15,7 @@
#include "Extension/Track.h"
#include "Extension/H265.h"
namespace
mediakit
{
namespace
mediakit
{
/**
* h265 Rtmp解码类
* 将 h265 over rtmp 解复用出 h265-Frame
...
...
@@ -25,7 +25,7 @@ public:
using
Ptr
=
std
::
shared_ptr
<
H265RtmpDecoder
>
;
H265RtmpDecoder
();
~
H265RtmpDecoder
()
{}
~
H265RtmpDecoder
()
=
default
;
/**
* 输入265 Rtmp包
...
...
@@ -33,22 +33,23 @@ public:
*/
void
inputRtmp
(
const
RtmpPacket
::
Ptr
&
rtmp
)
override
;
CodecId
getCodecId
()
const
override
{
return
CodecH265
;
}
CodecId
getCodecId
()
const
override
{
return
CodecH265
;
}
protected
:
void
onGetH265
(
const
char
*
pcData
,
size_t
iLen
,
uint32_t
dts
,
uint32_t
pts
);
H265Frame
::
Ptr
obtainFrame
();
void
onGetH265
(
const
char
*
data
,
size_t
size
,
uint32_t
dts
,
uint32_t
pts
);
void
splitFrame
(
const
uint8_t
*
data
,
size_t
size
,
uint32_t
dts
,
uint32_t
pts
);
protected
:
RtmpPacketInfo
_info
;
H265Frame
::
Ptr
_h265frame
;
};
/**
* 265 Rtmp打包类
*/
class
H265RtmpEncoder
:
public
H265RtmpDecoder
{
class
H265RtmpEncoder
:
public
H265RtmpDecoder
{
public
:
using
Ptr
=
std
::
shared_ptr
<
H265RtmpEncoder
>
;
...
...
@@ -87,9 +88,9 @@ private:
std
::
string
_pps
;
H265Track
::
Ptr
_track
;
RtmpPacket
::
Ptr
_rtmp_packet
;
FrameMerger
_merger
{
FrameMerger
::
mp4_nal_size
};
FrameMerger
_merger
{
FrameMerger
::
mp4_nal_size
};
};
}
//
namespace mediakit
}
//
namespace mediakit
#endif //ZLMEDIAKIT_H265RTMPCODEC_H
#endif //
ZLMEDIAKIT_H265RTMPCODEC_H
src/Rtmp/Rtmp.cpp
查看文件 @
47add544
...
...
@@ -258,6 +258,55 @@ void RtmpHandshake::random_generate(char *bytes, int size)
}
}
CodecId
parseVideoRtmpPacket
(
const
uint8_t
*
data
,
size_t
size
,
RtmpPacketInfo
*
info
)
{
RtmpPacketInfo
save
;
info
=
info
?
info
:
&
save
;
info
->
codec
=
CodecInvalid
;
CHECK
(
size
>
0
);
if
(
data
[
0
]
>>
7
==
1
)
{
// IsExHeader == 1
CHECK
(
size
>=
5
,
"Invalid rtmp buffer size: "
,
size
);
info
->
is_enhanced
=
true
;
info
->
video
.
frame_type
=
(
RtmpFrameType
)((
data
[
0
]
>>
4
)
&
0x07
);
info
->
video
.
pkt_type
=
(
RtmpPacketType
)(
data
[
0
]
&
0x0f
);
if
(
memcmp
(
data
+
1
,
"av01"
,
4
)
==
0
)
{
// AV1
info
->
codec
=
CodecAV1
;
}
else
if
(
memcmp
(
data
+
1
,
"vp09"
,
4
)
==
0
)
{
// VP9
info
->
codec
=
CodecVP9
;
}
else
if
(
memcmp
(
data
+
1
,
"hvc1"
,
4
)
==
0
)
{
// HEVC(H265)
info
->
codec
=
CodecH265
;
}
else
{
WarnL
<<
"Rtmp video codec not supported: "
<<
std
::
string
((
char
*
)
data
+
1
,
4
);
}
}
else
{
// IsExHeader == 0
info
->
is_enhanced
=
false
;
info
->
video
.
frame_type
=
(
RtmpFrameType
)(
data
[
0
]
>>
4
);
info
->
video
.
rtmp_codec
=
(
RtmpVideoCodec
)(
data
[
0
]
&
0x0f
);
switch
(
info
->
video
.
rtmp_codec
)
{
case
RtmpVideoCodec
:
:
h264
:
{
CHECK
(
size
>=
1
,
"Invalid rtmp buffer size: "
,
size
);
info
->
codec
=
CodecH264
;
info
->
video
.
h264_pkt_type
=
(
RtmpH264PacketType
)
data
[
1
];
break
;
}
case
RtmpVideoCodec
:
:
h265
:
{
CHECK
(
size
>=
1
,
"Invalid rtmp buffer size: "
,
size
);
info
->
codec
=
CodecH265
;
info
->
video
.
h264_pkt_type
=
(
RtmpH264PacketType
)
data
[
1
];
break
;
}
default
:
WarnL
<<
"Rtmp video codec not supported: "
<<
(
int
)
info
->
video
.
rtmp_codec
;
break
;
}
}
return
info
->
codec
;
}
}
//namespace mediakit
namespace
toolkit
{
...
...
src/Rtmp/Rtmp.h
查看文件 @
47add544
...
...
@@ -269,5 +269,72 @@ private:
//根据音频track获取flags
uint8_t
getAudioRtmpFlags
(
const
Track
::
Ptr
&
track
);
enum
class
RtmpFrameType
:
uint8_t
{
reserved
=
0
,
key_frame
=
1
,
// key frame (for AVC, a seekable frame)
inter_frame
=
2
,
// inter frame (for AVC, a non-seekable frame)
disposable_inter_frame
=
3
,
// disposable inter frame (H.263 only)
generated_key_frame
=
4
,
// generated key frame (reserved for server use only)
video_info_frame
=
5
,
// video info/command frame
};
enum
class
RtmpVideoCodec
:
uint8_t
{
h263
=
2
,
// Sorenson H.263
screen_video
=
3
,
// Screen video
vp6
=
4
,
// On2 VP6
vp6_alpha
=
5
,
// On2 VP6 with alpha channel
screen_video2
=
6
,
// Screen video version 2
h264
=
7
,
// avc
h265
=
12
,
// 国内扩展
};
enum
class
RtmpH264PacketType
:
uint8_t
{
h264_config_header
=
0
,
// AVC sequence header(sps/pps)
h264_nalu
=
1
,
// AVC NALU
h264_end_seq
=
2
,
// AVC end of sequence (lower level NALU sequence ender is not REQUIRED or supported)
};
enum
class
RtmpPacketType
:
uint8_t
{
PacketTypeSequenceStart
=
0
,
PacketTypeCodedFrames
=
1
,
PacketTypeSequenceEnd
=
2
,
// CompositionTime Offset is implied to equal zero. This is
// an optimization to save putting SI24 composition time value of zero on
// the wire. See pseudo code below in the VideoTagBody section
PacketTypeCodedFramesX
=
3
,
// VideoTagBody does not contain video data. VideoTagBody
// instead contains an AMF encoded metadata. See Metadata Frame
// section for an illustration of its usage. As an example, the metadata
// can be HDR information. This is a good way to signal HDR
// information. This also opens up future ways to express additional
// metadata that is meant for the next video sequence.
//
// note: presence of PacketTypeMetadata means that FrameType
// flags at the top of this table should be ignored
PacketTypeMetadata
=
4
,
// Carriage of bitstream in MPEG-2 TS format
// note: PacketTypeSequenceStart and PacketTypeMPEG2TSSequenceStart
// are mutually exclusive
PacketTypeMPEG2TSSequenceStart
=
5
,
};
struct
RtmpPacketInfo
{
CodecId
codec
=
CodecInvalid
;
bool
is_enhanced
;
union
{
struct
{
RtmpFrameType
frame_type
;
RtmpVideoCodec
rtmp_codec
;
RtmpPacketType
pkt_type
;
RtmpH264PacketType
h264_pkt_type
;
}
video
;
};
};
// https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf
CodecId
parseVideoRtmpPacket
(
const
uint8_t
*
data
,
size_t
size
,
RtmpPacketInfo
*
info
=
nullptr
);
}
//namespace mediakit
#endif//__rtmp_h
src/Rtmp/RtmpDemuxer.cpp
查看文件 @
47add544
...
...
@@ -19,12 +19,12 @@ size_t RtmpDemuxer::trackCount(const AMFValue &metadata) {
size_t
ret
=
0
;
metadata
.
object_for_each
([
&
](
const
string
&
key
,
const
AMFValue
&
val
)
{
if
(
key
==
"videocodecid"
)
{
//找到视频
//
找到视频
++
ret
;
return
;
}
if
(
key
==
"audiocodecid"
)
{
//找到音频
//
找到音频
++
ret
;
return
;
}
...
...
@@ -32,7 +32,7 @@ size_t RtmpDemuxer::trackCount(const AMFValue &metadata) {
return
ret
;
}
bool
RtmpDemuxer
::
loadMetaData
(
const
AMFValue
&
val
){
bool
RtmpDemuxer
::
loadMetaData
(
const
AMFValue
&
val
)
{
bool
ret
=
false
;
try
{
int
audiosamplerate
=
0
;
...
...
@@ -60,12 +60,12 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val){
return
;
}
if
(
key
==
"videocodecid"
)
{
//找到视频
//
找到视频
videocodecid
=
&
val
;
return
;
}
if
(
key
==
"audiocodecid"
)
{
//找到音频
//
找到音频
audiocodecid
=
&
val
;
return
;
}
...
...
@@ -75,16 +75,22 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val){
}
if
(
key
==
"videodatarate"
)
{
videodatarate
=
val
.
as_integer
();
_videodatarate
=
videodatarate
*
1024
;
return
;
}
});
if
(
videocodecid
)
{
//有视频
//
有视频
ret
=
true
;
makeVideoTrack
(
*
videocodecid
,
videodatarate
*
1024
);
if
(
videocodecid
->
type
()
==
AMF_NUMBER
&&
videocodecid
->
as_integer
()
==
(
int
)
RtmpVideoCodec
::
h264
)
{
// https://github.com/veovera/enhanced-rtmp/issues/8
_complete_delay
=
true
;
}
else
{
makeVideoTrack
(
*
videocodecid
,
videodatarate
*
1024
);
}
}
if
(
audiocodecid
)
{
//有音频
//
有音频
ret
=
true
;
makeAudioTrack
(
*
audiocodecid
,
audiosamplerate
,
audiochannels
,
audiosamplesize
,
audiodatarate
*
1024
);
}
...
...
@@ -92,8 +98,8 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val){
WarnL
<<
ex
.
what
();
}
if
(
ret
)
{
//metadata中存在track相关的描述,那么我们根据metadata判断有多少个track
if
(
ret
&&
!
_complete_delay
)
{
//
metadata中存在track相关的描述,那么我们根据metadata判断有多少个track
addTrackCompleted
();
}
return
ret
;
...
...
@@ -108,8 +114,14 @@ void RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
case
MSG_VIDEO
:
{
if
(
!
_try_get_video_track
)
{
_try_get_video_track
=
true
;
auto
codec
=
AMFValue
(
pkt
->
getMediaType
());
makeVideoTrack
(
codec
,
0
);
RtmpPacketInfo
info
;
auto
codec_id
=
parseVideoRtmpPacket
((
uint8_t
*
)
pkt
->
data
(),
pkt
->
size
(),
&
info
);
if
(
codec_id
!=
CodecInvalid
)
{
makeVideoTrack
(
Factory
::
getTrackByCodecId
(
codec_id
),
_videodatarate
);
if
(
_complete_delay
)
{
addTrackCompleted
();
}
}
}
if
(
_video_rtmp_decoder
)
{
_video_rtmp_decoder
->
inputRtmp
(
pkt
);
...
...
@@ -128,51 +140,55 @@ void RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) {
}
break
;
}
default
:
break
;
default
:
break
;
}
}
void
RtmpDemuxer
::
makeVideoTrack
(
const
AMFValue
&
videoCodec
,
int
bit_rate
)
{
makeVideoTrack
(
Factory
::
getVideoTrackByAmf
(
videoCodec
),
bit_rate
);
}
void
RtmpDemuxer
::
makeVideoTrack
(
const
Track
::
Ptr
&
track
,
int
bit_rate
)
{
if
(
_video_rtmp_decoder
)
{
return
;
}
//生成Track对象
_video_track
=
dynamic_pointer_cast
<
VideoTrack
>
(
Factory
::
getVideoTrackByAmf
(
videoCodec
)
);
//
生成Track对象
_video_track
=
dynamic_pointer_cast
<
VideoTrack
>
(
track
);
if
(
!
_video_track
)
{
return
;
}
//生成rtmpCodec对象以便解码rtmp
//
生成rtmpCodec对象以便解码rtmp
_video_rtmp_decoder
=
Factory
::
getRtmpCodecByTrack
(
_video_track
,
false
);
if
(
!
_video_rtmp_decoder
)
{
//找不到相应的rtmp解码器,该track无效
//
找不到相应的rtmp解码器,该track无效
_video_track
.
reset
();
return
;
}
_video_track
->
setBitRate
(
bit_rate
);
//设置rtmp解码器代理,生成的frame写入该Track
//
设置rtmp解码器代理,生成的frame写入该Track
_video_rtmp_decoder
->
addDelegate
(
_video_track
);
addTrack
(
_video_track
);
_try_get_video_track
=
true
;
}
void
RtmpDemuxer
::
makeAudioTrack
(
const
AMFValue
&
audioCodec
,
int
sample_rate
,
int
channels
,
int
sample_bit
,
int
bit_rate
)
{
void
RtmpDemuxer
::
makeAudioTrack
(
const
AMFValue
&
audioCodec
,
int
sample_rate
,
int
channels
,
int
sample_bit
,
int
bit_rate
)
{
if
(
_audio_rtmp_decoder
)
{
return
;
}
//生成Track对象
//
生成Track对象
_audio_track
=
dynamic_pointer_cast
<
AudioTrack
>
(
Factory
::
getAudioTrackByAmf
(
audioCodec
,
sample_rate
,
channels
,
sample_bit
));
if
(
!
_audio_track
)
{
return
;
}
//生成rtmpCodec对象以便解码rtmp
//
生成rtmpCodec对象以便解码rtmp
_audio_rtmp_decoder
=
Factory
::
getRtmpCodecByTrack
(
_audio_track
,
false
);
if
(
!
_audio_rtmp_decoder
)
{
//找不到相应的rtmp解码器,该track无效
//
找不到相应的rtmp解码器,该track无效
_audio_track
.
reset
();
return
;
}
_audio_track
->
setBitRate
(
bit_rate
);
//设置rtmp解码器代理,生成的frame写入该Track
//
设置rtmp解码器代理,生成的frame写入该Track
_audio_rtmp_decoder
->
addDelegate
(
_audio_track
);
addTrack
(
_audio_track
);
_try_get_audio_track
=
true
;
...
...
src/Rtmp/RtmpDemuxer.h
查看文件 @
47add544
...
...
@@ -45,12 +45,15 @@ public:
private
:
void
makeVideoTrack
(
const
AMFValue
&
val
,
int
bit_rate
);
void
makeVideoTrack
(
const
Track
::
Ptr
&
val
,
int
bit_rate
);
void
makeAudioTrack
(
const
AMFValue
&
val
,
int
sample_rate
,
int
channels
,
int
sample_bit
,
int
bit_rate
);
private
:
bool
_try_get_video_track
=
false
;
bool
_try_get_audio_track
=
false
;
bool
_complete_delay
=
false
;
float
_duration
=
0
;
int
_videodatarate
=
0
;
AudioTrack
::
Ptr
_audio_track
;
VideoTrack
::
Ptr
_video_track
;
RtmpCodec
::
Ptr
_audio_rtmp_decoder
;
...
...
src/Rtmp/RtmpPlayer.cpp
查看文件 @
47add544
...
...
@@ -191,6 +191,13 @@ void RtmpPlayer::send_connect() {
obj
.
set
(
"audioCodecs"
,
(
double
)
(
0x0400
));
//只支持H264
obj
.
set
(
"videoCodecs"
,
(
double
)
(
0x0080
));
AMFValue
fourCcList
(
AMF_STRICT_ARRAY
);
fourCcList
.
add
(
"av01"
);
fourCcList
.
add
(
"vp09"
);
fourCcList
.
add
(
"hvc1"
);
obj
.
set
(
"fourCcList"
,
fourCcList
);
sendInvoke
(
"connect"
,
obj
);
addOnResultCB
([
this
](
AMFDecoder
&
dec
)
{
//TraceL << "connect result";
...
...
src/Rtmp/RtmpPusher.cpp
查看文件 @
47add544
...
...
@@ -135,6 +135,13 @@ void RtmpPusher::send_connect() {
obj
.
set
(
"type"
,
"nonprivate"
);
obj
.
set
(
"tcUrl"
,
_tc_url
);
obj
.
set
(
"swfUrl"
,
_tc_url
);
AMFValue
fourCcList
(
AMF_STRICT_ARRAY
);
fourCcList
.
add
(
"av01"
);
fourCcList
.
add
(
"vp09"
);
fourCcList
.
add
(
"hvc1"
);
obj
.
set
(
"fourCcList"
,
fourCcList
);
sendInvoke
(
"connect"
,
obj
);
addOnResultCB
([
this
](
AMFDecoder
&
dec
)
{
//TraceL << "connect result";
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论