mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +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
|
@ -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…
Add table
Add a link
Reference in a new issue