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