1 /*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/video_coding/utility/ivf_file_writer.h"
12
13 #include <string.h>
14
15 #include <memory>
16 #include <string>
17
18 #include "modules/rtp_rtcp/source/byte_io.h"
19 #include "test/gtest.h"
20 #include "test/testsupport/file_utils.h"
21
22 namespace webrtc {
23
24 namespace {
25 static const int kHeaderSize = 32;
26 static const int kFrameHeaderSize = 12;
27 static uint8_t dummy_payload[4] = {0, 1, 2, 3};
28 } // namespace
29
30 class IvfFileWriterTest : public ::testing::Test {
31 protected:
SetUp()32 void SetUp() override {
33 file_name_ =
34 webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file");
35 }
TearDown()36 void TearDown() override { webrtc::test::RemoveFile(file_name_); }
37
WriteDummyTestFrames(VideoCodecType codec_type,int width,int height,int num_frames,bool use_capture_tims_ms)38 bool WriteDummyTestFrames(VideoCodecType codec_type,
39 int width,
40 int height,
41 int num_frames,
42 bool use_capture_tims_ms) {
43 EncodedImage frame;
44 frame.SetEncodedData(
45 EncodedImageBuffer::Create(dummy_payload, sizeof(dummy_payload)));
46 frame._encodedWidth = width;
47 frame._encodedHeight = height;
48 for (int i = 1; i <= num_frames; ++i) {
49 frame.set_size(i % sizeof(dummy_payload));
50 if (use_capture_tims_ms) {
51 frame.capture_time_ms_ = i;
52 } else {
53 frame.SetTimestamp(i);
54 }
55 if (!file_writer_->WriteFrame(frame, codec_type))
56 return false;
57 }
58 return true;
59 }
60
VerifyIvfHeader(FileWrapper * file,const uint8_t fourcc[4],int width,int height,uint32_t num_frames,bool use_capture_tims_ms)61 void VerifyIvfHeader(FileWrapper* file,
62 const uint8_t fourcc[4],
63 int width,
64 int height,
65 uint32_t num_frames,
66 bool use_capture_tims_ms) {
67 ASSERT_TRUE(file->is_open());
68 uint8_t data[kHeaderSize];
69 ASSERT_EQ(static_cast<size_t>(kHeaderSize), file->Read(data, kHeaderSize));
70
71 uint8_t dkif[4] = {'D', 'K', 'I', 'F'};
72 EXPECT_EQ(0, memcmp(dkif, data, 4));
73 EXPECT_EQ(0u, ByteReader<uint16_t>::ReadLittleEndian(&data[4]));
74 EXPECT_EQ(32u, ByteReader<uint16_t>::ReadLittleEndian(&data[6]));
75 EXPECT_EQ(0, memcmp(fourcc, &data[8], 4));
76 EXPECT_EQ(width, ByteReader<uint16_t>::ReadLittleEndian(&data[12]));
77 EXPECT_EQ(height, ByteReader<uint16_t>::ReadLittleEndian(&data[14]));
78 EXPECT_EQ(use_capture_tims_ms ? 1000u : 90000u,
79 ByteReader<uint32_t>::ReadLittleEndian(&data[16]));
80 EXPECT_EQ(1u, ByteReader<uint32_t>::ReadLittleEndian(&data[20]));
81 EXPECT_EQ(num_frames, ByteReader<uint32_t>::ReadLittleEndian(&data[24]));
82 EXPECT_EQ(0u, ByteReader<uint32_t>::ReadLittleEndian(&data[28]));
83 }
84
VerifyDummyTestFrames(FileWrapper * file,uint32_t num_frames)85 void VerifyDummyTestFrames(FileWrapper* file, uint32_t num_frames) {
86 const int kMaxFrameSize = 4;
87 for (uint32_t i = 1; i <= num_frames; ++i) {
88 uint8_t frame_header[kFrameHeaderSize];
89 ASSERT_EQ(static_cast<unsigned int>(kFrameHeaderSize),
90 file->Read(frame_header, kFrameHeaderSize));
91 uint32_t frame_length =
92 ByteReader<uint32_t>::ReadLittleEndian(&frame_header[0]);
93 EXPECT_EQ(i % 4, frame_length);
94 uint64_t timestamp =
95 ByteReader<uint64_t>::ReadLittleEndian(&frame_header[4]);
96 EXPECT_EQ(i, timestamp);
97
98 uint8_t data[kMaxFrameSize] = {};
99 ASSERT_EQ(frame_length,
100 static_cast<uint32_t>(file->Read(data, frame_length)));
101 EXPECT_EQ(0, memcmp(data, dummy_payload, frame_length));
102 }
103 }
104
RunBasicFileStructureTest(VideoCodecType codec_type,const uint8_t fourcc[4],bool use_capture_tims_ms)105 void RunBasicFileStructureTest(VideoCodecType codec_type,
106 const uint8_t fourcc[4],
107 bool use_capture_tims_ms) {
108 file_writer_ =
109 IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(file_name_), 0);
110 ASSERT_TRUE(file_writer_.get());
111 const int kWidth = 320;
112 const int kHeight = 240;
113 const int kNumFrames = 257;
114 ASSERT_TRUE(WriteDummyTestFrames(codec_type, kWidth, kHeight, kNumFrames,
115 use_capture_tims_ms));
116 EXPECT_TRUE(file_writer_->Close());
117
118 FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
119 VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFrames,
120 use_capture_tims_ms);
121 VerifyDummyTestFrames(&out_file, kNumFrames);
122
123 out_file.Close();
124 }
125
126 std::string file_name_;
127 std::unique_ptr<IvfFileWriter> file_writer_;
128 };
129
TEST_F(IvfFileWriterTest,WritesBasicVP8FileNtpTimestamp)130 TEST_F(IvfFileWriterTest, WritesBasicVP8FileNtpTimestamp) {
131 const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
132 RunBasicFileStructureTest(kVideoCodecVP8, fourcc, false);
133 }
134
TEST_F(IvfFileWriterTest,WritesBasicVP8FileMsTimestamp)135 TEST_F(IvfFileWriterTest, WritesBasicVP8FileMsTimestamp) {
136 const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
137 RunBasicFileStructureTest(kVideoCodecVP8, fourcc, true);
138 }
139
TEST_F(IvfFileWriterTest,WritesBasicVP9FileNtpTimestamp)140 TEST_F(IvfFileWriterTest, WritesBasicVP9FileNtpTimestamp) {
141 const uint8_t fourcc[4] = {'V', 'P', '9', '0'};
142 RunBasicFileStructureTest(kVideoCodecVP9, fourcc, false);
143 }
144
TEST_F(IvfFileWriterTest,WritesBasicVP9FileMsTimestamp)145 TEST_F(IvfFileWriterTest, WritesBasicVP9FileMsTimestamp) {
146 const uint8_t fourcc[4] = {'V', 'P', '9', '0'};
147 RunBasicFileStructureTest(kVideoCodecVP9, fourcc, true);
148 }
149
TEST_F(IvfFileWriterTest,WritesBasicH264FileNtpTimestamp)150 TEST_F(IvfFileWriterTest, WritesBasicH264FileNtpTimestamp) {
151 const uint8_t fourcc[4] = {'H', '2', '6', '4'};
152 RunBasicFileStructureTest(kVideoCodecH264, fourcc, false);
153 }
154
TEST_F(IvfFileWriterTest,WritesBasicH264FileMsTimestamp)155 TEST_F(IvfFileWriterTest, WritesBasicH264FileMsTimestamp) {
156 const uint8_t fourcc[4] = {'H', '2', '6', '4'};
157 RunBasicFileStructureTest(kVideoCodecH264, fourcc, true);
158 }
159
TEST_F(IvfFileWriterTest,ClosesWhenReachesLimit)160 TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) {
161 const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
162 const int kWidth = 320;
163 const int kHeight = 240;
164 const int kNumFramesToWrite = 2;
165 const int kNumFramesToFit = 1;
166
167 file_writer_ = IvfFileWriter::Wrap(
168 FileWrapper::OpenWriteOnly(file_name_),
169 kHeaderSize +
170 kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
171 ASSERT_TRUE(file_writer_.get());
172
173 ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
174 kNumFramesToWrite, true));
175 ASSERT_FALSE(file_writer_->Close());
176
177 FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
178 VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true);
179 VerifyDummyTestFrames(&out_file, kNumFramesToFit);
180
181 out_file.Close();
182 }
183
184 } // namespace webrtc
185