diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 6fa125e5d..494f0749b 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 7.0 Changelog +* v7.0, 2025-05-29, Merge [#4356](https://github.com/ossrs/srs/pull/4356): RTMP: Use extended timestamp as delta when chunk fmt=1/2. v7.0.37 (#4356) * v7.0, 2025-05-29, Merge [#4363](https://github.com/ossrs/srs/pull/4363): Fix error about TestRtcPublish_HttpFlvPlay. v7.0.36 (#4363) * v7.0, 2025-05-29, Merge [#4362](https://github.com/ossrs/srs/pull/4362): Update VSCode launch configuration to support GDB on Linux and LLDB on macOS. v7.0.35 (#4362) * v7.0, 2025-05-26, Merge [#4359](https://github.com/ossrs/srs/pull/4359): update pion/webrtc to v4. v7.0.34 (#4359) @@ -47,6 +48,7 @@ The changelog for SRS. ## SRS 6.0 Changelog +* v6.0, 2025-05-29, Merge [#4356](https://github.com/ossrs/srs/pull/4356): RTMP: Use extended timestamp as delta when chunk fmt=1/2. v6.0.167 (#4356) * v6.0, 2025-03-21, Merge [#4303](https://github.com/ossrs/srs/pull/4303): replace values with enums. v6.0.165 (#4303) * v6.0, 2025-03-20, Merge [#4305](https://github.com/ossrs/srs/pull/4305): free sample to prevent memory leak. v6.0.164 (#4305) * v6.0, 2025-03-18, Merge [#4302](https://github.com/ossrs/srs/pull/4302): update geekyeggo/delete-artifact to 5.0.0. v6.0.163 (#4302) diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index a1e79e215..af0474289 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 166 +#define VERSION_REVISION 167 #endif diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index 922f9586e..4ec17a9cf 100644 --- a/trunk/src/core/srs_core_version7.hpp +++ b/trunk/src/core/srs_core_version7.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 7 #define VERSION_MINOR 0 -#define VERSION_REVISION 36 +#define VERSION_REVISION 37 #endif \ No newline at end of file diff --git a/trunk/src/protocol/srs_protocol_rtmp_stack.cpp b/trunk/src/protocol/srs_protocol_rtmp_stack.cpp index 15c21ce5a..7c0c0c5c7 100644 --- a/trunk/src/protocol/srs_protocol_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_protocol_rtmp_stack.cpp @@ -1032,8 +1032,8 @@ srs_error_t SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) // 0x00ffffff), this value MUST be 16777215, and the 'extended // timestamp header' MUST be present. Otherwise, this value SHOULD be // the entire delta. - chunk->extended_timestamp = (chunk->header.timestamp_delta >= RTMP_EXTENDED_TIMESTAMP); - if (!chunk->extended_timestamp) { + chunk->has_extended_timestamp = (chunk->header.timestamp_delta >= RTMP_EXTENDED_TIMESTAMP); + if (!chunk->has_extended_timestamp) { // Extended timestamp: 0 or 4 bytes // This field MUST be sent when the normal timsestamp is set to // 0xffffff, it MUST NOT be sent if the normal timestamp is set to @@ -1085,13 +1085,13 @@ srs_error_t SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) } } else { // update the timestamp even fmt=3 for first chunk packet - if (is_first_chunk_of_msg && !chunk->extended_timestamp) { + if (is_first_chunk_of_msg && !chunk->has_extended_timestamp) { chunk->header.timestamp += chunk->header.timestamp_delta; } } // read extended-timestamp - if (chunk->extended_timestamp) { + if (chunk->has_extended_timestamp) { mh_size += 4; if ((err = in_buffer->grow(skt, 4)) != srs_success) { return srs_error_wrap(err, "read 4 bytes ext timestamp"); @@ -1127,7 +1127,7 @@ srs_error_t SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) * @remark, srs always send the extended-timestamp, to keep simple, * and compatible with adobe products. */ - uint32_t chunk_timestamp = (uint32_t)chunk->header.timestamp; + uint32_t chunk_extended_timestamp = (uint32_t)chunk->extended_timestamp; /** * if chunk_timestamp<=0, the chunk previous packet has no extended-timestamp, @@ -1137,11 +1137,16 @@ srs_error_t SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) * about the is_first_chunk_of_msg. * @remark, for the first chunk of message, always use the extended timestamp. */ - if (!is_first_chunk_of_msg && chunk_timestamp > 0 && chunk_timestamp != timestamp) { + if (!is_first_chunk_of_msg && chunk_extended_timestamp > 0 && chunk_extended_timestamp != timestamp) { mh_size -= 4; in_buffer->skip(-4); } else { - chunk->header.timestamp = timestamp; + chunk->extended_timestamp = timestamp; + if (fmt == RTMP_FMT_TYPE0) { + chunk->header.timestamp = timestamp; + } else if (is_first_chunk_of_msg) { + chunk->header.timestamp += timestamp; + } } } @@ -1439,9 +1444,10 @@ SrsChunkStream::SrsChunkStream(int _cid) { fmt = 0; cid = _cid; - extended_timestamp = false; + has_extended_timestamp = false; msg = NULL; msg_count = 0; + extended_timestamp = 0; } SrsChunkStream::~SrsChunkStream() diff --git a/trunk/src/protocol/srs_protocol_rtmp_stack.hpp b/trunk/src/protocol/srs_protocol_rtmp_stack.hpp index ddc890838..8db6f2cf4 100644 --- a/trunk/src/protocol/srs_protocol_rtmp_stack.hpp +++ b/trunk/src/protocol/srs_protocol_rtmp_stack.hpp @@ -380,11 +380,16 @@ public: // Cached message header SrsMessageHeader header; // Whether the chunk message header has extended timestamp. - bool extended_timestamp; + bool has_extended_timestamp; // The partially read message. SrsCommonMessage* msg; // Decoded msg count, to identify whether the chunk stream is fresh. int64_t msg_count; + // Because the extended timestamp may be a delta timestamp, it can differ + // from the timestamp in the header, so it should be stored as a distinct field + // for comparison with the extended timestamp of subsequent chunks. + // See https://github.com/ossrs/srs/pull/4356 for details. + int32_t extended_timestamp; public: SrsChunkStream(int _cid); virtual ~SrsChunkStream(); diff --git a/trunk/src/utest/srs_utest_protocol2.cpp b/trunk/src/utest/srs_utest_protocol2.cpp index 55b27ae6c..ba9270170 100644 --- a/trunk/src/utest/srs_utest_protocol2.cpp +++ b/trunk/src/utest/srs_utest_protocol2.cpp @@ -1199,6 +1199,373 @@ VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk2) EXPECT_EQ(0x00010203, msg->header.timestamp); } +/** +* 2 audio and video packet, fmt 1/2 +* first audio packet: dts:10000 +* first video packet: dts:11000 +* second audio packet: dts : 16787216 delta: 16777216 +* second video packet: dts : 16787216 delta: 16776216 +*/ +VOID TEST(ProtocolStackTest, ProtocolRecvAVExtTimedeltaStream) +{ + srs_error_t err = srs_success; + + MockBufferIO bio; + SrsProtocol proto(&bio); + + // audio message + uint8_t audio_data1[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x03, // fmt 0 + 0x00, 0x27, 0x10, // timestamp 10000 + 0x00, 0x00, 0x80, // length, 128 + 0x08, // message_type + 0x00, 0x00, 0x00, 0x00, // stream_id + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + uint8_t audio_data2[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x43, // fmt 1 + 0xff, 0xff, 0xff, // means extended timestamp + 0x00, 0x00, 0x80, // length, 128 + 0x08, // message_type + 0x01, 0x00, 0x00, 0x00, // extended timestamp delta:16777216 + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + // video message + uint8_t video_data1[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x04, // fmt 0 + 0x00, 0x2a, 0xf8, // timestamp 11000 + 0x00, 0x00, 0x80, // length, 128 + 0x09, // message_type + 0x00, 0x00, 0x00, 0x01, // stream_id + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + uint8_t video_data2[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x44, // fmt 1 + 0xff, 0xff, 0xff, // means extended timestamp + 0x00, 0x00, 0x80, // length, 128 + 0x09, // message_type + 0x00, 0xff, 0xfc, 0x18, // extended timestamp delta:16776216 + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + if (true) { + bio.in_buffer.append((char*)audio_data1, sizeof(audio_data1)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_audio()); + EXPECT_EQ(0x00002710, msg->header.timestamp); + } + + if (true) { + bio.in_buffer.append((char*)video_data1, sizeof(video_data1)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_video()); + EXPECT_EQ(0x00002af8, msg->header.timestamp); + } + + if (true) { + bio.in_buffer.append((char*)audio_data2, sizeof(audio_data2)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_audio()); + EXPECT_EQ(0x01002710, msg->header.timestamp); + } + + if (true) { + bio.in_buffer.append((char*)video_data2, sizeof(video_data2)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_video()); + EXPECT_EQ(0x01002710, msg->header.timestamp); + } +} + +/** +* 2 audio packet, fmt 1/2 +* first audio packet: dts:10000 +* second audio packet: dts : 16787216 delta: 16777216 +*/ +VOID TEST(ProtocolStackTest, ProtocolRecvAVExtTimedeltaAudio) +{ + srs_error_t err = srs_success; + + MockBufferIO bio; + SrsProtocol proto(&bio); + + // audio message + uint8_t audio_data1[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x03, // fmt 0 + 0x00, 0x27, 0x10, // timestamp 10000 + 0x00, 0x00, 0x80, // length, 128 + 0x08, // message_type + 0x00, 0x00, 0x00, 0x00, // stream_id + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + uint8_t audio_data2[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x43, // fmt 1 + 0xff, 0xff, 0xff, // means extended timestamp + 0x00, 0x00, 0x80, // length, 128 + 0x08, // message_type + 0x01, 0x00, 0x00, 0x00, // extended timestamp delta:16777216 + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + if (true) { + bio.in_buffer.append((char*)audio_data1, sizeof(audio_data1)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_audio()); + EXPECT_EQ(0x00002710, msg->header.timestamp); + } + + if (true) { + bio.in_buffer.append((char*)audio_data2, sizeof(audio_data2)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_audio()); + EXPECT_EQ(0x01002710, msg->header.timestamp); + } +} + +/** +* 2 video packet, fmt 1/2 +* first video packet: dts:11000 +* second video packet: dts : 16787216 delta: 16776216 +*/ +VOID TEST(ProtocolStackTest, ProtocolRecvAVExtTimedeltaVideo) +{ + srs_error_t err = srs_success; + + MockBufferIO bio; + SrsProtocol proto(&bio); + + // video message + uint8_t video_data1[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x04, // fmt 0 + 0x00, 0x2a, 0xf8, // timestamp 11000 + 0x00, 0x00, 0x80, // length, 128 + 0x09, // message_type + 0x00, 0x00, 0x00, 0x01, // stream_id + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + uint8_t video_data2[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x44, // fmt 1 + 0xff, 0xff, 0xff, // means extended timestamp + 0x00, 0x00, 0x80, // length, 128 + 0x09, // message_type + 0x00, 0xff, 0xfc, 0x18, // extended timestamp delta:16776216 + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + }; + + if (true) { + bio.in_buffer.append((char*)video_data1, sizeof(video_data1)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_video()); + EXPECT_EQ(0x00002af8, msg->header.timestamp); + } + + if (true) { + bio.in_buffer.append((char*)video_data2, sizeof(video_data2)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_video()); + EXPECT_EQ(0x01002710, msg->header.timestamp); + } +} + +/** +* 2 video packet, fmt 3 +* first video packet: dts:11000 +* second video packet: dts : 16787216 delta: 16776216 +*/ +VOID TEST(ProtocolStackTest, ProtocolRecvAVExtTimedeltaVideoFmt3) +{ + srs_error_t err = srs_success; + + MockBufferIO bio; + SrsProtocol proto(&bio); + + // video message + uint8_t video_data1[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x04, // fmt 0 + 0x00, 0x2a, 0xf8, // timestamp 11000 + 0x00, 0x01, 0x00, // length, 256 + 0x09, // message_type + 0x00, 0x00, 0x00, 0x01, // stream_id + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + // chunk #2 + 0xC4, // fmt 3 + /*next chunk.*/ 0x61, 0x79, 0x65, 0x72, + 0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e, + 0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70, + 0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65, + 0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, + 0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + uint8_t video_data2[] = { + // 12bytes header, 1byts chunk header, 11bytes msg heder + 0x44, // fmt 1 + 0xff, 0xff, 0xff, // means extended timestamp + 0x00, 0x01, 0x10, // length, 272 + 0x09, // message_type + 0x00, 0xff, 0xfc, 0x18, // extended timestamp delta:16776216 + // msg payload start + 0x02, 0x00, 0x07, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c, + 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c, + 0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00, + 0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, + // chunk #2 + 0xC4, // fmt 3 + 0x00, 0xff, 0xfc, 0x18, // extended timestamp delta:16776216 + /*next chunk.*/ 0x61, 0x79, 0x65, 0x72, + 0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e, + 0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70, + 0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65, + 0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65, + 0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // chunk #2 + 0xC4, + /*next chunk.*/ + 0x00, 0xff, 0xfc, 0x18, // extended timestamp delta:16776216 + 0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e + }; + + if (true) { + bio.in_buffer.append((char*)video_data1, sizeof(video_data1)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_video()); + EXPECT_EQ(0x00002af8, msg->header.timestamp); + } + + if (true) { + bio.in_buffer.append((char*)video_data2, sizeof(video_data2)); + SrsCommonMessage* msg_raw = NULL; + HELPER_ASSERT_SUCCESS(proto.recv_message(&msg_raw)); + SrsUniquePtr msg(msg_raw); + EXPECT_TRUE(msg->header.is_video()); + EXPECT_EQ(0x01002710, msg->header.timestamp); + } +} + /** * a video message, in 2 chunks packet. * use 1B chunk header, min chunk id is 2.