mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 11:51:57 +00:00
RTMP: Support enhanced RTMP specification for HEVC. v6.0.42 (#3495)
* RTMP: Support enhanced RTMP specification for HEVC, v6.0.42. * Player: Upgrade mpegts.js to support it. Enhanced RTMP specification: https://github.com/veovera/enhanced-rtmp First, start SRS `v6.0.42+` with HTTP-TS support: ```bash ./objs/srs -c conf/http.ts.live.conf ``` Then, you can use [OBS 29.1+](https://github.com/obsproject/obs-studio/releases) to push HEVC via RTMP. Start OBS with the following settings in the `Settings > Stream` tab: * Server: `rtmp://localhost/live` * Stream Key: `livestream` * Encoder: Please select the HEVC hardware encoder. Finally, open the player http://localhost:8080/players/srs_player.html?stream=livestream.ts Or use VLS or ffplay to play `http://localhost:8080/live/livestream.ts` --------- Co-authored-by: chundonglinlin <chundonglinlin@163.com>
This commit is contained in:
parent
dcd02fe69c
commit
26aabe413d
10 changed files with 351 additions and 78 deletions
|
@ -8,8 +8,9 @@ The changelog for SRS.
|
|||
|
||||
## SRS 6.0 Changelog
|
||||
|
||||
* v5.0, 2023-04-01, Merge [#3392](https://github.com/ossrs/srs/pull/3392): Support composited bridges for 1:N protocols converting. v6.0.41 (#3392)
|
||||
* v5.0, 2023-04-01, Merge [#3458](https://github.com/ossrs/srs/pull/3450): API: Support HTTP basic authentication for API. v6.0.40 (#3458)
|
||||
* v6.0, 2023-04-08, Merge [#3495](https://github.com/ossrs/srs/pull/3495): RTMP: Support enhanced RTMP specification for HEVC. v6.0.42 (#3495)
|
||||
* v6.0, 2023-04-01, Merge [#3392](https://github.com/ossrs/srs/pull/3392): Support composited bridges for 1:N protocols converting. v6.0.41 (#3392)
|
||||
* v6.0, 2023-04-01, Merge [#3458](https://github.com/ossrs/srs/pull/3450): API: Support HTTP basic authentication for API. v6.0.40 (#3458)
|
||||
* v6.0, 2023-03-27, Merge [#3450](https://github.com/ossrs/srs/pull/3450): WebRTC: Error message carries the SDP when failed. v6.0.39 (#3450)
|
||||
* v6.0, 2023-03-25, Merge [#3477](https://github.com/ossrs/srs/pull/3477): Remove unnecessary NULL check in srs_freep. v6.0.38 (#3477)
|
||||
* v6.0, 2023-03-25, Merge [#3455](https://github.com/ossrs/srs/pull/3455): RTC: Call on_play before create session, for it might be freed for timeout. v6.0.37 (#3455)
|
||||
|
|
File diff suppressed because one or more lines are too long
9
trunk/research/players/js/mpegts-1.7.3.min.js
vendored
Normal file
9
trunk/research/players/js/mpegts-1.7.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -102,7 +102,7 @@
|
|||
</body>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/mpegts-1.7.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/mpegts-1.7.3.min.js"></script>
|
||||
<script type="text/javascript" src="js/hls-0.14.17.min.js"></script>
|
||||
<script type="text/javascript" src="js/dash-v4.5.1.all.min.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 6
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 41
|
||||
#define VERSION_REVISION 42
|
||||
|
||||
#endif
|
||||
|
|
|
@ -153,8 +153,10 @@ bool SrsFlvVideo::keyframe(char* data, int size)
|
|||
if (size < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char frame_type = data[0];
|
||||
|
||||
// See rtmp_specification_1.0.pdf
|
||||
// See https://github.com/veovera/enhanced-rtmp
|
||||
uint8_t frame_type = data[0] & 0x7f;
|
||||
frame_type = (frame_type >> 4) & 0x0F;
|
||||
|
||||
return frame_type == SrsVideoAvcFrameTypeKeyFrame;
|
||||
|
@ -173,14 +175,23 @@ bool SrsFlvVideo::sh(char* data, int size)
|
|||
if (size < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char frame_type = data[0];
|
||||
frame_type = (frame_type >> 4) & 0x0F;
|
||||
|
||||
char avc_packet_type = data[1];
|
||||
|
||||
|
||||
uint8_t frame_type = data[0];
|
||||
bool is_ext_header = frame_type & 0x80;
|
||||
SrsVideoAvcFrameTrait avc_packet_type = SrsVideoAvcFrameTraitForbidden;
|
||||
if (!is_ext_header) {
|
||||
// See rtmp_specification_1.0.pdf
|
||||
frame_type = (frame_type >> 4) & 0x0F;
|
||||
avc_packet_type = (SrsVideoAvcFrameTrait)data[1];
|
||||
} else {
|
||||
// See https://github.com/veovera/enhanced-rtmp
|
||||
avc_packet_type = (SrsVideoAvcFrameTrait)(frame_type & 0x0f);
|
||||
frame_type = (frame_type >> 4) & 0x07;
|
||||
}
|
||||
|
||||
// Note that SrsVideoHEVCFrameTraitPacketTypeSequenceStart is equal to SrsVideoAvcFrameTraitSequenceHeader
|
||||
return frame_type == SrsVideoAvcFrameTypeKeyFrame
|
||||
&& avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader;
|
||||
&& avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader;
|
||||
}
|
||||
|
||||
bool SrsFlvVideo::h264(char* data, int size)
|
||||
|
@ -204,8 +215,24 @@ bool SrsFlvVideo::hevc(char* data, int size)
|
|||
return false;
|
||||
}
|
||||
|
||||
char codec_id = data[0];
|
||||
codec_id = codec_id & 0x0F;
|
||||
uint8_t frame_type = data[0];
|
||||
bool is_ext_header = frame_type & 0x80;
|
||||
SrsVideoCodecId codec_id = SrsVideoCodecIdForbidden;
|
||||
if (!is_ext_header) {
|
||||
// See rtmp_specification_1.0.pdf
|
||||
codec_id = (SrsVideoCodecId)(frame_type & 0x0F);
|
||||
} else {
|
||||
// See https://github.com/veovera/enhanced-rtmp
|
||||
if (size < 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Video FourCC
|
||||
if (data[1] != 'h' || data[2] != 'v' || data[3] != 'c' || data[4] != '1') {
|
||||
return false;
|
||||
}
|
||||
codec_id = SrsVideoCodecIdHEVC;
|
||||
}
|
||||
|
||||
return codec_id == SrsVideoCodecIdHEVC;
|
||||
}
|
||||
|
@ -218,12 +245,34 @@ bool SrsFlvVideo::acceptable(char* data, int size)
|
|||
return false;
|
||||
}
|
||||
|
||||
char frame_type = data[0];
|
||||
SrsVideoCodecId codec_id = (SrsVideoCodecId)(uint8_t)(frame_type & 0x0f);
|
||||
frame_type = (frame_type >> 4) & 0x0f;
|
||||
|
||||
if (frame_type < 1 || frame_type > 5) {
|
||||
return false;
|
||||
uint8_t frame_type = data[0];
|
||||
bool is_ext_header = frame_type & 0x80;
|
||||
SrsVideoCodecId codec_id = SrsVideoCodecIdForbidden;
|
||||
if (!is_ext_header) {
|
||||
// See rtmp_specification_1.0.pdf
|
||||
codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
|
||||
frame_type = (frame_type >> 4) & 0x0f;
|
||||
|
||||
if (frame_type < 1 || frame_type > 5) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// See https://github.com/veovera/enhanced-rtmp
|
||||
uint8_t packet_type = frame_type & 0x0f;
|
||||
frame_type = (frame_type >> 4) & 0x07;
|
||||
|
||||
if (packet_type > SrsVideoHEVCFrameTraitPacketTypeMPEG2TSSequenceStart || frame_type > SrsVideoAvcFrameTypeVideoInfoFrame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size < 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data[1] != 'h' || data[2] != 'v' || data[3] != 'c' || data[4] != '1') {
|
||||
return false;
|
||||
}
|
||||
codec_id = SrsVideoCodecIdHEVC;
|
||||
}
|
||||
|
||||
if (codec_id != SrsVideoCodecIdAVC && codec_id != SrsVideoCodecIdAV1 && codec_id != SrsVideoCodecIdHEVC) {
|
||||
|
@ -775,33 +824,7 @@ srs_error_t SrsFormat::on_video(int64_t timestamp, char* data, int size)
|
|||
|
||||
SrsBuffer* buffer = new SrsBuffer(data, size);
|
||||
SrsAutoFree(SrsBuffer, buffer);
|
||||
|
||||
// We already checked the size is positive and data is not NULL.
|
||||
srs_assert(buffer->require(1));
|
||||
|
||||
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
||||
int8_t frame_type = buffer->read_1bytes();
|
||||
SrsVideoCodecId codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
|
||||
|
||||
// Check codec for H.264 and H.265.
|
||||
bool codec_ok = (codec_id == SrsVideoCodecIdAVC);
|
||||
#ifdef SRS_H265
|
||||
codec_ok = codec_ok ? true : (codec_id == SrsVideoCodecIdHEVC);
|
||||
#endif
|
||||
if (!codec_ok) return err;
|
||||
|
||||
if (!vcodec) {
|
||||
vcodec = new SrsVideoCodecConfig();
|
||||
}
|
||||
if (!video) {
|
||||
video = new SrsVideoFrame();
|
||||
}
|
||||
|
||||
if ((err = video->initialize(vcodec)) != srs_success) {
|
||||
return srs_error_wrap(err, "init video");
|
||||
}
|
||||
|
||||
buffer->skip(-1 * buffer->pos());
|
||||
return video_avc_demux(buffer, timestamp);
|
||||
}
|
||||
|
||||
|
@ -847,18 +870,55 @@ bool SrsFormat::is_avc_sequence_header()
|
|||
srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
||||
if (!stream->require(1)) {
|
||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "video avc demux shall atleast 1bytes");
|
||||
}
|
||||
|
||||
// Parse the frame type and the first bit indicates the ext header.
|
||||
uint8_t frame_type = stream->read_1bytes();
|
||||
bool is_ext_header = frame_type & 0x80;
|
||||
|
||||
// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
|
||||
int8_t frame_type = stream->read_1bytes();
|
||||
SrsVideoCodecId codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
|
||||
frame_type = (frame_type >> 4) & 0x0f;
|
||||
|
||||
SrsVideoCodecId codec_id = SrsVideoCodecIdForbidden;
|
||||
SrsVideoAvcFrameTrait packet_type = SrsVideoAvcFrameTraitForbidden;
|
||||
if (!is_ext_header) {
|
||||
// See rtmp_specification_1.0.pdf
|
||||
codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
|
||||
frame_type = (frame_type >> 4) & 0x0f;
|
||||
} else {
|
||||
// See https://github.com/veovera/enhanced-rtmp
|
||||
packet_type = (SrsVideoAvcFrameTrait)(frame_type & 0x0f);
|
||||
frame_type = (frame_type >> 4) & 0x07;
|
||||
|
||||
if (!stream->require(4)) {
|
||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "fourCC requires 4bytes, only %dbytes", stream->left());
|
||||
}
|
||||
|
||||
uint32_t four_cc = stream->read_4bytes();
|
||||
if (four_cc == 0x68766331) { // 'hvc1'=0x68766331
|
||||
codec_id = SrsVideoCodecIdHEVC;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vcodec) {
|
||||
vcodec = new SrsVideoCodecConfig();
|
||||
}
|
||||
|
||||
if (!video) {
|
||||
video = new SrsVideoFrame();
|
||||
}
|
||||
|
||||
if ((err = video->initialize(vcodec)) != srs_success) {
|
||||
return srs_error_wrap(err, "init video");
|
||||
}
|
||||
|
||||
video->frame_type = (SrsVideoAvcFrameType)frame_type;
|
||||
|
||||
// ignore info frame without error,
|
||||
// @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909
|
||||
if (video->frame_type == SrsVideoAvcFrameTypeVideoInfoFrame) {
|
||||
srs_warn("avc igone the info frame");
|
||||
srs_warn("avc ignore the info frame");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -871,17 +931,29 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
|||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "only support video H.264/H.265, actual=%d", codec_id);
|
||||
}
|
||||
vcodec->id = codec_id;
|
||||
|
||||
if (!stream->require(4)) {
|
||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "avc decode avc_packet_type");
|
||||
|
||||
int32_t composition_time = 0;
|
||||
if (!is_ext_header) {
|
||||
// See rtmp_specification_1.0.pdf
|
||||
if (!stream->require(4)) {
|
||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires 4bytes, only %dbytes", stream->left());
|
||||
}
|
||||
packet_type = (SrsVideoAvcFrameTrait)stream->read_1bytes();
|
||||
composition_time = stream->read_3bytes();
|
||||
} else {
|
||||
// See https://github.com/veovera/enhanced-rtmp
|
||||
if (packet_type == SrsVideoHEVCFrameTraitPacketTypeCodedFrames) {
|
||||
if (!stream->require(3)) {
|
||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "requires 3 bytes, only %dbytes", stream->left());
|
||||
}
|
||||
composition_time = stream->read_3bytes();
|
||||
}
|
||||
}
|
||||
int8_t avc_packet_type = stream->read_1bytes();
|
||||
int32_t composition_time = stream->read_3bytes();
|
||||
|
||||
|
||||
// pts = dts + cts.
|
||||
video->dts = timestamp;
|
||||
video->cts = composition_time;
|
||||
video->avc_packet_type = (SrsVideoAvcFrameTrait)avc_packet_type;
|
||||
video->avc_packet_type = packet_type;
|
||||
|
||||
// Update the RAW AVC data.
|
||||
raw = stream->data() + stream->pos();
|
||||
|
@ -890,12 +962,12 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
|||
// Parse sequence header for H.265/HEVC.
|
||||
if (codec_id == SrsVideoCodecIdHEVC) {
|
||||
#ifdef SRS_H265
|
||||
if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||
if (packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||
// TODO: demux vps/sps/pps for hevc
|
||||
if ((err = hevc_demux_hvcc(stream)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux hevc VPS/SPS/PPS");
|
||||
}
|
||||
} else if (avc_packet_type == SrsVideoAvcFrameTraitNALU) {
|
||||
} else if (packet_type == SrsVideoAvcFrameTraitNALU || packet_type == SrsVideoHEVCFrameTraitPacketTypeCodedFramesX) {
|
||||
// TODO: demux nalu for hevc
|
||||
if ((err = video_nalu_demux(stream)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux hevc NALU");
|
||||
|
@ -908,12 +980,12 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
|
|||
}
|
||||
|
||||
// Parse sequence header for H.264/AVC.
|
||||
if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||
if (packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
|
||||
// TODO: FIXME: Maybe we should ignore any error for parsing sps/pps.
|
||||
if ((err = avc_demux_sps_pps(stream)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux SPS/PPS");
|
||||
}
|
||||
} else if (avc_packet_type == SrsVideoAvcFrameTraitNALU){
|
||||
} else if (packet_type == SrsVideoAvcFrameTraitNALU){
|
||||
if ((err = video_nalu_demux(stream)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux NALU");
|
||||
}
|
||||
|
|
|
@ -64,12 +64,32 @@ std::string srs_video_codec_id2str(SrsVideoCodecId codec);
|
|||
enum SrsVideoAvcFrameTrait
|
||||
{
|
||||
// set to the max value to reserved, for array map.
|
||||
SrsVideoAvcFrameTraitReserved = 3,
|
||||
SrsVideoAvcFrameTraitForbidden = 3,
|
||||
SrsVideoAvcFrameTraitReserved = 6,
|
||||
SrsVideoAvcFrameTraitForbidden = 6,
|
||||
|
||||
SrsVideoAvcFrameTraitSequenceHeader = 0,
|
||||
SrsVideoAvcFrameTraitNALU = 1,
|
||||
SrsVideoAvcFrameTraitSequenceHeaderEOF = 2,
|
||||
|
||||
SrsVideoHEVCFrameTraitPacketTypeSequenceStart = 0,
|
||||
SrsVideoHEVCFrameTraitPacketTypeCodedFrames = 1,
|
||||
SrsVideoHEVCFrameTraitPacketTypeSequenceEnd = 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
|
||||
SrsVideoHEVCFrameTraitPacketTypeCodedFramesX = 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
|
||||
SrsVideoHEVCFrameTraitPacketTypeMetadata = 4,
|
||||
// Carriage of bitstream in MPEG-2 TS format
|
||||
SrsVideoHEVCFrameTraitPacketTypeMPEG2TSSequenceStart = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -3897,7 +3897,8 @@ VOID TEST(KernelCodecTest, VideoFormat)
|
|||
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 1));
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x00", 1));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x57", 1));
|
||||
}
|
||||
|
||||
if (true) {
|
||||
|
@ -4001,7 +4002,16 @@ VOID TEST(KernelCodecTest, HevcVideoFormat)
|
|||
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x00", 1));
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x00", 1));
|
||||
|
||||
// enhanced rtmp/flv
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, NULL, 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x80", 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x90", 0));
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\xd0\x68\x76\x63\x31", 5));
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x80", 1));
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x90", 1));
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x90\x68\x76\x63\x31", 5));
|
||||
}
|
||||
|
||||
if (true) {
|
||||
|
@ -4010,11 +4020,22 @@ VOID TEST(KernelCodecTest, HevcVideoFormat)
|
|||
|
||||
//HEVC: 0x5c
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)"\x5c", 1));
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*)"\x1c", 1));
|
||||
|
||||
// CodecId: 0x00
|
||||
SrsBuffer b((char*)"\x00", 1);
|
||||
srs_error_t err = f.video_avc_demux(&b, 0);
|
||||
HELPER_EXPECT_FAILED(err);
|
||||
|
||||
// enhanced rtmp/flv
|
||||
SrsBuffer b1((char*)"\x80", 1);
|
||||
HELPER_EXPECT_FAILED(f.video_avc_demux(&b1, 0));
|
||||
SrsBuffer b2((char*)"\x90", 1);
|
||||
HELPER_EXPECT_FAILED(f.video_avc_demux(&b2, 0));
|
||||
SrsBuffer b3((char*)"\x90\x68\x76\x63\x31", 5);
|
||||
HELPER_EXPECT_FAILED(f.video_avc_demux(&b3, 0));
|
||||
SrsBuffer b4((char*)"\xd0\x68\x76\x63\x31", 5);
|
||||
HELPER_EXPECT_SUCCESS(f.video_avc_demux(&b4, 0));
|
||||
}
|
||||
|
||||
uint8_t vps_sps_pps[] = {
|
||||
|
@ -4075,6 +4096,93 @@ VOID TEST(KernelCodecTest, HevcVideoFormat)
|
|||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)rawIBMF, sizeof(rawIBMF)));
|
||||
EXPECT_EQ(1, f.video->nb_samples);
|
||||
}
|
||||
|
||||
// enhanced rtmp
|
||||
uint8_t ext_vps_sps_pps[] = {
|
||||
// IsExHeader | FrameType: UB[4]
|
||||
// PacketType: UB[4]
|
||||
0x90,
|
||||
// Video FourCC
|
||||
0x68, 0x76, 0x63, 0x31,
|
||||
// SrsHevcDecoderConfigurationRecord
|
||||
0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f, 0x03,
|
||||
// Nalus
|
||||
// data_byte(1B)+num_nalus(2B)+nal_unit_length(2B)
|
||||
0x20, 0x00, 0x01, 0x00, 0x18,
|
||||
// VPS
|
||||
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0x95, 0x98, 0x09,
|
||||
// data_byte(1B)+num_nalus(2B)+nal_unit_length(2B)
|
||||
0x21, 0x00, 0x01, 0x00, 0x28,
|
||||
// SPS
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x02, 0x80, 0x80, 0x2d, 0x16,
|
||||
0x59, 0x59, 0xa4, 0x93, 0x2b, 0xc0, 0x40, 0x40, 0x00, 0x00, 0xfa, 0x40, 0x00, 0x17, 0x70, 0x02,
|
||||
// data_byte(1B)+num_nalus(2B)+nal_unit_length(2B)
|
||||
0x22, 0x00, 0x01, 0x00, 0x07,
|
||||
// PPS
|
||||
0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62, 0x40
|
||||
};
|
||||
|
||||
uint8_t ext_rawIBMF[] = {
|
||||
// IsExHeader | FrameType: UB[4]
|
||||
// PacketType: UB[4]
|
||||
0x93,
|
||||
// Video FourCC
|
||||
0x68, 0x76, 0x63, 0x31,
|
||||
// HEVC NALU
|
||||
0x00, 0x00, 0x00, 0x0b,
|
||||
0x28, 0x1, 0xaf, 0x1d, 0x18, 0x38, 0xd4, 0x38, 0x32, 0xda, 0x23
|
||||
};
|
||||
|
||||
uint8_t ext_rawIBMF1[] = {
|
||||
// IsExHeader | FrameType: UB[4]
|
||||
// PacketType: UB[4]
|
||||
0x91,
|
||||
// Video FourCC
|
||||
0x68, 0x76, 0x63, 0x31,
|
||||
// CompositionTime Offset
|
||||
0x00, 0x00, 0x7d,
|
||||
// HEVC NALU
|
||||
0x00, 0x00, 0x00, 0x0b,
|
||||
0x28, 0x1, 0xaf, 0x1d, 0x18, 0x38, 0xd4, 0x38, 0x32, 0xda, 0x23
|
||||
};
|
||||
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_EXPECT_SUCCESS(f.initialize());
|
||||
|
||||
// firstly demux sequence header
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_vps_sps_pps, sizeof(ext_vps_sps_pps)));
|
||||
EXPECT_EQ(1, f.video->frame_type);
|
||||
EXPECT_EQ(0, f.video->avc_packet_type);
|
||||
EXPECT_EQ(3, f.vcodec->hevc_dec_conf_record_.nalu_vec.size());
|
||||
EXPECT_EQ(1280, f.vcodec->width);
|
||||
EXPECT_EQ(720, f.vcodec->height);
|
||||
|
||||
// secondly demux sequence header
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_vps_sps_pps, sizeof(ext_vps_sps_pps)));
|
||||
EXPECT_EQ(1, f.video->frame_type);
|
||||
EXPECT_EQ(0, f.video->avc_packet_type);
|
||||
EXPECT_EQ(3, f.vcodec->hevc_dec_conf_record_.nalu_vec.size());
|
||||
EXPECT_EQ(1280, f.vcodec->width);
|
||||
EXPECT_EQ(720, f.vcodec->height);
|
||||
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_rawIBMF, sizeof(ext_rawIBMF)));
|
||||
EXPECT_EQ(1, f.video->frame_type);
|
||||
EXPECT_EQ(3, f.video->avc_packet_type);
|
||||
EXPECT_EQ(1, f.video->nb_samples);
|
||||
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_rawIBMF, sizeof(ext_rawIBMF)));
|
||||
EXPECT_EQ(1, f.video->frame_type);
|
||||
EXPECT_EQ(3, f.video->avc_packet_type);
|
||||
EXPECT_EQ(1, f.video->nb_samples);
|
||||
|
||||
// check cts
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*)ext_rawIBMF1, sizeof(ext_rawIBMF1)));
|
||||
EXPECT_EQ(1, f.video->frame_type);
|
||||
EXPECT_EQ(125, f.video->cts);
|
||||
EXPECT_EQ(1, f.video->avc_packet_type);
|
||||
EXPECT_EQ(1, f.video->nb_samples);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -411,3 +411,75 @@ VOID TEST(KernelFileWriterTest, RealfileTest)
|
|||
EXPECT_STREQ("HelloWorld", str.substr(20).c_str());
|
||||
}
|
||||
|
||||
VOID TEST(KernelRTMPExtTest, ExtRTMPTest)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// For legacy RTMP specification, without ext tag header.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*) "\x17\x01\x00\x00\x12", 5));
|
||||
|
||||
// Verify the frame type, codec id, avc packet type and composition time.
|
||||
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, f.video->frame_type);
|
||||
EXPECT_EQ(SrsVideoCodecIdAVC, f.vcodec->id);
|
||||
EXPECT_EQ(SrsVideoAvcFrameTraitNALU, f.video->avc_packet_type);
|
||||
EXPECT_EQ(0x12, f.video->cts);
|
||||
}
|
||||
|
||||
// For new RTMP enhanced specification, with ext tag header.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*) "\x91hvc1\x00\x00\x12", 8));
|
||||
|
||||
// Verify the frame type, codec id, avc packet type and composition time.
|
||||
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, f.video->frame_type);
|
||||
EXPECT_EQ(SrsVideoCodecIdHEVC, f.vcodec->id);
|
||||
EXPECT_EQ(SrsVideoHEVCFrameTraitPacketTypeCodedFrames, f.video->avc_packet_type);
|
||||
EXPECT_EQ(0x12, f.video->cts);
|
||||
}
|
||||
|
||||
// If packet type is 3, which is coded frame X, the composition time is 0.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_SUCCESS(f.on_video(0, (char*) "\x93hvc1", 5));
|
||||
|
||||
// Verify the frame type, codec id, avc packet type and composition time.
|
||||
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, f.video->frame_type);
|
||||
EXPECT_EQ(SrsVideoCodecIdHEVC, f.vcodec->id);
|
||||
EXPECT_EQ(SrsVideoHEVCFrameTraitPacketTypeCodedFramesX, f.video->avc_packet_type);
|
||||
EXPECT_EQ(0, f.video->cts);
|
||||
}
|
||||
|
||||
// Should fail if only 1 byte for ext tag header, should be more bytes for fourcc.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x91", 1));
|
||||
}
|
||||
|
||||
// Should fail if only 5 bytes for ext tag header, should be more bytes for fourcc.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x91hvc1", 5));
|
||||
}
|
||||
|
||||
// Should fail if codec id is hvc2 for ext tag header, should be hvc1.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x93hvc2", 5));
|
||||
}
|
||||
|
||||
// Should fail if codec id is mvc1 for ext tag header, should be hvc1.
|
||||
if (true) {
|
||||
SrsFormat f;
|
||||
HELPER_ASSERT_SUCCESS(f.initialize());
|
||||
HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x93mvc1", 5));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue