1 /*
2 * Copyright (c) 2019 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_reader.h"
12
13 #include <string>
14 #include <vector>
15
16 #include "api/video_codecs/video_codec.h"
17 #include "modules/rtp_rtcp/source/byte_io.h"
18 #include "modules/video_coding/utility/ivf_defines.h"
19 #include "rtc_base/logging.h"
20
21 namespace webrtc {
22 namespace {
23
24 constexpr size_t kIvfFrameHeaderSize = 12;
25 constexpr int kCodecTypeBytesCount = 4;
26
27 constexpr uint8_t kFileHeaderStart[kCodecTypeBytesCount] = {'D', 'K', 'I', 'F'};
28 constexpr uint8_t kVp8Header[kCodecTypeBytesCount] = {'V', 'P', '8', '0'};
29 constexpr uint8_t kVp9Header[kCodecTypeBytesCount] = {'V', 'P', '9', '0'};
30 constexpr uint8_t kAv1Header[kCodecTypeBytesCount] = {'A', 'V', '0', '1'};
31 constexpr uint8_t kH264Header[kCodecTypeBytesCount] = {'H', '2', '6', '4'};
32
33 // RTP standard required 90kHz clock rate.
34 constexpr int32_t kRtpClockRateHz = 90000;
35
36 } // namespace
37
Create(FileWrapper file)38 std::unique_ptr<IvfFileReader> IvfFileReader::Create(FileWrapper file) {
39 auto reader =
40 std::unique_ptr<IvfFileReader>(new IvfFileReader(std::move(file)));
41 if (!reader->Reset()) {
42 return nullptr;
43 }
44 return reader;
45 }
~IvfFileReader()46 IvfFileReader::~IvfFileReader() {
47 Close();
48 }
49
Reset()50 bool IvfFileReader::Reset() {
51 // Set error to true while initialization.
52 has_error_ = true;
53 if (!file_.Rewind()) {
54 RTC_LOG(LS_ERROR) << "Failed to rewind IVF file";
55 return false;
56 }
57
58 uint8_t ivf_header[kIvfHeaderSize] = {0};
59 size_t read = file_.Read(&ivf_header, kIvfHeaderSize);
60 if (read != kIvfHeaderSize) {
61 RTC_LOG(LS_ERROR) << "Failed to read IVF header";
62 return false;
63 }
64
65 if (memcmp(&ivf_header[0], kFileHeaderStart, 4) != 0) {
66 RTC_LOG(LS_ERROR) << "File is not in IVF format: DKIF header expected";
67 return false;
68 }
69
70 absl::optional<VideoCodecType> codec_type = ParseCodecType(ivf_header, 8);
71 if (!codec_type) {
72 return false;
73 }
74 codec_type_ = *codec_type;
75
76 width_ = ByteReader<uint16_t>::ReadLittleEndian(&ivf_header[12]);
77 height_ = ByteReader<uint16_t>::ReadLittleEndian(&ivf_header[14]);
78 if (width_ == 0 || height_ == 0) {
79 RTC_LOG(LS_ERROR) << "Invalid IVF header: width or height is 0";
80 return false;
81 }
82
83 time_scale_ = ByteReader<uint32_t>::ReadLittleEndian(&ivf_header[16]);
84 if (time_scale_ == 0) {
85 RTC_LOG(LS_ERROR) << "Invalid IVF header: time scale can't be 0";
86 return false;
87 }
88
89 num_frames_ = static_cast<size_t>(
90 ByteReader<uint32_t>::ReadLittleEndian(&ivf_header[24]));
91 if (num_frames_ <= 0) {
92 RTC_LOG(LS_ERROR) << "Invalid IVF header: number of frames 0 or negative";
93 return false;
94 }
95
96 num_read_frames_ = 0;
97 next_frame_header_ = ReadNextFrameHeader();
98 if (!next_frame_header_) {
99 RTC_LOG(LS_ERROR) << "Failed to read 1st frame header";
100 return false;
101 }
102 // Initialization succeed: reset error.
103 has_error_ = false;
104
105 const char* codec_name = CodecTypeToPayloadString(codec_type_);
106 RTC_LOG(LS_INFO) << "Opened IVF file with codec data of type " << codec_name
107 << " at resolution " << width_ << " x " << height_
108 << ", using " << time_scale_ << "Hz clock resolution.";
109
110 return true;
111 }
112
NextFrame()113 absl::optional<EncodedImage> IvfFileReader::NextFrame() {
114 if (has_error_ || !HasMoreFrames()) {
115 return absl::nullopt;
116 }
117
118 rtc::scoped_refptr<EncodedImageBuffer> payload = EncodedImageBuffer::Create();
119 std::vector<size_t> layer_sizes;
120 // next_frame_header_ have to be presented by the way how it was loaded. If it
121 // is missing it means there is a bug in error handling.
122 RTC_DCHECK(next_frame_header_);
123 int64_t current_timestamp = next_frame_header_->timestamp;
124 // The first frame from the file should be marked as Key frame.
125 bool is_first_frame = num_read_frames_ == 0;
126 while (next_frame_header_ &&
127 current_timestamp == next_frame_header_->timestamp) {
128 // Resize payload to fit next spatial layer.
129 size_t current_layer_size = next_frame_header_->frame_size;
130 size_t current_layer_start_pos = payload->size();
131 payload->Realloc(payload->size() + current_layer_size);
132 layer_sizes.push_back(current_layer_size);
133
134 // Read next layer into payload
135 size_t read = file_.Read(&payload->data()[current_layer_start_pos],
136 current_layer_size);
137 if (read != current_layer_size) {
138 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
139 << ": failed to read frame payload";
140 has_error_ = true;
141 return absl::nullopt;
142 }
143 num_read_frames_++;
144
145 current_timestamp = next_frame_header_->timestamp;
146 next_frame_header_ = ReadNextFrameHeader();
147 }
148 if (!next_frame_header_) {
149 // If EOF was reached, we need to check that all frames were met.
150 if (!has_error_ && num_read_frames_ != num_frames_) {
151 RTC_LOG(LS_ERROR) << "Unexpected EOF";
152 has_error_ = true;
153 return absl::nullopt;
154 }
155 }
156
157 EncodedImage image;
158 image.capture_time_ms_ = current_timestamp;
159 image.SetTimestamp(
160 static_cast<uint32_t>(current_timestamp * kRtpClockRateHz / time_scale_));
161 image.SetEncodedData(payload);
162 image.SetSpatialIndex(static_cast<int>(layer_sizes.size()) - 1);
163 for (size_t i = 0; i < layer_sizes.size(); ++i) {
164 image.SetSpatialLayerFrameSize(static_cast<int>(i), layer_sizes[i]);
165 }
166 if (is_first_frame) {
167 image._frameType = VideoFrameType::kVideoFrameKey;
168 }
169
170 return image;
171 }
172
Close()173 bool IvfFileReader::Close() {
174 if (!file_.is_open())
175 return false;
176
177 file_.Close();
178 return true;
179 }
180
ParseCodecType(uint8_t * buffer,size_t start_pos)181 absl::optional<VideoCodecType> IvfFileReader::ParseCodecType(uint8_t* buffer,
182 size_t start_pos) {
183 if (memcmp(&buffer[start_pos], kVp8Header, kCodecTypeBytesCount) == 0) {
184 return VideoCodecType::kVideoCodecVP8;
185 }
186 if (memcmp(&buffer[start_pos], kVp9Header, kCodecTypeBytesCount) == 0) {
187 return VideoCodecType::kVideoCodecVP9;
188 }
189 if (memcmp(&buffer[start_pos], kAv1Header, kCodecTypeBytesCount) == 0) {
190 return VideoCodecType::kVideoCodecAV1;
191 }
192 if (memcmp(&buffer[start_pos], kH264Header, kCodecTypeBytesCount) == 0) {
193 return VideoCodecType::kVideoCodecH264;
194 }
195 has_error_ = true;
196 RTC_LOG(LS_ERROR) << "Unknown codec type: "
197 << std::string(
198 reinterpret_cast<char const*>(&buffer[start_pos]),
199 kCodecTypeBytesCount);
200 return absl::nullopt;
201 }
202
203 absl::optional<IvfFileReader::FrameHeader>
ReadNextFrameHeader()204 IvfFileReader::ReadNextFrameHeader() {
205 uint8_t ivf_frame_header[kIvfFrameHeaderSize] = {0};
206 size_t read = file_.Read(&ivf_frame_header, kIvfFrameHeaderSize);
207 if (read != kIvfFrameHeaderSize) {
208 if (read != 0 || !file_.ReadEof()) {
209 has_error_ = true;
210 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
211 << ": failed to read IVF frame header";
212 }
213 return absl::nullopt;
214 }
215 FrameHeader header;
216 header.frame_size = static_cast<size_t>(
217 ByteReader<uint32_t>::ReadLittleEndian(&ivf_frame_header[0]));
218 header.timestamp =
219 ByteReader<uint64_t>::ReadLittleEndian(&ivf_frame_header[4]);
220
221 if (header.frame_size == 0) {
222 has_error_ = true;
223 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
224 << ": invalid frame size";
225 return absl::nullopt;
226 }
227
228 if (header.timestamp < 0) {
229 has_error_ = true;
230 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
231 << ": negative timestamp";
232 return absl::nullopt;
233 }
234
235 return header;
236 }
237
238 } // namespace webrtc
239