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 个修改的文件
包含
194 行增加
和
38 行删除
+194
-38
src/Extension/Factory.cpp
+1
-1
src/Extension/Factory.h
+10
-0
src/Extension/H264Rtmp.cpp
+0
-4
src/Extension/H265Rtmp.cpp
+0
-0
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
差异被折叠。
点击展开。
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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论