blob: 7bc9e976a6cce63ea74c69c1b66bd8ce14170616 [file] [log] [blame]
/*
* Copyright (c) 2019 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.
*/
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
#include "api/array_view.h"
#include "modules/rtp_rtcp/source/rtp_format_vp8.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "test/gmock.h"
#include "test/gtest.h"
// VP8 payload descriptor
// https://datatracker.ietf.org/doc/html/rfc7741#section-4.2
//
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |X|R|N|S|R| PID | (REQUIRED)
// +-+-+-+-+-+-+-+-+
// X: |I|L|T|K| RSV | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// I: |M| PictureID | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// | PictureID |
// +-+-+-+-+-+-+-+-+
// L: | TL0PICIDX | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// T/K: |TID|Y| KEYIDX | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
//
// VP8 payload header. Considered part of the actual payload, sent to decoder.
// https://datatracker.ietf.org/doc/html/rfc7741#section-4.3
//
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |Size0|H| VER |P|
// +-+-+-+-+-+-+-+-+
// : ... :
// +-+-+-+-+-+-+-+-+
namespace webrtc {
namespace {
TEST(VideoRtpDepacketizerVp8Test, BasicHeader) {
uint8_t packet[4] = {0};
packet[0] = 0b0001'0100; // S = 1, partition ID = 4.
packet[1] = 0x01; // P frame.
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 1);
EXPECT_EQ(video_header.frame_type, VideoFrameType::kVideoFrameDelta);
EXPECT_EQ(video_header.codec, kVideoCodecVP8);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_FALSE(vp8_header.nonReference);
EXPECT_TRUE(vp8_header.beginningOfPartition);
EXPECT_EQ(vp8_header.partitionId, 4);
EXPECT_EQ(vp8_header.pictureId, kNoPictureId);
EXPECT_EQ(vp8_header.tl0PicIdx, kNoTl0PicIdx);
EXPECT_EQ(vp8_header.temporalIdx, kNoTemporalIdx);
EXPECT_EQ(vp8_header.keyIdx, kNoKeyIdx);
}
TEST(VideoRtpDepacketizerVp8Test, OneBytePictureID) {
const uint8_t kPictureId = 17;
uint8_t packet[10] = {0};
packet[0] = 0b1010'0000;
packet[1] = 0b1000'0000;
packet[2] = kPictureId;
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 3);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_EQ(vp8_header.pictureId, kPictureId);
}
TEST(VideoRtpDepacketizerVp8Test, TwoBytePictureID) {
const uint16_t kPictureId = 0x1234;
uint8_t packet[10] = {0};
packet[0] = 0b1010'0000;
packet[1] = 0b1000'0000;
packet[2] = 0x80 | 0x12;
packet[3] = 0x34;
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 4);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_EQ(vp8_header.pictureId, kPictureId);
}
TEST(VideoRtpDepacketizerVp8Test, Tl0PicIdx) {
const uint8_t kTl0PicIdx = 17;
uint8_t packet[13] = {0};
packet[0] = 0b1000'0000;
packet[1] = 0b0100'0000;
packet[2] = kTl0PicIdx;
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 3);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_EQ(vp8_header.tl0PicIdx, kTl0PicIdx);
}
TEST(VideoRtpDepacketizerVp8Test, TIDAndLayerSync) {
uint8_t packet[10] = {0};
packet[0] = 0b1000'0000;
packet[1] = 0b0010'0000;
packet[2] = 0b10'0'00000; // TID(2) + LayerSync(false)
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 3);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_EQ(vp8_header.temporalIdx, 2);
EXPECT_FALSE(vp8_header.layerSync);
}
TEST(VideoRtpDepacketizerVp8Test, KeyIdx) {
const uint8_t kKeyIdx = 17;
uint8_t packet[10] = {0};
packet[0] = 0b1000'0000;
packet[1] = 0b0001'0000;
packet[2] = kKeyIdx;
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 3);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_EQ(vp8_header.keyIdx, kKeyIdx);
}
TEST(VideoRtpDepacketizerVp8Test, MultipleExtensions) {
uint8_t packet[10] = {0};
packet[0] = 0b1010'0110; // X and N bit set, partition ID = 6
packet[1] = 0b1111'0000;
packet[2] = 0x80 | 0x12; // PictureID, high 7 bits.
packet[3] = 0x34; // PictureID, low 8 bits.
packet[4] = 42; // Tl0PicIdx.
packet[5] = 0b01'1'10001;
RTPVideoHeader video_header;
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
EXPECT_EQ(offset, 6);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
EXPECT_TRUE(vp8_header.nonReference);
EXPECT_EQ(vp8_header.partitionId, 0b0110);
EXPECT_EQ(vp8_header.pictureId, 0x1234);
EXPECT_EQ(vp8_header.tl0PicIdx, 42);
EXPECT_EQ(vp8_header.temporalIdx, 1);
EXPECT_TRUE(vp8_header.layerSync);
EXPECT_EQ(vp8_header.keyIdx, 0b10001);
}
TEST(VideoRtpDepacketizerVp8Test, TooShortHeader) {
uint8_t packet[4] = {0};
packet[0] = 0b1000'0000;
packet[1] = 0b1111'0000; // All extensions are enabled...
packet[2] = 0x80 | 17; // ... but only 2 bytes PictureID is provided.
packet[3] = 17; // PictureID, low 8 bits.
RTPVideoHeader unused;
EXPECT_EQ(VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &unused), 0);
}
TEST(VideoRtpDepacketizerVp8Test, WithPacketizer) {
uint8_t data[10] = {0};
RtpPacketToSend packet(/*extenions=*/nullptr);
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(data, /*limits=*/{}, input_header);
EXPECT_EQ(packetizer.NumPackets(), 1u);
ASSERT_TRUE(packetizer.NextPacket(&packet));
VideoRtpDepacketizerVp8 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(packet.PayloadBuffer());
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecVP8);
const auto& vp8_header =
absl::get<RTPVideoHeaderVP8>(parsed->video_header.video_type_header);
EXPECT_EQ(vp8_header.nonReference, input_header.nonReference);
EXPECT_EQ(vp8_header.pictureId, input_header.pictureId);
EXPECT_EQ(vp8_header.tl0PicIdx, input_header.tl0PicIdx);
EXPECT_EQ(vp8_header.temporalIdx, input_header.temporalIdx);
EXPECT_EQ(vp8_header.layerSync, input_header.layerSync);
EXPECT_EQ(vp8_header.keyIdx, input_header.keyIdx);
}
TEST(VideoRtpDepacketizerVp8Test, ReferencesInputCopyOnWriteBuffer) {
constexpr size_t kHeaderSize = 5;
uint8_t packet[16] = {0};
packet[0] = 0b1000'0000;
packet[1] = 0b1111'0000; // with all extensions,
packet[2] = 15; // and one-byte picture id.
rtc::CopyOnWriteBuffer rtp_payload(packet);
VideoRtpDepacketizerVp8 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload.size(), rtp_payload.size() - kHeaderSize);
// Compare pointers to check there was no copy on write buffer unsharing.
EXPECT_EQ(parsed->video_payload.cdata(), rtp_payload.cdata() + kHeaderSize);
}
TEST(VideoRtpDepacketizerVp8Test, FailsOnEmptyPayload) {
rtc::ArrayView<const uint8_t> empty;
RTPVideoHeader video_header;
EXPECT_EQ(VideoRtpDepacketizerVp8::ParseRtpPayload(empty, &video_header), 0);
}
} // namespace
} // namespace webrtc