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.