|  | /* | 
|  | *  Copyright (c) 2016 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 "webrtc/modules/video_coding/utility/ivf_file_writer.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "webrtc/base/helpers.h" | 
|  | #include "webrtc/base/logging.h" | 
|  | #include "webrtc/base/thread.h" | 
|  | #include "webrtc/base/timeutils.h" | 
|  | #include "webrtc/modules/rtp_rtcp/source/byte_io.h" | 
|  | #include "webrtc/test/gtest.h" | 
|  | #include "webrtc/test/testsupport/fileutils.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | namespace { | 
|  | static const int kHeaderSize = 32; | 
|  | static const int kFrameHeaderSize = 12; | 
|  | static uint8_t dummy_payload[4] = {0, 1, 2, 3}; | 
|  | }  // namespace | 
|  |  | 
|  | class IvfFileWriterTest : public ::testing::Test { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | file_name_ = | 
|  | webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file"); | 
|  | } | 
|  | void TearDown() override { rtc::RemoveFile(file_name_); } | 
|  |  | 
|  | bool WriteDummyTestFrames(VideoCodecType codec_type, | 
|  | int width, | 
|  | int height, | 
|  | int num_frames, | 
|  | bool use_capture_tims_ms) { | 
|  | EncodedImage frame; | 
|  | frame._buffer = dummy_payload; | 
|  | frame._encodedWidth = width; | 
|  | frame._encodedHeight = height; | 
|  | for (int i = 1; i <= num_frames; ++i) { | 
|  | frame._length = i % sizeof(dummy_payload); | 
|  | if (use_capture_tims_ms) { | 
|  | frame.capture_time_ms_ = i; | 
|  | } else { | 
|  | frame._timeStamp = i; | 
|  | } | 
|  | if (!file_writer_->WriteFrame(frame, codec_type)) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void VerifyIvfHeader(rtc::File* file, | 
|  | const uint8_t fourcc[4], | 
|  | int width, | 
|  | int height, | 
|  | uint32_t num_frames, | 
|  | bool use_capture_tims_ms) { | 
|  | ASSERT_TRUE(file->IsOpen()); | 
|  | uint8_t data[kHeaderSize]; | 
|  | ASSERT_EQ(static_cast<size_t>(kHeaderSize), file->Read(data, kHeaderSize)); | 
|  |  | 
|  | uint8_t dkif[4] = {'D', 'K', 'I', 'F'}; | 
|  | EXPECT_EQ(0, memcmp(dkif, data, 4)); | 
|  | EXPECT_EQ(0u, ByteReader<uint16_t>::ReadLittleEndian(&data[4])); | 
|  | EXPECT_EQ(32u, ByteReader<uint16_t>::ReadLittleEndian(&data[6])); | 
|  | EXPECT_EQ(0, memcmp(fourcc, &data[8], 4)); | 
|  | EXPECT_EQ(width, ByteReader<uint16_t>::ReadLittleEndian(&data[12])); | 
|  | EXPECT_EQ(height, ByteReader<uint16_t>::ReadLittleEndian(&data[14])); | 
|  | EXPECT_EQ(use_capture_tims_ms ? 1000u : 90000u, | 
|  | ByteReader<uint32_t>::ReadLittleEndian(&data[16])); | 
|  | EXPECT_EQ(1u, ByteReader<uint32_t>::ReadLittleEndian(&data[20])); | 
|  | EXPECT_EQ(num_frames, ByteReader<uint32_t>::ReadLittleEndian(&data[24])); | 
|  | EXPECT_EQ(0u, ByteReader<uint32_t>::ReadLittleEndian(&data[28])); | 
|  | } | 
|  |  | 
|  | void VerifyDummyTestFrames(rtc::File* file, uint32_t num_frames) { | 
|  | const int kMaxFrameSize = 4; | 
|  | for (uint32_t i = 1; i <= num_frames; ++i) { | 
|  | uint8_t frame_header[kFrameHeaderSize]; | 
|  | ASSERT_EQ(static_cast<unsigned int>(kFrameHeaderSize), | 
|  | file->Read(frame_header, kFrameHeaderSize)); | 
|  | uint32_t frame_length = | 
|  | ByteReader<uint32_t>::ReadLittleEndian(&frame_header[0]); | 
|  | EXPECT_EQ(i % 4, frame_length); | 
|  | uint64_t timestamp = | 
|  | ByteReader<uint64_t>::ReadLittleEndian(&frame_header[4]); | 
|  | EXPECT_EQ(i, timestamp); | 
|  |  | 
|  | uint8_t data[kMaxFrameSize] = {}; | 
|  | ASSERT_EQ(frame_length, | 
|  | static_cast<uint32_t>(file->Read(data, frame_length))); | 
|  | EXPECT_EQ(0, memcmp(data, dummy_payload, frame_length)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RunBasicFileStructureTest(VideoCodecType codec_type, | 
|  | const uint8_t fourcc[4], | 
|  | bool use_capture_tims_ms) { | 
|  | file_writer_ = IvfFileWriter::Wrap(rtc::File::Open(file_name_), 0); | 
|  | ASSERT_TRUE(file_writer_.get()); | 
|  | const int kWidth = 320; | 
|  | const int kHeight = 240; | 
|  | const int kNumFrames = 257; | 
|  | ASSERT_TRUE(WriteDummyTestFrames(codec_type, kWidth, kHeight, kNumFrames, | 
|  | use_capture_tims_ms)); | 
|  | EXPECT_TRUE(file_writer_->Close()); | 
|  |  | 
|  | rtc::File out_file = rtc::File::Open(file_name_); | 
|  | VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFrames, | 
|  | use_capture_tims_ms); | 
|  | VerifyDummyTestFrames(&out_file, kNumFrames); | 
|  |  | 
|  | out_file.Close(); | 
|  | } | 
|  |  | 
|  | std::string file_name_; | 
|  | std::unique_ptr<IvfFileWriter> file_writer_; | 
|  | }; | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, WritesBasicVP8FileNtpTimestamp) { | 
|  | const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; | 
|  | RunBasicFileStructureTest(kVideoCodecVP8, fourcc, false); | 
|  | } | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, WritesBasicVP8FileMsTimestamp) { | 
|  | const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; | 
|  | RunBasicFileStructureTest(kVideoCodecVP8, fourcc, true); | 
|  | } | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, WritesBasicVP9FileNtpTimestamp) { | 
|  | const uint8_t fourcc[4] = {'V', 'P', '9', '0'}; | 
|  | RunBasicFileStructureTest(kVideoCodecVP9, fourcc, false); | 
|  | } | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, WritesBasicVP9FileMsTimestamp) { | 
|  | const uint8_t fourcc[4] = {'V', 'P', '9', '0'}; | 
|  | RunBasicFileStructureTest(kVideoCodecVP9, fourcc, true); | 
|  | } | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, WritesBasicH264FileNtpTimestamp) { | 
|  | const uint8_t fourcc[4] = {'H', '2', '6', '4'}; | 
|  | RunBasicFileStructureTest(kVideoCodecH264, fourcc, false); | 
|  | } | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, WritesBasicH264FileMsTimestamp) { | 
|  | const uint8_t fourcc[4] = {'H', '2', '6', '4'}; | 
|  | RunBasicFileStructureTest(kVideoCodecH264, fourcc, true); | 
|  | } | 
|  |  | 
|  | TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) { | 
|  | const uint8_t fourcc[4] = {'V', 'P', '8', '0'}; | 
|  | const int kWidth = 320; | 
|  | const int kHeight = 240; | 
|  | const int kNumFramesToWrite = 2; | 
|  | const int kNumFramesToFit = 1; | 
|  |  | 
|  | file_writer_ = IvfFileWriter::Wrap( | 
|  | rtc::File::Open(file_name_), | 
|  | kHeaderSize + | 
|  | kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); | 
|  | ASSERT_TRUE(file_writer_.get()); | 
|  |  | 
|  | ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, | 
|  | kNumFramesToWrite, true)); | 
|  | ASSERT_FALSE(file_writer_->Close()); | 
|  |  | 
|  | rtc::File out_file = rtc::File::Open(file_name_); | 
|  | VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true); | 
|  | VerifyDummyTestFrames(&out_file, kNumFramesToFit); | 
|  |  | 
|  | out_file.Close(); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |