diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index f517568726..4086299a7a 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -217,7 +217,6 @@ 'rtp_rtcp/source/rtp_packet_history_unittest.cc', 'rtp_rtcp/source/rtp_payload_registry_unittest.cc', 'rtp_rtcp/source/rtp_rtcp_impl_unittest.cc', - 'rtp_rtcp/source/rtp_utility_unittest.cc', 'rtp_rtcp/source/rtp_header_extension_unittest.cc', 'rtp_rtcp/source/rtp_sender_unittest.cc', 'rtp_rtcp/source/vp8_partition_aggregator_unittest.cc', diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc index e638c361ef..a3d673b388 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc @@ -15,10 +15,223 @@ #include -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { +namespace { +struct ParsedPayload { + ParsedPayload() : data(NULL), data_length(0) {} + + const uint8_t* data; // Start address of parsed payload data. + int data_length; // Length of parsed payload data. +}; + +int ParseVP8PictureID(RTPVideoHeaderVP8* vp8, + const uint8_t** data, + int* data_length, + int* parsed_bytes) { + assert(vp8 != NULL); + if (*data_length <= 0) + return -1; + + vp8->pictureId = (**data & 0x7F); + if (**data & 0x80) { + (*data)++; + (*parsed_bytes)++; + if (--(*data_length) <= 0) + return -1; + // PictureId is 15 bits + vp8->pictureId = (vp8->pictureId << 8) + **data; + } + (*data)++; + (*parsed_bytes)++; + (*data_length)--; + return 0; +} + +int ParseVP8Tl0PicIdx(RTPVideoHeaderVP8* vp8, + const uint8_t** data, + int* data_length, + int* parsed_bytes) { + assert(vp8 != NULL); + if (*data_length <= 0) + return -1; + + vp8->tl0PicIdx = **data; + (*data)++; + (*parsed_bytes)++; + (*data_length)--; + return 0; +} + +int ParseVP8TIDAndKeyIdx(RTPVideoHeaderVP8* vp8, + const uint8_t** data, + int* data_length, + int* parsed_bytes, + bool has_tid, + bool has_key_idx) { + assert(vp8 != NULL); + if (*data_length <= 0) + return -1; + + if (has_tid) { + vp8->temporalIdx = ((**data >> 6) & 0x03); + vp8->layerSync = (**data & 0x20) ? true : false; // Y bit + } + if (has_key_idx) { + vp8->keyIdx = (**data & 0x1F); + } + (*data)++; + (*parsed_bytes)++; + (*data_length)--; + return 0; +} + +int ParseVP8Extension(RTPVideoHeaderVP8* vp8, + const uint8_t* data, + int data_length) { + assert(vp8 != NULL); + int parsed_bytes = 0; + if (data_length <= 0) + return -1; + // Optional X field is present. + bool has_picture_id = (*data & 0x80) ? true : false; // I bit + bool has_tl0_pic_idx = (*data & 0x40) ? true : false; // L bit + bool has_tid = (*data & 0x20) ? true : false; // T bit + bool has_key_idx = (*data & 0x10) ? true : false; // K bit + + // Advance data and decrease remaining payload size. + data++; + parsed_bytes++; + data_length--; + + if (has_picture_id) { + if (ParseVP8PictureID(vp8, &data, &data_length, &parsed_bytes) != 0) { + return -1; + } + } + + if (has_tl0_pic_idx) { + if (ParseVP8Tl0PicIdx(vp8, &data, &data_length, &parsed_bytes) != 0) { + return -1; + } + } + + if (has_tid || has_key_idx) { + if (ParseVP8TIDAndKeyIdx( + vp8, &data, &data_length, &parsed_bytes, has_tid, has_key_idx) != + 0) { + return -1; + } + } + return parsed_bytes; +} + +int ParseVP8FrameSize(WebRtcRTPHeader* rtp_header, + const uint8_t* data, + int data_length) { + assert(rtp_header != NULL); + if (rtp_header->frameType != kVideoFrameKey) { + // Included in payload header for I-frames. + return 0; + } + if (data_length < 10) { + // For an I-frame we should always have the uncompressed VP8 header + // in the beginning of the partition. + return -1; + } + rtp_header->type.Video.width = ((data[7] << 8) + data[6]) & 0x3FFF; + rtp_header->type.Video.height = ((data[9] << 8) + data[8]) & 0x3FFF; + return 0; +} + +// +// VP8 format: +// +// Payload descriptor +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |X|R|N|S|PartID | (REQUIRED) +// +-+-+-+-+-+-+-+-+ +// X: |I|L|T|K| RSV | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// I: | PictureID | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// L: | TL0PICIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// T/K: |TID:Y| KEYIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// +// Payload header (considered part of the actual payload, sent to decoder) +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |Size0|H| VER |P| +// +-+-+-+-+-+-+-+-+ +// | ... | +// + + +bool ParseVP8(WebRtcRTPHeader* rtp_header, + const uint8_t* data, + int data_length, + ParsedPayload* payload) { + assert(rtp_header != NULL); + // Parse mandatory first byte of payload descriptor. + bool extension = (*data & 0x80) ? true : false; // X bit + bool beginning_of_partition = (*data & 0x10) ? true : false; // S bit + int partition_id = (*data & 0x0F); // PartID field + + rtp_header->type.Video.isFirstPacket = + beginning_of_partition && (partition_id == 0); + + rtp_header->type.Video.codecHeader.VP8.nonReference = + (*data & 0x20) ? true : false; // N bit + rtp_header->type.Video.codecHeader.VP8.partitionId = partition_id; + rtp_header->type.Video.codecHeader.VP8.beginningOfPartition = + beginning_of_partition; + rtp_header->type.Video.codecHeader.VP8.pictureId = kNoPictureId; + rtp_header->type.Video.codecHeader.VP8.tl0PicIdx = kNoTl0PicIdx; + rtp_header->type.Video.codecHeader.VP8.temporalIdx = kNoTemporalIdx; + rtp_header->type.Video.codecHeader.VP8.layerSync = false; + rtp_header->type.Video.codecHeader.VP8.keyIdx = kNoKeyIdx; + + if (partition_id > 8) { + // Weak check for corrupt data: PartID MUST NOT be larger than 8. + return false; + } + + // Advance data and decrease remaining payload size. + data++; + data_length--; + + if (extension) { + const int parsed_bytes = ParseVP8Extension( + &rtp_header->type.Video.codecHeader.VP8, data, data_length); + if (parsed_bytes < 0) + return false; + data += parsed_bytes; + data_length -= parsed_bytes; + } + + if (data_length <= 0) { + LOG(LS_ERROR) << "Error parsing VP8 payload descriptor!"; + return false; + } + + // Read P bit from payload header (only at beginning of first partition). + if (data_length > 0 && beginning_of_partition && partition_id == 0) { + rtp_header->frameType = (*data & 0x01) ? kVideoFrameDelta : kVideoFrameKey; + } else { + rtp_header->frameType = kVideoFrameDelta; + } + + if (0 != ParseVP8FrameSize(rtp_header, data, data_length)) { + return false; + } + payload->data = data; + payload->data_length = data_length; + return true; +} +} // namespace // Define how the VP8PacketizerModes are implemented. // Modes are: kStrict, kAggregate, kEqualSize. @@ -523,48 +736,15 @@ RtpDepacketizerVp8::RtpDepacketizerVp8(RtpData* const callback) bool RtpDepacketizerVp8::Parse(WebRtcRTPHeader* rtp_header, const uint8_t* payload_data, size_t payload_data_length) { - RtpUtility::RTPPayload parsed_packet; - RtpUtility::RTPPayloadParser rtp_payload_parser( - kRtpVideoVp8, payload_data, payload_data_length); - - if (!rtp_payload_parser.Parse(parsed_packet)) + ParsedPayload payload; + if (!ParseVP8(rtp_header, payload_data, payload_data_length, &payload)) return false; - if (parsed_packet.info.VP8.dataLength == 0) + if (payload.data_length == 0) return true; - rtp_header->frameType = (parsed_packet.frameType == RtpUtility::kIFrame) - ? kVideoFrameKey - : kVideoFrameDelta; - - RTPVideoHeaderVP8* to_header = &rtp_header->type.Video.codecHeader.VP8; - RtpUtility::RTPPayloadVP8* from_header = &parsed_packet.info.VP8; - - rtp_header->type.Video.isFirstPacket = - from_header->beginningOfPartition && (from_header->partitionID == 0); - to_header->nonReference = from_header->nonReferenceFrame; - to_header->pictureId = - from_header->hasPictureID ? from_header->pictureID : kNoPictureId; - to_header->tl0PicIdx = - from_header->hasTl0PicIdx ? from_header->tl0PicIdx : kNoTl0PicIdx; - if (from_header->hasTID) { - to_header->temporalIdx = from_header->tID; - to_header->layerSync = from_header->layerSync; - } else { - to_header->temporalIdx = kNoTemporalIdx; - to_header->layerSync = false; - } - to_header->keyIdx = from_header->hasKeyIdx ? from_header->keyIdx : kNoKeyIdx; - - rtp_header->type.Video.width = from_header->frameWidth; - rtp_header->type.Video.height = from_header->frameHeight; - - to_header->partitionId = from_header->partitionID; - to_header->beginningOfPartition = from_header->beginningOfPartition; - - if (callback_->OnReceivedPayloadData(parsed_packet.info.VP8.data, - parsed_packet.info.VP8.dataLength, - rtp_header) != 0) { + if (callback_->OnReceivedPayloadData( + payload.data, payload.data_length, rtp_header) != 0) { return false; } return true; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc index c6574e6e71..7a4dbcf6d6 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc @@ -12,17 +12,76 @@ * This file includes unit tests for the VP8 packetizer. */ +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h" #include "webrtc/system_wrappers/interface/compile_assert.h" #include "webrtc/typedefs.h" -#define CHECK_ARRAY_SIZE(expected_size, array) \ - COMPILE_ASSERT(expected_size == sizeof(array) / sizeof(array[0]), \ +#define CHECK_ARRAY_SIZE(expected_size, array) \ + COMPILE_ASSERT(expected_size == sizeof(array) / sizeof(array[0]), \ check_array_size); +using ::testing::_; +using ::testing::Args; +using ::testing::ElementsAreArray; +using ::testing::Return; + namespace webrtc { +namespace { +// Payload descriptor +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |X|R|N|S|PartID | (REQUIRED) +// +-+-+-+-+-+-+-+-+ +// X: |I|L|T|K| RSV | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// I: | PictureID | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// L: | TL0PICIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// T/K: |TID:Y| KEYIDX | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ +// +// Payload header +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |Size0|H| VER |P| +// +-+-+-+-+-+-+-+-+ +// | Size1 | +// +-+-+-+-+-+-+-+-+ +// | Size2 | +// +-+-+-+-+-+-+-+-+ +// | Bytes 4..N of | +// | VP8 payload | +// : : +// +-+-+-+-+-+-+-+-+ +// | OPTIONAL RTP | +// | padding | +// : : +// +-+-+-+-+-+-+-+-+ + +void VerifyBasicHeader(WebRtcRTPHeader* header, bool N, bool S, int part_id) { + ASSERT_TRUE(header != NULL); + EXPECT_EQ(N, header->type.Video.codecHeader.VP8.nonReference); + EXPECT_EQ(S, header->type.Video.codecHeader.VP8.beginningOfPartition); + EXPECT_EQ(part_id, header->type.Video.codecHeader.VP8.partitionId); +} + +void VerifyExtensions(WebRtcRTPHeader* header, + int16_t picture_id, /* I */ + int16_t tl0_pic_idx, /* L */ + uint8_t temporal_idx, /* T */ + int key_idx /* K */) { + ASSERT_TRUE(header != NULL); + EXPECT_EQ(picture_id, header->type.Video.codecHeader.VP8.pictureId); + EXPECT_EQ(tl0_pic_idx, header->type.Video.codecHeader.VP8.tl0PicIdx); + EXPECT_EQ(temporal_idx, header->type.Video.codecHeader.VP8.temporalIdx); + EXPECT_EQ(key_idx, header->type.Video.codecHeader.VP8.keyIdx); +} +} // namespace class RtpPacketizerVp8Test : public ::testing::Test { protected: @@ -35,7 +94,8 @@ class RtpPacketizerVp8Test : public ::testing::Test { hdr_info_.layerSync = false; hdr_info_.tl0PicIdx = kNoTl0PicIdx; hdr_info_.keyIdx = kNoKeyIdx; - if (helper_ != NULL) return false; + if (helper_ != NULL) + return false; helper_ = new test::RtpFormatVp8TestHelper(&hdr_info_); return helper_->Init(partition_sizes, num_partitions); } @@ -59,14 +119,17 @@ TEST_F(RtpPacketizerVp8Test, TestStrictMode) { // The expected sizes are obtained by running a verified good implementation. const int kExpectedSizes[] = {9, 9, 12, 11, 11, 11, 10}; const int kExpectedPart[] = {0, 0, 1, 2, 2, 2, 2}; - const bool kExpectedFragStart[] = - {true, false, true, true, false, false, false}; + const bool kExpectedFragStart[] = {true, false, true, true, + false, false, false}; const int kExpectedNum = sizeof(kExpectedSizes) / sizeof(kExpectedSizes[0]); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } TEST_F(RtpPacketizerVp8Test, TestAggregateMode) { @@ -89,8 +152,11 @@ TEST_F(RtpPacketizerVp8Test, TestAggregateMode) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions1) { @@ -113,8 +179,11 @@ TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions1) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions2) { @@ -137,8 +206,11 @@ TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions2) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } TEST_F(RtpPacketizerVp8Test, TestAggregateModeTwoLargePartitions) { @@ -161,8 +233,11 @@ TEST_F(RtpPacketizerVp8Test, TestAggregateModeTwoLargePartitions) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify that EqualSize mode is forced if fragmentation info is missing. @@ -172,7 +247,7 @@ TEST_F(RtpPacketizerVp8Test, TestEqualSizeModeFallback) { ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID - const int kMaxSize = 12; // Small enough to produce 4 packets. + const int kMaxSize = 12; // Small enough to produce 4 packets. RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize); packetizer.SetPayloadData( helper_->payload_data(), helper_->payload_size(), NULL); @@ -187,8 +262,11 @@ TEST_F(RtpPacketizerVp8Test, TestEqualSizeModeFallback) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); helper_->set_sloppy_partitioning(true); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify that non-reference bit is set. EqualSize mode fallback is expected. @@ -213,8 +291,11 @@ TEST_F(RtpPacketizerVp8Test, TestNonReferenceBit) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); helper_->set_sloppy_partitioning(true); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify Tl0PicIdx and TID fields, and layerSync bit. @@ -241,8 +322,11 @@ TEST_F(RtpPacketizerVp8Test, TestTl0PicIdxAndTID) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify KeyIdx field. @@ -267,8 +351,11 @@ TEST_F(RtpPacketizerVp8Test, TestKeyIdx) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } // Verify TID field and KeyIdx field in combination. @@ -294,7 +381,202 @@ TEST_F(RtpPacketizerVp8Test, TestTIDAndKeyIdx) { CHECK_ARRAY_SIZE(kExpectedNum, kExpectedPart); CHECK_ARRAY_SIZE(kExpectedNum, kExpectedFragStart); - helper_->GetAllPacketsAndCheck(&packetizer, kExpectedSizes, kExpectedPart, - kExpectedFragStart, kExpectedNum); + helper_->GetAllPacketsAndCheck(&packetizer, + kExpectedSizes, + kExpectedPart, + kExpectedFragStart, + kExpectedNum); } -} // namespace + +class RtpDepacketizerVp8Test : public ::testing::Test { + protected: + RtpDepacketizerVp8Test() + : callback_(), + depacketizer_(RtpDepacketizer::Create(kRtpVideoVp8, &callback_)) {} + + void ExpectPacket(const uint8_t* data, size_t length) { + EXPECT_CALL(callback_, OnReceivedPayloadData(_, length, _)) + .With(Args<0, 1>(ElementsAreArray(data, length))) + .Times(1) + .WillOnce(Return(0)); + } + + MockRtpData callback_; + scoped_ptr depacketizer_; +}; + +TEST_F(RtpDepacketizerVp8Test, BasicHeader) { + const uint8_t kHeaderLength = 1; + uint8_t packet[4] = {0}; + packet[0] = 0x14; // Binary 0001 0100; S = 1, PartID = 4. + packet[1] = 0x01; // P frame. + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + + EXPECT_EQ(kVideoFrameDelta, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 0, 1, 4); + VerifyExtensions( + &rtp_header, kNoPictureId, kNoTl0PicIdx, kNoTemporalIdx, kNoKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, PictureID) { + const uint8_t kHeaderLength1 = 3; + const uint8_t kHeaderLength2 = 4; + const uint8_t kPictureId = 17; + uint8_t packet[10] = {0}; + packet[0] = 0xA0; + packet[1] = 0x80; + packet[2] = kPictureId; + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength1, sizeof(packet) - kHeaderLength1); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + EXPECT_EQ(kVideoFrameDelta, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 1, 0, 0); + VerifyExtensions( + &rtp_header, kPictureId, kNoTl0PicIdx, kNoTemporalIdx, kNoKeyIdx); + + // Re-use packet, but change to long PictureID. + packet[2] = 0x80 | kPictureId; + packet[3] = kPictureId; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength2, sizeof(packet) - kHeaderLength2); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + VerifyBasicHeader(&rtp_header, 1, 0, 0); + VerifyExtensions(&rtp_header, + (kPictureId << 8) + kPictureId, + kNoTl0PicIdx, + kNoTemporalIdx, + kNoKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, Tl0PicIdx) { + const uint8_t kHeaderLength = 3; + const uint8_t kTl0PicIdx = 17; + uint8_t packet[13] = {0}; + packet[0] = 0x90; + packet[1] = 0x40; + packet[2] = kTl0PicIdx; + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + EXPECT_EQ(kVideoFrameKey, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 0, 1, 0); + VerifyExtensions( + &rtp_header, kNoPictureId, kTl0PicIdx, kNoTemporalIdx, kNoKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, TIDAndLayerSync) { + const uint8_t kHeaderLength = 3; + uint8_t packet[10] = {0}; + packet[0] = 0x88; + packet[1] = 0x20; + packet[2] = 0x80; // TID(2) + LayerSync(false) + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + EXPECT_EQ(kVideoFrameDelta, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 0, 0, 8); + VerifyExtensions(&rtp_header, kNoPictureId, kNoTl0PicIdx, 2, kNoKeyIdx); + EXPECT_FALSE(rtp_header.type.Video.codecHeader.VP8.layerSync); +} + +TEST_F(RtpDepacketizerVp8Test, KeyIdx) { + const uint8_t kHeaderLength = 3; + const uint8_t kKeyIdx = 17; + uint8_t packet[10] = {0}; + packet[0] = 0x88; + packet[1] = 0x10; // K = 1. + packet[2] = kKeyIdx; + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + EXPECT_EQ(kVideoFrameDelta, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 0, 0, 8); + VerifyExtensions( + &rtp_header, kNoPictureId, kNoTl0PicIdx, kNoTemporalIdx, kKeyIdx); +} + +TEST_F(RtpDepacketizerVp8Test, MultipleExtensions) { + const uint8_t kHeaderLength = 6; + uint8_t packet[10] = {0}; + packet[0] = 0x88; + packet[1] = 0x80 | 0x40 | 0x20 | 0x10; + packet[2] = 0x80 | 17; // PictureID, high 7 bits. + packet[3] = 17; // PictureID, low 8 bits. + packet[4] = 42; // Tl0PicIdx. + packet[5] = 0x40 | 0x20 | 0x11; // TID(1) + LayerSync(true) + KEYIDX(17). + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + EXPECT_EQ(kVideoFrameDelta, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 0, 0, 8); + VerifyExtensions(&rtp_header, (17 << 8) + 17, 42, 1, 17); +} + +TEST_F(RtpDepacketizerVp8Test, TooShortHeader) { + uint8_t packet[4] = {0}; + packet[0] = 0x88; + packet[1] = 0x80 | 0x40 | 0x20 | 0x10; // All extensions are enabled... + packet[2] = 0x80 | 17; // ... but only 2 bytes PictureID is provided. + packet[3] = 17; // PictureID, low 8 bits. + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + EXPECT_FALSE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); +} + +TEST_F(RtpDepacketizerVp8Test, TestWithPacketizer) { + const uint8_t kHeaderLength = 5; + uint8_t payload[10] = {0}; + uint8_t packet[20] = {0}; + RTPVideoHeaderVP8 input_header; + input_header.nonReference = true; + input_header.pictureId = 300; + input_header.temporalIdx = 1; + input_header.layerSync = false; + input_header.tl0PicIdx = kNoTl0PicIdx; // Disable. + input_header.keyIdx = 31; + RtpPacketizerVp8 packetizer(input_header, 20); + packetizer.SetPayloadData(payload, 10, NULL); + bool last; + size_t send_bytes; + ASSERT_TRUE(packetizer.NextPacket(packet, &send_bytes, &last)); + ASSERT_TRUE(last); + + WebRtcRTPHeader rtp_header; + memset(&rtp_header, 0, sizeof(rtp_header)); + + ExpectPacket(packet + kHeaderLength, sizeof(packet) - kHeaderLength); + EXPECT_TRUE(depacketizer_->Parse(&rtp_header, packet, sizeof(packet))); + EXPECT_EQ(kVideoFrameKey, rtp_header.frameType); + VerifyBasicHeader(&rtp_header, 1, 1, 0); + VerifyExtensions(&rtp_header, + input_header.pictureId, + input_header.tl0PicIdx, + input_header.temporalIdx, + input_header.keyIdx); + EXPECT_EQ(rtp_header.type.Video.codecHeader.VP8.layerSync, + input_header.layerSync); +} +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc index 764e9acfe2..22fae10003 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc @@ -24,7 +24,6 @@ namespace webrtc { using RtpUtility::GetCurrentRTP; using RtpUtility::Payload; -using RtpUtility::RTPPayloadParser; using RtpUtility::StringCompare; RtpReceiver* RtpReceiver::CreateVideoReceiver( diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc index 95389b46af..441a9c50c2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc @@ -188,33 +188,6 @@ uint32_t pow2(uint8_t exp) { return 1 << exp; } -void RTPPayload::SetType(RtpVideoCodecTypes videoType) { - type = videoType; - - switch (type) { - case kRtpVideoGeneric: - break; - case kRtpVideoVp8: { - info.VP8.nonReferenceFrame = false; - info.VP8.beginningOfPartition = false; - info.VP8.partitionID = 0; - info.VP8.hasPictureID = false; - info.VP8.hasTl0PicIdx = false; - info.VP8.hasTID = false; - info.VP8.hasKeyIdx = false; - info.VP8.pictureID = -1; - info.VP8.tl0PicIdx = -1; - info.VP8.tID = -1; - info.VP8.layerSync = false; - info.VP8.frameWidth = 0; - info.VP8.frameHeight = 0; - break; - } - default: - break; - } -} - RtpHeaderParser::RtpHeaderParser(const uint8_t* rtpData, const size_t rtpDataLength) : _ptrRTPDataBegin(rtpData), @@ -565,208 +538,6 @@ uint8_t RtpHeaderParser::ParsePaddingBytes( } return num_zero_bytes; } - -RTPPayloadParser::RTPPayloadParser(const RtpVideoCodecTypes videoType, - const uint8_t* payloadData, - uint16_t payloadDataLength) - : _dataPtr(payloadData), - _dataLength(payloadDataLength), - _videoType(videoType) {} - -RTPPayloadParser::~RTPPayloadParser() { -} - -bool RTPPayloadParser::Parse(RTPPayload& parsedPacket) const { - parsedPacket.SetType(_videoType); - - switch (_videoType) { - case kRtpVideoGeneric: - return ParseGeneric(parsedPacket); - case kRtpVideoVp8: - return ParseVP8(parsedPacket); - default: - return false; - } -} - -bool RTPPayloadParser::ParseGeneric(RTPPayload& /*parsedPacket*/) const { - return false; -} - -// -// VP8 format: -// -// Payload descriptor -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |X|R|N|S|PartID | (REQUIRED) -// +-+-+-+-+-+-+-+-+ -// X: |I|L|T|K| RSV | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// I: | PictureID | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// L: | TL0PICIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// T/K: |TID:Y| KEYIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// -// Payload header (considered part of the actual payload, sent to decoder) -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |Size0|H| VER |P| -// +-+-+-+-+-+-+-+-+ -// | ... | -// + + - -bool RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const { - RTPPayloadVP8* vp8 = &parsedPacket.info.VP8; - const uint8_t* dataPtr = _dataPtr; - int dataLength = _dataLength; - - // Parse mandatory first byte of payload descriptor - bool extension = (*dataPtr & 0x80) ? true : false; // X bit - vp8->nonReferenceFrame = (*dataPtr & 0x20) ? true : false; // N bit - vp8->beginningOfPartition = (*dataPtr & 0x10) ? true : false; // S bit - vp8->partitionID = (*dataPtr & 0x0F); // PartID field - - if (vp8->partitionID > 8) { - // Weak check for corrupt data: PartID MUST NOT be larger than 8. - return false; - } - - // Advance dataPtr and decrease remaining payload size - dataPtr++; - dataLength--; - - if (extension) { - const int parsedBytes = ParseVP8Extension(vp8, dataPtr, dataLength); - if (parsedBytes < 0) return false; - dataPtr += parsedBytes; - dataLength -= parsedBytes; - } - - if (dataLength <= 0) { - LOG(LS_ERROR) << "Error parsing VP8 payload descriptor!"; - return false; - } - - // Read P bit from payload header (only at beginning of first partition) - if (dataLength > 0 && vp8->beginningOfPartition && vp8->partitionID == 0) { - parsedPacket.frameType = (*dataPtr & 0x01) ? kPFrame : kIFrame; - } else { - parsedPacket.frameType = kPFrame; - } - if (0 != ParseVP8FrameSize(parsedPacket, dataPtr, dataLength)) { - return false; - } - parsedPacket.info.VP8.data = dataPtr; - parsedPacket.info.VP8.dataLength = dataLength; - return true; -} - -int RTPPayloadParser::ParseVP8FrameSize(RTPPayload& parsedPacket, - const uint8_t* dataPtr, - int dataLength) const { - if (parsedPacket.frameType != kIFrame) { - // Included in payload header for I-frames. - return 0; - } - if (dataLength < 10) { - // For an I-frame we should always have the uncompressed VP8 header - // in the beginning of the partition. - return -1; - } - RTPPayloadVP8* vp8 = &parsedPacket.info.VP8; - vp8->frameWidth = ((dataPtr[7] << 8) + dataPtr[6]) & 0x3FFF; - vp8->frameHeight = ((dataPtr[9] << 8) + dataPtr[8]) & 0x3FFF; - return 0; -} - -int RTPPayloadParser::ParseVP8Extension(RTPPayloadVP8* vp8, - const uint8_t* dataPtr, - int dataLength) const { - int parsedBytes = 0; - if (dataLength <= 0) return -1; - // Optional X field is present - vp8->hasPictureID = (*dataPtr & 0x80) ? true : false; // I bit - vp8->hasTl0PicIdx = (*dataPtr & 0x40) ? true : false; // L bit - vp8->hasTID = (*dataPtr & 0x20) ? true : false; // T bit - vp8->hasKeyIdx = (*dataPtr & 0x10) ? true : false; // K bit - - // Advance dataPtr and decrease remaining payload size - dataPtr++; - parsedBytes++; - dataLength--; - - if (vp8->hasPictureID) { - if (ParseVP8PictureID(vp8, &dataPtr, &dataLength, &parsedBytes) != 0) { - return -1; - } - } - - if (vp8->hasTl0PicIdx) { - if (ParseVP8Tl0PicIdx(vp8, &dataPtr, &dataLength, &parsedBytes) != 0) { - return -1; - } - } - - if (vp8->hasTID || vp8->hasKeyIdx) { - if (ParseVP8TIDAndKeyIdx(vp8, &dataPtr, &dataLength, &parsedBytes) != 0) { - return -1; - } - } - return parsedBytes; -} - -int RTPPayloadParser::ParseVP8PictureID(RTPPayloadVP8* vp8, - const uint8_t** dataPtr, - int* dataLength, - int* parsedBytes) const { - if (*dataLength <= 0) return -1; - vp8->pictureID = (**dataPtr & 0x7F); - if (**dataPtr & 0x80) { - (*dataPtr)++; - (*parsedBytes)++; - if (--(*dataLength) <= 0) return -1; - // PictureID is 15 bits - vp8->pictureID = (vp8->pictureID << 8) +** dataPtr; - } - (*dataPtr)++; - (*parsedBytes)++; - (*dataLength)--; - return 0; -} - -int RTPPayloadParser::ParseVP8Tl0PicIdx(RTPPayloadVP8* vp8, - const uint8_t** dataPtr, - int* dataLength, - int* parsedBytes) const { - if (*dataLength <= 0) return -1; - vp8->tl0PicIdx = **dataPtr; - (*dataPtr)++; - (*parsedBytes)++; - (*dataLength)--; - return 0; -} - -int RTPPayloadParser::ParseVP8TIDAndKeyIdx(RTPPayloadVP8* vp8, - const uint8_t** dataPtr, - int* dataLength, - int* parsedBytes) const { - if (*dataLength <= 0) return -1; - if (vp8->hasTID) { - vp8->tID = ((**dataPtr >> 6) & 0x03); - vp8->layerSync = (**dataPtr & 0x20) ? true : false; // Y bit - } - if (vp8->hasKeyIdx) { - vp8->keyIdx = (**dataPtr & 0x1F); - } - (*dataPtr)++; - (*parsedBytes)++; - (*dataLength)--; - return 0; -} - } // namespace RtpUtility } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.h b/webrtc/modules/rtp_rtcp/source/rtp_utility.h index ef50570d28..3a3ad5ee36 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.h @@ -115,97 +115,7 @@ namespace RtpUtility { const uint8_t* const _ptrRTPDataBegin; const uint8_t* const _ptrRTPDataEnd; }; - - enum FrameTypes - { - kIFrame, // key frame - kPFrame // Delta frame - }; - - struct RTPPayloadVP8 - { - bool nonReferenceFrame; - bool beginningOfPartition; - int partitionID; - bool hasPictureID; - bool hasTl0PicIdx; - bool hasTID; - bool hasKeyIdx; - int pictureID; - int tl0PicIdx; - int tID; - bool layerSync; - int keyIdx; - int frameWidth; - int frameHeight; - - const uint8_t* data; - uint16_t dataLength; - }; - - union RTPPayloadUnion - { - RTPPayloadVP8 VP8; - }; - - struct RTPPayload - { - void SetType(RtpVideoCodecTypes videoType); - - RtpVideoCodecTypes type; - FrameTypes frameType; - RTPPayloadUnion info; - }; - - // RTP payload parser - class RTPPayloadParser - { - public: - RTPPayloadParser(const RtpVideoCodecTypes payloadType, - const uint8_t* payloadData, - // Length w/o padding. - const uint16_t payloadDataLength); - - ~RTPPayloadParser(); - - bool Parse(RTPPayload& parsedPacket) const; - - private: - bool ParseGeneric(RTPPayload& parsedPacket) const; - - bool ParseVP8(RTPPayload& parsedPacket) const; - - int ParseVP8Extension(RTPPayloadVP8 *vp8, - const uint8_t *dataPtr, - int dataLength) const; - - int ParseVP8PictureID(RTPPayloadVP8 *vp8, - const uint8_t **dataPtr, - int *dataLength, - int *parsedBytes) const; - - int ParseVP8Tl0PicIdx(RTPPayloadVP8 *vp8, - const uint8_t **dataPtr, - int *dataLength, - int *parsedBytes) const; - - int ParseVP8TIDAndKeyIdx(RTPPayloadVP8 *vp8, - const uint8_t **dataPtr, - int *dataLength, - int *parsedBytes) const; - - int ParseVP8FrameSize(RTPPayload& parsedPacket, - const uint8_t *dataPtr, - int dataLength) const; - - private: - const uint8_t* _dataPtr; - const uint16_t _dataLength; - const RtpVideoCodecTypes _videoType; - }; - - } // namespace RtpUtility - +} // namespace RtpUtility } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_UTILITY_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc deleted file mode 100644 index 82ec953c98..0000000000 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file conatins unit tests for the RtpUtility. - */ - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" -#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -using RtpUtility::RTPPayloadParser; -using RtpUtility::RTPPayload; -using RtpUtility::RTPPayloadVP8; - -// Payload descriptor -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |X|R|N|S|PartID | (REQUIRED) -// +-+-+-+-+-+-+-+-+ -// X: |I|L|T|K| RSV | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// I: | PictureID | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// L: | TL0PICIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// T/K: |TID:Y| KEYIDX | (OPTIONAL) -// +-+-+-+-+-+-+-+-+ -// -// Payload header -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// |Size0|H| VER |P| -// +-+-+-+-+-+-+-+-+ -// | Size1 | -// +-+-+-+-+-+-+-+-+ -// | Size2 | -// +-+-+-+-+-+-+-+-+ -// | Bytes 4..N of | -// | VP8 payload | -// : : -// +-+-+-+-+-+-+-+-+ -// | OPTIONAL RTP | -// | padding | -// : : -// +-+-+-+-+-+-+-+-+ - -void VerifyBasicHeader(const RTPPayloadVP8 &header, - bool N, bool S, int PartID) { - EXPECT_EQ(N, header.nonReferenceFrame); - EXPECT_EQ(S, header.beginningOfPartition); - EXPECT_EQ(PartID, header.partitionID); -} - -void VerifyExtensions(const RTPPayloadVP8 &header, - bool I, bool L, bool T, bool K) { - EXPECT_EQ(I, header.hasPictureID); - EXPECT_EQ(L, header.hasTl0PicIdx); - EXPECT_EQ(T, header.hasTID); - EXPECT_EQ(K, header.hasKeyIdx); -} - -TEST(ParseVP8Test, BasicHeader) { - uint8_t payload[4] = {0}; - payload[0] = 0x14; // Binary 0001 0100; S = 1, PartID = 4. - payload[1] = 0x01; // P frame. - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 4); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 4 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ(payload + 1, parsedPacket.info.VP8.data); - EXPECT_EQ(4 - 1, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, PictureID) { - uint8_t payload[10] = {0}; - payload[0] = 0xA0; - payload[1] = 0x80; - payload[2] = 17; - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ(17, parsedPacket.info.VP8.pictureID); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); - - - // Re-use payload, but change to long PictureID. - payload[2] = 0x80 | 17; - payload[3] = 17; - RTPPayloadParser rtpPayloadParser2(kRtpVideoVp8, payload, 10); - - ASSERT_TRUE(rtpPayloadParser2.Parse(parsedPacket)); - - VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID); - - EXPECT_EQ(payload + 4, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 4, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, Tl0PicIdx) { - uint8_t payload[13] = {0}; - payload[0] = 0x90; - payload[1] = 0x40; - payload[2] = 17; - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 13); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kIFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 1 /*L*/, 0 /*T*/, 0 /*K*/); - - EXPECT_EQ(17, parsedPacket.info.VP8.tl0PicIdx); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(13 - 3, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, TIDAndLayerSync) { - uint8_t payload[10] = {0}; - payload[0] = 0x88; - payload[1] = 0x20; - payload[2] = 0x80; // TID(2) + LayerSync(false) - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 1 /*T*/, 0 /*K*/); - - EXPECT_EQ(2, parsedPacket.info.VP8.tID); - EXPECT_FALSE(parsedPacket.info.VP8.layerSync); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, KeyIdx) { - uint8_t payload[10] = {0}; - payload[0] = 0x88; - payload[1] = 0x10; // K = 1. - payload[2] = 0x11; // KEYIDX = 17 decimal. - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 0 /*T*/, 1 /*K*/); - - EXPECT_EQ(17, parsedPacket.info.VP8.keyIdx); - - EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, MultipleExtensions) { - uint8_t payload[10] = {0}; - payload[0] = 0x88; - payload[1] = 0x80 | 0x40 | 0x20 | 0x10; - payload[2] = 0x80 | 17; // PictureID, high 7 bits. - payload[3] = 17; // PictureID, low 8 bits. - payload[4] = 42; // Tl0PicIdx. - payload[5] = 0x40 | 0x20 | 0x11; // TID(1) + LayerSync(true) + KEYIDX(17). - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 10); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kPFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 1 /*L*/, 1 /*T*/, 1 /*K*/); - - EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID); - EXPECT_EQ(42, parsedPacket.info.VP8.tl0PicIdx); - EXPECT_EQ(1, parsedPacket.info.VP8.tID); - EXPECT_EQ(17, parsedPacket.info.VP8.keyIdx); - - EXPECT_EQ(payload + 6, parsedPacket.info.VP8.data); - EXPECT_EQ(10 - 6, parsedPacket.info.VP8.dataLength); -} - -TEST(ParseVP8Test, TooShortHeader) { - uint8_t payload[4] = {0}; - payload[0] = 0x88; - payload[1] = 0x80 | 0x40 | 0x20 | 0x10; // All extensions are enabled... - payload[2] = 0x80 | 17; // ... but only 2 bytes PictureID is provided. - payload[3] = 17; // PictureID, low 8 bits. - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, payload, 4); - - RTPPayload parsedPacket; - EXPECT_FALSE(rtpPayloadParser.Parse(parsedPacket)); -} - -TEST(ParseVP8Test, TestWithPacketizer) { - uint8_t payload[10] = {0}; - uint8_t packet[20] = {0}; - RTPVideoHeaderVP8 inputHeader; - inputHeader.nonReference = true; - inputHeader.pictureId = 300; - inputHeader.temporalIdx = 1; - inputHeader.layerSync = false; - inputHeader.tl0PicIdx = kNoTl0PicIdx; // Disable. - inputHeader.keyIdx = 31; - RtpPacketizerVp8 packetizer(inputHeader, 20); - packetizer.SetPayloadData(payload, 10, NULL); - bool last; - size_t send_bytes; - ASSERT_TRUE(packetizer.NextPacket(packet, &send_bytes, &last)); - ASSERT_TRUE(last); - - RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, packet, send_bytes); - - RTPPayload parsedPacket; - ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket)); - - EXPECT_EQ(RtpUtility::kIFrame, parsedPacket.frameType); - EXPECT_EQ(kRtpVideoVp8, parsedPacket.type); - - VerifyBasicHeader(parsedPacket.info.VP8, - inputHeader.nonReference /*N*/, - 1 /*S*/, - 0 /*PartID*/); - VerifyExtensions(parsedPacket.info.VP8, - 1 /*I*/, - 0 /*L*/, - 1 /*T*/, - 1 /*K*/); - - EXPECT_EQ(inputHeader.pictureId, parsedPacket.info.VP8.pictureID); - EXPECT_EQ(inputHeader.temporalIdx, parsedPacket.info.VP8.tID); - EXPECT_EQ(inputHeader.layerSync, parsedPacket.info.VP8.layerSync); - EXPECT_EQ(inputHeader.keyIdx, parsedPacket.info.VP8.keyIdx); - - EXPECT_EQ(packet + 5, parsedPacket.info.VP8.data); - EXPECT_EQ(send_bytes - 5, parsedPacket.info.VP8.dataLength); -} - -} // namespace