Refactor VP8 de-packetizer.

It's duplicated to parse VP8 RTP packet at the moment. We firstly call
RTPPayloadParser functions to save parsed information in RTPPayload
structure, then copy them to RTP header.

This CL removes RTPPayloadParser class and directly saves parsed data in
RTP header.

R=pbos@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/27449004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7211 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2014-09-17 11:58:20 +00:00
parent 3bd5603b18
commit 5a098c51ea
7 changed files with 529 additions and 676 deletions

View File

@ -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',

View File

@ -15,10 +15,223 @@
#include <vector>
#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;

View File

@ -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<RtpDepacketizer> 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

View File

@ -24,7 +24,6 @@ namespace webrtc {
using RtpUtility::GetCurrentRTP;
using RtpUtility::Payload;
using RtpUtility::RTPPayloadParser;
using RtpUtility::StringCompare;
RtpReceiver* RtpReceiver::CreateVideoReceiver(

View File

@ -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

View File

@ -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_

View File

@ -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