diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index a8a1b03d50..ff731baccf 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -1037,6 +1037,7 @@ if (is_ios || is_mac) { "objc/unittests/RTCPeerConnectionFactory_xctest.m", "objc/unittests/frame_buffer_helpers.h", "objc/unittests/frame_buffer_helpers.mm", + "objc/unittests/nalu_rewriter_xctest.mm", ] # TODO(peterhanspers): Reenable these tests on simulator. @@ -1059,6 +1060,7 @@ if (is_ios || is_mac) { ":native_api_audio_device_module", ":native_video", ":peerconnectionfactory_base_objc", + ":video_toolbox_cc", ":videocapture_objc", ":videocodec_objc", ":videoframebuffer_objc", @@ -1599,7 +1601,10 @@ if (is_ios || is_mac) { } rtc_static_library("video_toolbox_cc") { - visibility = [ ":videotoolbox_objc" ] + visibility = [ + ":videotoolbox_objc", + ":sdk_unittests_sources", + ] sources = [ "objc/components/video_codec/helpers.cc", "objc/components/video_codec/helpers.h", diff --git a/sdk/objc/components/video_codec/nalu_rewriter_unittest.cc b/sdk/objc/components/video_codec/nalu_rewriter_unittest.cc deleted file mode 100644 index 4dc00c9231..0000000000 --- a/sdk/objc/components/video_codec/nalu_rewriter_unittest.cc +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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 - -#include "common_video/h264/h264_common.h" -#include "rtc_base/arraysize.h" -#include "sdk/objc/components/video_codec/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 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 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 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 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 diff --git a/sdk/objc/unittests/nalu_rewriter_xctest.mm b/sdk/objc/unittests/nalu_rewriter_xctest.mm new file mode 100644 index 0000000000..4b049901c5 --- /dev/null +++ b/sdk/objc/unittests/nalu_rewriter_xctest.mm @@ -0,0 +1,414 @@ +/* + * Copyright 2018 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 "common_video/h264/h264_common.h" +#include "components/video_codec/nalu_rewriter.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/gunit.h" + +#import + +#if TARGET_OS_IPHONE +#import +#import +#endif + +@interface NaluRewriterTests : XCTestCase + +@end + +static const uint8_t NALU_TEST_DATA_0[] = {0xAA, 0xBB, 0xCC}; +static const uint8_t NALU_TEST_DATA_1[] = {0xDE, 0xAD, 0xBE, 0xEF}; + +// clang-format off +static 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}; +// clang-format on + +@implementation NaluRewriterTests + +- (void)testCreateVideoFormatDescription { + CMVideoFormatDescriptionRef description = + webrtc::CreateVideoFormatDescription(SPS_PPS_BUFFER, arraysize(SPS_PPS_BUFFER)); + XCTAssertTrue(description); + if (description) { + CFRelease(description); + description = nullptr; + } + + // clang-format off + 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}; + // clang-format on + description = webrtc::CreateVideoFormatDescription(sps_pps_not_at_start_buffer, + arraysize(sps_pps_not_at_start_buffer)); + + XCTAssertTrue(description); + + if (description) { + CFRelease(description); + description = nullptr; + } + + const uint8_t other_buffer[] = {0x00, 0x00, 0x00, 0x01, 0x28}; + XCTAssertFalse(webrtc::CreateVideoFormatDescription(other_buffer, arraysize(other_buffer))); +} + +- (void)testReadEmptyInput { + const uint8_t annex_b_test_data[] = {0x00}; + webrtc::AnnexBBufferReader reader(annex_b_test_data, 0); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadSingleNalu { + const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x00, 0x01, 0xAA}; + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(arraysize(annex_b_test_data), reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 4, nalu); + XCTAssertEqual(1u, nalu_length); + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadSingleNalu3ByteHeader { + const uint8_t annex_b_test_data[] = {0x00, 0x00, 0x01, 0xAA}; + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(arraysize(annex_b_test_data), reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 3, nalu); + XCTAssertEqual(1u, nalu_length); + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testReadMissingNalu { + // clang-format off + const uint8_t annex_b_test_data[] = {0x01, + 0x00, 0x01, + 0x00, 0x00, 0x00, 0xFF}; + // clang-format on + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)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 + webrtc::AnnexBBufferReader reader(annex_b_test_data, arraysize(annex_b_test_data)); + const uint8_t* nalu = nullptr; + size_t nalu_length = 0; + XCTAssertEqual(arraysize(annex_b_test_data), reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 4, nalu); + XCTAssertEqual(8u, nalu_length); + XCTAssertEqual(5u, reader.BytesRemaining()); + XCTAssertTrue(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(annex_b_test_data + 15, nalu); + XCTAssertEqual(2u, nalu_length); + XCTAssertEqual(0u, reader.BytesRemaining()); + XCTAssertFalse(reader.ReadNalu(&nalu, &nalu_length)); + XCTAssertEqual(nullptr, nalu); + XCTAssertEqual(0u, nalu_length); +} + +- (void)testEmptyOutputBuffer { + const uint8_t expected_buffer[] = {0x00}; + const size_t buffer_size = 1; + std::unique_ptr buffer(new uint8_t[buffer_size]); + memset(buffer.get(), 0, buffer_size); + webrtc::AvccBufferWriter writer(buffer.get(), 0); + XCTAssertEqual(0u, writer.BytesRemaining()); + XCTAssertFalse(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)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 buffer(new uint8_t[buffer_size]); + webrtc::AvccBufferWriter writer(buffer.get(), buffer_size); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertTrue(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(0u, writer.BytesRemaining()); + XCTAssertFalse(writer.WriteNalu(NALU_TEST_DATA_1, arraysize(NALU_TEST_DATA_1))); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)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 buffer(new uint8_t[buffer_size]); + webrtc::AvccBufferWriter writer(buffer.get(), buffer_size); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertTrue(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(buffer_size - (arraysize(NALU_TEST_DATA_0) + 4), writer.BytesRemaining()); + XCTAssertTrue(writer.WriteNalu(NALU_TEST_DATA_1, arraysize(NALU_TEST_DATA_1))); + XCTAssertEqual(0u, writer.BytesRemaining()); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)testOverflow { + const uint8_t expected_buffer[] = {0x00, 0x00, 0x00}; + const size_t buffer_size = arraysize(NALU_TEST_DATA_0); + std::unique_ptr buffer(new uint8_t[buffer_size]); + memset(buffer.get(), 0, buffer_size); + webrtc::AvccBufferWriter writer(buffer.get(), buffer_size); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertFalse(writer.WriteNalu(NALU_TEST_DATA_0, arraysize(NALU_TEST_DATA_0))); + XCTAssertEqual(buffer_size, writer.BytesRemaining()); + XCTAssertEqual(0, memcmp(expected_buffer, buffer.get(), arraysize(expected_buffer))); +} + +- (void)testH264AnnexBBufferToCMSampleBuffer { + // clang-format off + const uint8_t annex_b_test_data[] = { + 0x00, + 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x01, + 0xAA, 0xFF, // second chunk, 2 bytes + 0x00, 0x00, 0x01, + 0xBB}; // third chunk, 1 byte, will not fit into output array + + const uint8_t expected_cmsample_data[] = { + 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x02, + 0xAA, 0xFF}; // second chunk, 2 bytes + // clang-format on + + CMMemoryPoolRef memory_pool = CMMemoryPoolCreate(nil); + CMSampleBufferRef out_sample_buffer = nil; + CMVideoFormatDescriptionRef description = [self createDescription]; + + Boolean result = webrtc::H264AnnexBBufferToCMSampleBuffer(annex_b_test_data, + arraysize(annex_b_test_data), + description, + &out_sample_buffer, + memory_pool); + + XCTAssertTrue(result); + + XCTAssertEqual(description, CMSampleBufferGetFormatDescription(out_sample_buffer)); + + char* data_ptr = nullptr; + CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(out_sample_buffer); + size_t block_buffer_size = CMBlockBufferGetDataLength(block_buffer); + CMBlockBufferGetDataPointer(block_buffer, 0, nullptr, nullptr, &data_ptr); + XCTAssertEqual(block_buffer_size, arraysize(annex_b_test_data)); + + int data_comparison_result = + memcmp(expected_cmsample_data, data_ptr, arraysize(expected_cmsample_data)); + + XCTAssertEqual(0, data_comparison_result); + + if (description) { + CFRelease(description); + description = nullptr; + } + + CMMemoryPoolInvalidate(memory_pool); + CFRelease(memory_pool); +} + +- (void)testH264CMSampleBufferToAnnexBBuffer { + // clang-format off + const uint8_t cmsample_data[] = { + 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x02, + 0xAA, 0xFF}; // second chunk, 2 bytes + + const uint8_t expected_annex_b_data[] = { + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x01, + 0xAA, 0xFF}; // second chunk, 2 bytes + // clang-format on + + rtc::Buffer annexb_buffer(arraysize(cmsample_data)); + std::unique_ptr out_header_ptr; + CMSampleBufferRef sample_buffer = + [self createCMSampleBufferRef:(void*)cmsample_data cmsampleSize:arraysize(cmsample_data)]; + + Boolean result = webrtc::H264CMSampleBufferToAnnexBBuffer(sample_buffer, + /* is_keyframe */ false, + &annexb_buffer, + &out_header_ptr); + + XCTAssertTrue(result); + + XCTAssertEqual(arraysize(expected_annex_b_data), annexb_buffer.size()); + + int data_comparison_result = + memcmp(expected_annex_b_data, annexb_buffer.data(), arraysize(expected_annex_b_data)); + + XCTAssertEqual(0, data_comparison_result); + + webrtc::RTPFragmentationHeader* out_header = out_header_ptr.get(); + + XCTAssertEqual(2, (int)out_header->Size()); + + XCTAssertEqual(4, (int)out_header->Offset(0)); + XCTAssertEqual(4, (int)out_header->Length(0)); + XCTAssertEqual(0, (int)out_header->TimeDiff(0)); + XCTAssertEqual(0, (int)out_header->PayloadType(0)); + + XCTAssertEqual(12, (int)out_header->Offset(1)); + XCTAssertEqual(2, (int)out_header->Length(1)); + XCTAssertEqual(0, (int)out_header->TimeDiff(1)); + XCTAssertEqual(0, (int)out_header->PayloadType(1)); +} + +- (void)testH264CMSampleBufferToAnnexBBufferWithKeyframe { + // clang-format off + const uint8_t cmsample_data[] = { + 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x02, + 0xAA, 0xFF}; // second chunk, 2 bytes + + const uint8_t expected_annex_b_data[] = { + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0xFF, // first chunk, 4 bytes + 0x00, 0x00, 0x00, 0x01, + 0xAA, 0xFF}; // second chunk, 2 bytes + // clang-format on + + rtc::Buffer annexb_buffer(arraysize(cmsample_data)); + std::unique_ptr out_header_ptr; + CMSampleBufferRef sample_buffer = + [self createCMSampleBufferRef:(void*)cmsample_data cmsampleSize:arraysize(cmsample_data)]; + + Boolean result = webrtc::H264CMSampleBufferToAnnexBBuffer(sample_buffer, + /* is_keyframe */ true, + &annexb_buffer, + &out_header_ptr); + + XCTAssertTrue(result); + + XCTAssertEqual(arraysize(SPS_PPS_BUFFER) + arraysize(expected_annex_b_data), + annexb_buffer.size()); + + XCTAssertEqual(0, memcmp(SPS_PPS_BUFFER, annexb_buffer.data(), arraysize(SPS_PPS_BUFFER))); + + XCTAssertEqual(0, + memcmp(expected_annex_b_data, + annexb_buffer.data() + arraysize(SPS_PPS_BUFFER), + arraysize(expected_annex_b_data))); + + webrtc::RTPFragmentationHeader* out_header = out_header_ptr.get(); + + XCTAssertEqual(4, (int)out_header->Size()); + + XCTAssertEqual(4, (int)out_header->Offset(0)); + XCTAssertEqual(14, (int)out_header->Length(0)); + XCTAssertEqual(0, (int)out_header->TimeDiff(0)); + XCTAssertEqual(0, (int)out_header->PayloadType(0)); + + XCTAssertEqual(22, (int)out_header->Offset(1)); + XCTAssertEqual(4, (int)out_header->Length(1)); + XCTAssertEqual(0, (int)out_header->TimeDiff(1)); + XCTAssertEqual(0, (int)out_header->PayloadType(1)); + + XCTAssertEqual(30, (int)out_header->Offset(2)); + XCTAssertEqual(4, (int)out_header->Length(2)); + XCTAssertEqual(0, (int)out_header->TimeDiff(2)); + XCTAssertEqual(0, (int)out_header->PayloadType(2)); + + XCTAssertEqual(38, (int)out_header->Offset(3)); + XCTAssertEqual(2, (int)out_header->Length(3)); + XCTAssertEqual(0, (int)out_header->TimeDiff(3)); + XCTAssertEqual(0, (int)out_header->PayloadType(3)); +} + +- (CMVideoFormatDescriptionRef)createDescription { + CMVideoFormatDescriptionRef description = + webrtc::CreateVideoFormatDescription(SPS_PPS_BUFFER, arraysize(SPS_PPS_BUFFER)); + XCTAssertTrue(description); + return description; +} + +- (CMSampleBufferRef)createCMSampleBufferRef:(void*)cmsampleData cmsampleSize:(size_t)cmsampleSize { + CMSampleBufferRef sample_buffer = nil; + OSStatus status; + + CMVideoFormatDescriptionRef description = [self createDescription]; + CMBlockBufferRef block_buffer = nullptr; + + status = CMBlockBufferCreateWithMemoryBlock(nullptr, + cmsampleData, + cmsampleSize, + nullptr, + nullptr, + 0, + cmsampleSize, + kCMBlockBufferAssureMemoryNowFlag, + &block_buffer); + + status = CMSampleBufferCreate(nullptr, + block_buffer, + true, + nullptr, + nullptr, + description, + 1, + 0, + nullptr, + 0, + nullptr, + &sample_buffer); + + return sample_buffer; +} + +@end