blob: 5739ec019b247f846ffce94efedae5d96f0a950d [file] [log] [blame]
/*
* Copyright (c) 2020 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_h264.h"
#include <cstdint>
#include <optional>
#include <vector>
#include "api/array_view.h"
#include "api/video/video_codec_type.h"
#include "api/video/video_frame_type.h"
#include "common_video/h264/h264_common.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtp_format_h264.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
#include "rtc_base/buffer.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
using ::testing::SizeIs;
// clang-format off: split example data on NAL unit boundaries.
constexpr uint8_t kOriginalSps[] = {
H264::kSps, 0x00, 0x00, 0x03, 0x03,
0xF4, 0x05, 0x03, 0xC7, 0xC0
};
constexpr uint8_t kRewrittenSps[] = {
H264::kSps, 0x00, 0x00, 0x03, 0x03,
0xF4, 0x05, 0x03, 0xC7, 0xE0,
0x1B, 0x41, 0x10, 0x8D, 0x00
};
constexpr uint8_t kIdrOne[] = {H264::kIdr, 0xFF, 0x00, 0x00, 0x04};
constexpr uint8_t kIdrTwo[] = {H264::kIdr, 0xFF, 0x00, 0x11};
// clang-format on
TEST(VideoRtpDepacketizerH264Test, SingleNalu) {
const uint8_t kPayload[] = {H264::kIdr, 0xFF}; // F=0, NRI=0, Type=5 (IDR).
CopyOnWriteBuffer rtp_payload(kPayload);
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
const RTPVideoHeaderH264& h264 =
std::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
EXPECT_EQ(h264.nalu_type, H264::kIdr);
}
TEST(VideoRtpDepacketizerH264Test, SingleNaluSpsWithResolution) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9,
0x40, 0x50, 0x05, 0xBA, 0x10, 0x00,
0x00, 0x03, 0x00, 0xC0, 0x00, 0x00,
0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25
};
// clang-format on
CopyOnWriteBuffer rtp_payload(kPayload);
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed->video_header.width, 1280u);
EXPECT_EQ(parsed->video_header.height, 720u);
const auto& h264 =
std::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
}
TEST(VideoRtpDepacketizerH264Test, StapAKey) {
const NaluInfo kExpectedNalus[] = {
{.type = H264::kSps, .sps_id = 0, .pps_id = -1},
{.type = H264::kPps, .sps_id = 1, .pps_id = 2},
{.type = H264::kIdr, .sps_id = -1, .pps_id = 0}};
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kStapA, // F=0, NRI=0, Type=24.
// Length (2 bytes), nal header, payload.
0x00, 0x18,
kExpectedNalus[0].type, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x50, 0x05,
0xBA, 0x10, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x2A, 0xE0,
0xF1, 0x83, 0x25,
0x00, 0xD,
kExpectedNalus[1].type, 0x69, 0xFC, 0x0, 0x0, 0x3, 0x0, 0x7, 0xFF, 0xFF,
0xFF, 0xF6, 0x40,
0x00, 0xB,
kExpectedNalus[2].type, 0x85, 0xB8, 0x0, 0x4, 0x0, 0x0, 0x13, 0x93, 0x12,
0x0
};
// clang-format on
CopyOnWriteBuffer rtp_payload(kPayload);
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
const auto& h264 =
std::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264StapA);
// NALU type for aggregated packets is the type of the first packet only.
EXPECT_EQ(h264.nalu_type, H264::kSps);
EXPECT_THAT(h264.nalus, ElementsAreArray(kExpectedNalus));
}
TEST(VideoRtpDepacketizerH264Test, StapANaluSpsWithResolution) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kStapA, // F=0, NRI=0, Type=24.
// Length (2 bytes), nal header, payload.
0x00, 0x19,
H264::kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x50, 0x05, 0xBA, 0x10,
0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83,
0x25, 0x80, 0x00, 0x03,
H264::kIdr, 0xFF, 0x00,
0x00, 0x04,
H264::kIdr, 0xFF, 0x00, 0x11
};
// clang-format on
CopyOnWriteBuffer rtp_payload(kPayload);
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed->video_header.width, 1280u);
EXPECT_EQ(parsed->video_header.height, 720u);
const auto& h264 =
std::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264StapA);
}
TEST(VideoRtpDepacketizerH264Test, EmptyStapARejected) {
// clang-format off: split example data on NAL unit boundaries.
uint8_t lone_empty_packet[] = {
H264::kStapA, 0x00, 0x00
};
uint8_t leading_empty_packet[] = {
H264::kStapA,
0x00, 0x00, // Empty STAP-A is invalid.
0x00, 0x04,
H264::kIdr, 0xFF, 0x00, 0x11
};
uint8_t middle_empty_packet[] = {
H264::kStapA,
0x00, 0x03,
H264::kIdr, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x04,
H264::kIdr, 0xFF, 0x00, 0x11
};
uint8_t trailing_empty_packet[] = {
H264::kStapA,
0x00, 0x03,
H264::kIdr, 0xFF, 0x00, 0x00, 0x00
};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(lone_empty_packet)));
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(leading_empty_packet)));
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(middle_empty_packet)));
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(trailing_empty_packet)));
}
TEST(VideoRtpDepacketizerH264Test, DepacketizeWithRewriting) {
CopyOnWriteBuffer in_buffer;
Buffer out_buffer;
uint8_t kHeader[] = {H264::kStapA, 0x00};
in_buffer.AppendData(kHeader, 1);
out_buffer.AppendData(kHeader, 1);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kOriginalSps));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kOriginalSps);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kRewrittenSps));
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kRewrittenSps);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrOne));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kIdrOne);
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrOne);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrTwo));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kIdrTwo);
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrTwo);
VideoRtpDepacketizerH264 depacketizer;
auto parsed = depacketizer.Parse(in_buffer);
ASSERT_TRUE(parsed);
EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(),
parsed->video_payload.size()),
ElementsAreArray(out_buffer));
}
TEST(VideoRtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
CopyOnWriteBuffer in_buffer;
Buffer out_buffer;
uint8_t kHeader[] = {H264::kStapA, 0x00};
in_buffer.AppendData(kHeader, 1);
out_buffer.AppendData(kHeader, 1);
// First SPS will be kept...
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kOriginalSps));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kOriginalSps);
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kOriginalSps);
// ...only the second one will be rewritten.
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kOriginalSps));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kOriginalSps);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kRewrittenSps));
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kRewrittenSps);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrOne));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kIdrOne);
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrOne);
ByteWriter<uint16_t>::WriteBigEndian(kHeader, sizeof(kIdrTwo));
in_buffer.AppendData(kHeader, 2);
in_buffer.AppendData(kIdrTwo);
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrTwo);
VideoRtpDepacketizerH264 depacketizer;
auto parsed = depacketizer.Parse(in_buffer);
ASSERT_TRUE(parsed);
std::vector<uint8_t> expected_packet_payload(
out_buffer.data(), &out_buffer.data()[out_buffer.size()]);
EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(),
parsed->video_payload.size()),
ElementsAreArray(out_buffer));
}
TEST(VideoRtpDepacketizerH264Test, StapADelta) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kStapA, // F=0, NRI=0, Type=24.
// Length (2 bytes), nal header, payload.
0x00, 0x02,
H264::kSlice, 0xFF,
0x00, 0x03,
H264::kSlice, 0xFF, 0x00,
0x00, 0x04,
H264::kSlice, 0xFF, 0x00, 0x11
};
// clang-format on
CopyOnWriteBuffer rtp_payload(kPayload);
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload.size(), rtp_payload.size());
EXPECT_EQ(parsed->video_payload.cdata(), rtp_payload.cdata());
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
const RTPVideoHeaderH264& h264 =
std::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264StapA);
// NALU type for aggregated packets is the type of the first packet only.
EXPECT_EQ(h264.nalu_type, H264::kSlice);
}
TEST(VideoRtpDepacketizerH264Test, FuA) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload1[] = {
H264::kFuA, // F=0, NRI=0, Type=28.
kH264SBit | H264::kIdr, // FU header.
0x85, 0xB8, 0x00, 0x04, 0x00, 0x00, 0x13, 0x93, 0x12, 0x00 // Payload.
};
const uint8_t kExpected1[] = {
H264::kIdr, 0x85, 0xB8, 0x00, 0x04, 0x00,
0x00, 0x13, 0x93, 0x12, 0x00};
const uint8_t kPayload2[] = {
H264::kFuA, // F=0, NRI=0, Type=28.
H264::kIdr, // FU header.
0x02 // Payload.
};
const uint8_t kExpected2[] = {0x02};
const uint8_t kPayload3[] = {
H264::kFuA, // F=0, NRI=0, Type=28.
kH264EBit | H264::kIdr, // FU header.
0x03 // Payload.
};
const uint8_t kExpected3[] = {0x03};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed1 =
depacketizer.Parse(CopyOnWriteBuffer(kPayload1));
ASSERT_TRUE(parsed1);
// We expect that the first packet is one byte shorter since the FU-A header
// has been replaced by the original nal header.
EXPECT_THAT(MakeArrayView(parsed1->video_payload.cdata(),
parsed1->video_payload.size()),
ElementsAreArray(kExpected1));
EXPECT_EQ(parsed1->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed1->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed1->video_header.is_first_packet_in_frame);
{
const RTPVideoHeaderH264& h264 =
std::get<RTPVideoHeaderH264>(parsed1->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264FuA);
EXPECT_EQ(h264.nalu_type, H264::kIdr);
ASSERT_THAT(h264.nalus, SizeIs(1));
EXPECT_EQ(h264.nalus[0].type, H264::kIdr);
EXPECT_EQ(h264.nalus[0].sps_id, -1);
EXPECT_EQ(h264.nalus[0].pps_id, 0);
}
// Following packets will be 2 bytes shorter since they will only be appended
// onto the first packet.
auto parsed2 = depacketizer.Parse(CopyOnWriteBuffer(kPayload2));
EXPECT_THAT(MakeArrayView(parsed2->video_payload.cdata(),
parsed2->video_payload.size()),
ElementsAreArray(kExpected2));
EXPECT_FALSE(parsed2->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed2->video_header.codec, kVideoCodecH264);
{
const RTPVideoHeaderH264& h264 =
std::get<RTPVideoHeaderH264>(parsed2->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264FuA);
EXPECT_EQ(h264.nalu_type, H264::kIdr);
// NALU info is only expected for the first FU-A packet.
EXPECT_THAT(h264.nalus, IsEmpty());
}
auto parsed3 = depacketizer.Parse(CopyOnWriteBuffer(kPayload3));
EXPECT_THAT(MakeArrayView(parsed3->video_payload.cdata(),
parsed3->video_payload.size()),
ElementsAreArray(kExpected3));
EXPECT_FALSE(parsed3->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed3->video_header.codec, kVideoCodecH264);
{
const RTPVideoHeaderH264& h264 =
std::get<RTPVideoHeaderH264>(parsed3->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, H264PacketizationTypes::kH264FuA);
EXPECT_EQ(h264.nalu_type, H264::kIdr);
// NALU info is only expected for the first FU-A packet.
EXPECT_THAT(h264.nalus, IsEmpty());
}
}
TEST(VideoRtpDepacketizerH264Test, EmptyPayload) {
CopyOnWriteBuffer empty;
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(empty));
}
TEST(VideoRtpDepacketizerH264Test, TruncatedFuaNalu) {
const uint8_t kPayload[] = {0x9c};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, TruncatedSingleStapANalu) {
const uint8_t kPayload[] = {0xd8, 0x27};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, StapAPacketWithTruncatedNalUnits) {
const uint8_t kPayload[] = {0x58, 0xCB, 0xED, 0xDF};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, TruncationJustAfterSingleStapANalu) {
const uint8_t kPayload[] = {0x38, 0x27, 0x27};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, SeiPacket) {
const uint8_t kPayload[] = {
H264::kSei, // F=0, NRI=0, Type=6.
0x03, 0x03, 0x03, 0x03 // Payload.
};
VideoRtpDepacketizerH264 depacketizer;
auto parsed = depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
const RTPVideoHeaderH264& h264 =
std::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
EXPECT_EQ(h264.nalu_type, H264::kSei);
ASSERT_THAT(h264.nalus, SizeIs(1));
EXPECT_EQ(h264.nalus[0].type, H264::kSei);
EXPECT_EQ(h264.nalus[0].sps_id, -1);
EXPECT_EQ(h264.nalus[0].pps_id, -1);
}
TEST(VideoRtpDepacketizerH264Test, ShortSpsPacket) {
const uint8_t kPayload[] = {0x27, 0x80, 0x00};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, BadSps) {
const uint8_t kPayload[] = {
H264::kSps, 0x42, 0x41, 0x2a, 0xd3, 0x93, 0xd3, 0x3b // Payload.
};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, BadPps) {
const uint8_t kPayload[] = {
H264::kPps,
0x00 // Payload.
};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, BadSlice) {
const uint8_t kPayload[] = {
H264::kIdr,
0xc0 // Payload.
};
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(CopyOnWriteBuffer(kPayload)));
}
TEST(VideoRtpDepacketizerH264Test, StapASpsPpsMultiSlice) {
// A STAP-A containing a black 320x192 key frame with multiple slices.
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kStapA, // F=0, NRI=0, Type=24.
0x00, 0x10,
0x67, 0x42, 0xc0, 0x15, 0x8c, 0x68, 0x14, 0x19, // SPS.
0x79, 0xe0, 0x1e, 0x11, 0x08, 0xd4, 0x00, 0x04,
0x00, 0x06,
0x68, 0xce, 0x3c, 0x80, 0x00, 0x2e, // PPS.
// Slices.
0x00, 0x30,
0x65, 0xb8, 0x00, 0x04, 0x08, 0x79, 0x31, 0x40, 0x00, 0x42, 0xae, 0x4d,
0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9,
0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xd6, 0xeb, 0xae, 0xba, 0xeb, 0xae,
0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xbc, 0x00, 0x2f,
0x00, 0x2f,
0x65, 0x05, 0x2e, 0x00, 0x01, 0x02, 0x1e, 0x4c, 0x50, 0x00, 0x10, 0xab,
0x93, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x75, 0xba, 0xeb, 0xae, 0xba,
0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xaf,
0x00, 0x30,
0x65, 0x02, 0x8b, 0x80, 0x00, 0x40, 0x87, 0x93, 0x14, 0x00, 0x04, 0x2a,
0xe4, 0xdc, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x6e, 0xba, 0xeb, 0xae,
0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xc0,
0x00, 0x30,
0x65, 0x03, 0xcb, 0x80, 0x00, 0x40, 0x87, 0x93, 0x14, 0x00, 0x04, 0x2a,
0xe4, 0xdc, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x6e, 0xba, 0xeb, 0xae,
0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xc0,
0x00, 0x30,
0x65, 0x01, 0x42, 0xe0, 0x00, 0x10, 0x21, 0xe4, 0xc5, 0x00, 0x01, 0x0a,
0xb9, 0x37, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x5b, 0xae, 0xba, 0xeb,
0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xf0,
0x00, 0x30,
0x65, 0x01, 0x92, 0xe0, 0x00, 0x10, 0x21, 0xe4, 0xc5, 0x00, 0x01, 0x0a,
0xb9, 0x37, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x5b, 0xae, 0xba, 0xeb,
0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xeb, 0xae, 0xba, 0xf0
};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}
TEST(VideoRtpDepacketizerH264Test, SecondSliceIdrNalu) {
// First few bytes of a second slice of an IDR nalu with
// first_mb_in_slice = 480.
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
0x65, 0x00, 0xf0, 0x88, 0x82, 0x01, 0x3b, 0xff, 0xdf, 0xfe, 0x0b, 0xbb,
0xfc, 0xb4, 0x30, 0xd1, 0x00, 0xef, 0xfd, 0xef, 0x0e, 0x79, 0x8b, 0x74,
0x9b, 0x44, 0xf3, 0xb8, 0x65, 0x8f, 0xa1, 0x92, 0x30, 0xf9, 0x40, 0x06,
0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x18, 0x87, 0x4f, 0x6a, 0xfe, 0x60, 0x03, 0x9f, 0xfe, 0xd8,
0x8b, 0xa6, 0x67, 0x31
};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
EXPECT_FALSE(parsed->video_header.is_first_packet_in_frame);
}
TEST(VideoRtpDepacketizerH264Test, AudSetsFirstPacketInFrame) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kAud, 0x10
};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}
TEST(VideoRtpDepacketizerH264Test, PpsSetsFirstPacketInFrame) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kPps, 0x69, 0xFC, 0x00, 0x00, 0x03, 0x00,
0x07, 0xFF, 0xFF, 0xFF, 0xF6, 0x40
};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}
TEST(VideoRtpDepacketizerH264Test, SeiSetsFirstPacketInFrame) {
// clang-format off: split example data on NAL unit boundaries.
const uint8_t kPayload[] = {
H264::kSei, 0x05, 0x04, 0xDE, 0xAD, 0xBE, 0xEF, 0x80
};
// clang-format on
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
}
TEST(VideoRtpDepacketizerH264Test, EmptyNaluPayload) {
const uint8_t kPayload[] = {H264::kEndOfSequence};
VideoRtpDepacketizerH264 depacketizer;
std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
}
} // namespace
} // namespace webrtc