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,WritesBasicAv1FileNtpTimestamp)150 TEST_F(IvfFileWriterTest, WritesBasicAv1FileNtpTimestamp) {
151   const uint8_t fourcc[4] = {'A', 'V', '0', '1'};
152   RunBasicFileStructureTest(kVideoCodecAV1, fourcc, false);
153 }
154 
TEST_F(IvfFileWriterTest,WritesBasicAv1FileMsTimestamp)155 TEST_F(IvfFileWriterTest, WritesBasicAv1FileMsTimestamp) {
156   const uint8_t fourcc[4] = {'A', 'V', '0', '1'};
157   RunBasicFileStructureTest(kVideoCodecAV1, fourcc, true);
158 }
159 
TEST_F(IvfFileWriterTest,WritesBasicH264FileNtpTimestamp)160 TEST_F(IvfFileWriterTest, WritesBasicH264FileNtpTimestamp) {
161   const uint8_t fourcc[4] = {'H', '2', '6', '4'};
162   RunBasicFileStructureTest(kVideoCodecH264, fourcc, false);
163 }
164 
TEST_F(IvfFileWriterTest,WritesBasicH264FileMsTimestamp)165 TEST_F(IvfFileWriterTest, WritesBasicH264FileMsTimestamp) {
166   const uint8_t fourcc[4] = {'H', '2', '6', '4'};
167   RunBasicFileStructureTest(kVideoCodecH264, fourcc, true);
168 }
169 
TEST_F(IvfFileWriterTest,ClosesWhenReachesLimit)170 TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) {
171   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
172   const int kWidth = 320;
173   const int kHeight = 240;
174   const int kNumFramesToWrite = 2;
175   const int kNumFramesToFit = 1;
176 
177   file_writer_ = IvfFileWriter::Wrap(
178       FileWrapper::OpenWriteOnly(file_name_),
179       kHeaderSize +
180           kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
181   ASSERT_TRUE(file_writer_.get());
182 
183   ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
184                                     kNumFramesToWrite, true));
185   ASSERT_FALSE(file_writer_->Close());
186 
187   FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
188   VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true);
189   VerifyDummyTestFrames(&out_file, kNumFramesToFit);
190 
191   out_file.Close();
192 }
193 
194 }  // namespace webrtc
195