webrtc/sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter_unittest.cc
Guy Hershenbaum 2fcb834bb4 Search for SPS NALU rather than assuming its position
Summary:
The implementation of H264AnnexBBufferHasVideoFormatDescription was
assuming that the SPS NALU is either the first NALU in the stream, or
the second one, in case an AUD NALU is present in the first location.
This change removes this assumption and instead searches for the SPS
NALU, failing only if we can't find one.

In addition, it cleans up some binary buffer manipulation code, using the
the parsed NALU indices we already have in AnnexBBufferReader instead.

Test Plan: Unit tests

Change-Id: Id9715aa1d751f0ba1a1992def2b690607896df56

bug: webrtc:8922
Change-Id: Id9715aa1d751f0ba1a1992def2b690607896df56
Reviewed-on: https://webrtc-review.googlesource.com/49982
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22205}
2018-02-27 14:39:59 +00:00

236 lines
9 KiB
C++

/*
* Copyright (c) 2015 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 <memory>
#include "common_video/h264/h264_common.h"
#include "rtc_base/arraysize.h"
#include "sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter.h"
#include "test/gtest.h"
namespace webrtc {
using H264::kSps;
static const uint8_t NALU_TEST_DATA_0[] = {0xAA, 0xBB, 0xCC};
static const uint8_t NALU_TEST_DATA_1[] = {0xDE, 0xAD, 0xBE, 0xEF};
TEST(H264VideoToolboxNaluTest, TestCreateVideoFormatDescription) {
const uint8_t sps_pps_buffer[] = {
// SPS nalu.
0x00, 0x00, 0x00, 0x01,
0x27, 0x42, 0x00, 0x1E, 0xAB, 0x40, 0xF0, 0x28, 0xD3, 0x70, 0x20, 0x20,
0x20, 0x20,
// PPS nalu.
0x00, 0x00, 0x00, 0x01,
0x28, 0xCE, 0x3C, 0x30
};
CMVideoFormatDescriptionRef description =
CreateVideoFormatDescription(sps_pps_buffer, arraysize(sps_pps_buffer));
EXPECT_TRUE(description);
if (description) {
CFRelease(description);
description = nullptr;
}
const uint8_t sps_pps_not_at_start_buffer[] = {
// Add some non-SPS/PPS NALUs at the beginning
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x01,
0xAB, 0x33, 0x21,
// SPS nalu.
0x00, 0x00, 0x01, 0x27, 0x42, 0x00, 0x1E, 0xAB, 0x40, 0xF0, 0x28, 0xD3,
0x70, 0x20, 0x20, 0x20, 0x20,
// PPS nalu.
0x00, 0x00, 0x01, 0x28, 0xCE, 0x3C, 0x30};
description = CreateVideoFormatDescription(
sps_pps_not_at_start_buffer, arraysize(sps_pps_not_at_start_buffer));
EXPECT_TRUE(description);
if (description) {
CFRelease(description);
description = nullptr;
}
const uint8_t other_buffer[] = {0x00, 0x00, 0x00, 0x01, 0x28};
EXPECT_FALSE(CreateVideoFormatDescription(other_buffer,
arraysize(other_buffer)));
}
TEST(AnnexBBufferReaderTest, TestReadEmptyInput) {
const uint8_t annex_b_test_data[] = {0x00};
AnnexBBufferReader reader(annex_b_test_data, 0);
const uint8_t* nalu = nullptr;
size_t nalu_length = 0;
EXPECT_EQ(0u, reader.BytesRemaining());
EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(nullptr, nalu);
EXPECT_EQ(0u, nalu_length);
}
TEST(AnnexBBufferReaderTest, TestReadSingleNalu) {
const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x00, 0x01, 0xAA};
AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data));
const uint8_t* nalu = nullptr;
size_t nalu_length = 0;
EXPECT_EQ(arraysize(annex_b_test_data), reader.BytesRemaining());
EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(annex_b_test_data + 4, nalu);
EXPECT_EQ(1u, nalu_length);
EXPECT_EQ(0u, reader.BytesRemaining());
EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(nullptr, nalu);
EXPECT_EQ(0u, nalu_length);
}
TEST(AnnexBBufferReaderTest, TestReadSingleNalu3ByteHeader) {
const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x01, 0xAA};
AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data));
const uint8_t* nalu = nullptr;
size_t nalu_length = 0;
EXPECT_EQ(arraysize(annex_b_test_data), reader.BytesRemaining());
EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(annex_b_test_data + 3, nalu);
EXPECT_EQ(1u, nalu_length);
EXPECT_EQ(0u, reader.BytesRemaining());
EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(nullptr, nalu);
EXPECT_EQ(0u, nalu_length);
}
TEST(AnnexBBufferReaderTest, TestReadMissingNalu) {
// clang-format off
const uint8_t annex_b_test_data[] = {0x01,
0x00, 0x01,
0x00, 0x00, 0x00, 0xFF};
// clang-format on
AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data));
const uint8_t* nalu = nullptr;
size_t nalu_length = 0;
EXPECT_EQ(0u, reader.BytesRemaining());
EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(nullptr, nalu);
EXPECT_EQ(0u, nalu_length);
}
TEST(AnnexBBufferReaderTest, TestReadMultipleNalus) {
// clang-format off
const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x00, 0x01, 0xFF,
0x01,
0x00, 0x01,
0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x01, 0xAA, 0xBB};
// clang-format on
AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data));
const uint8_t* nalu = nullptr;
size_t nalu_length = 0;
EXPECT_EQ(arraysize(annex_b_test_data), reader.BytesRemaining());
EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(annex_b_test_data + 4, nalu);
EXPECT_EQ(8u, nalu_length);
EXPECT_EQ(6u, reader.BytesRemaining());
EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(annex_b_test_data + 16, nalu);
EXPECT_EQ(2u, nalu_length);
EXPECT_EQ(0u, reader.BytesRemaining());
EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(nullptr, nalu);
EXPECT_EQ(0u, nalu_length);
}
TEST(AnnexBBufferReaderTest, TestFindNextNaluOfType) {
const uint8_t notSps = 0x1F;
const uint8_t annex_b_test_data[] = {
0x00, 0x00, 0x00, 0x01, kSps, 0x00, 0x00, 0x01, notSps,
0x00, 0x00, 0x01, notSps, 0xDD, 0x00, 0x00, 0x01, notSps,
0xEE, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, kSps, 0xBB, 0x00, 0x00,
0x01, notSps, 0x00, 0x00, 0x01, notSps, 0xDD, 0x00, 0x00,
0x01, notSps, 0xEE, 0xFF, 0x00, 0x00, 0x00, 0x01};
AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data));
const uint8_t* nalu = nullptr;
size_t nalu_length = 0;
EXPECT_EQ(arraysize(annex_b_test_data), reader.BytesRemaining());
EXPECT_TRUE(reader.FindNextNaluOfType(kSps));
EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(annex_b_test_data + 4, nalu);
EXPECT_EQ(1u, nalu_length);
EXPECT_TRUE(reader.FindNextNaluOfType(kSps));
EXPECT_TRUE(reader.ReadNalu(&nalu, &nalu_length));
EXPECT_EQ(annex_b_test_data + 32, nalu);
EXPECT_EQ(2u, nalu_length);
EXPECT_FALSE(reader.FindNextNaluOfType(kSps));
EXPECT_FALSE(reader.ReadNalu(&nalu, &nalu_length));
}
TEST(AvccBufferWriterTest, TestEmptyOutputBuffer) {
const uint8_t expected_buffer[] = {0x00};
const size_t buffer_size = 1;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
memset(buffer.get(), 0, buffer_size);
AvccBufferWriter writer(buffer.get(), 0);
EXPECT_EQ(0u, writer.BytesRemaining());
EXPECT_FALSE(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0)));
EXPECT_EQ(0,
memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer)));
}
TEST(AvccBufferWriterTest, TestWriteSingleNalu) {
const uint8_t expected_buffer[] = {
0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC,
};
const size_t buffer_size = arraysize(NALU_TEST_DATA_0) + 4;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
AvccBufferWriter writer(buffer.get(), buffer_size);
EXPECT_EQ(buffer_size, writer.BytesRemaining());
EXPECT_TRUE(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0)));
EXPECT_EQ(0u, writer.BytesRemaining());
EXPECT_FALSE(writer.WriteNalu(NALU_TEST_DATA_1, arraysize(NALU_TEST_DATA_1)));
EXPECT_EQ(0,
memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer)));
}
TEST(AvccBufferWriterTest, TestWriteMultipleNalus) {
// clang-format off
const uint8_t expected_buffer[] = {
0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC,
0x00, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF
};
// clang-format on
const size_t buffer_size =
arraysize(NALU_TEST_DATA_0) + arraysize(NALU_TEST_DATA_1) + 8;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
AvccBufferWriter writer(buffer.get(), buffer_size);
EXPECT_EQ(buffer_size, writer.BytesRemaining());
EXPECT_TRUE(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0)));
EXPECT_EQ(buffer_size - (arraysize(NALU_TEST_DATA_0) + 4),
writer.BytesRemaining());
EXPECT_TRUE(writer.WriteNalu(NALU_TEST_DATA_1, arraysize(NALU_TEST_DATA_1)));
EXPECT_EQ(0u, writer.BytesRemaining());
EXPECT_EQ(0,
memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer)));
}
TEST(AvccBufferWriterTest, TestOverflow) {
const uint8_t expected_buffer[] = {0x00, 0x00, 0x00};
const size_t buffer_size = arraysize(NALU_TEST_DATA_0);
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
memset(buffer.get(), 0, buffer_size);
AvccBufferWriter writer(buffer.get(), buffer_size);
EXPECT_EQ(buffer_size, writer.BytesRemaining());
EXPECT_FALSE(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0)));
EXPECT_EQ(buffer_size, writer.BytesRemaining());
EXPECT_EQ(0,
memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer)));
}
} // namespace webrtc