blob: 8703a29c37da0ee47bcbad5abd3f19b94a609b2a [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'};
30constexpr uint8_t kH264Header[kCodecTypeBytesCount] = {'H', '2', '6', '4'};
31
32} // namespace
33
34std::unique_ptr<IvfFileReader> IvfFileReader::Create(FileWrapper file) {
35 auto reader =
36 std::unique_ptr<IvfFileReader>(new IvfFileReader(std::move(file)));
37 if (!reader->Reset()) {
38 return nullptr;
39 }
40 return reader;
41}
42IvfFileReader::~IvfFileReader() {
43 Close();
44}
45
46bool IvfFileReader::Reset() {
47 // Set error to true while initialization.
48 has_error_ = true;
49 if (!file_.Rewind()) {
50 RTC_LOG(LS_ERROR) << "Failed to rewind IVF file";
51 return false;
52 }
53
54 uint8_t ivf_header[kIvfHeaderSize] = {0};
55 size_t read = file_.Read(&ivf_header, kIvfHeaderSize);
56 if (read != kIvfHeaderSize) {
57 RTC_LOG(LS_ERROR) << "Failed to read IVF header";
58 return false;
59 }
60
61 if (memcmp(&ivf_header[0], kFileHeaderStart, 4) != 0) {
62 RTC_LOG(LS_ERROR) << "File is not in IVF format: DKIF header expected";
63 return false;
64 }
65
66 absl::optional<VideoCodecType> codec_type = ParseCodecType(ivf_header, 8);
67 if (!codec_type) {
68 return false;
69 }
70 codec_type_ = *codec_type;
71
72 width_ = ByteReader<uint16_t>::ReadLittleEndian(&ivf_header[12]);
73 height_ = ByteReader<uint16_t>::ReadLittleEndian(&ivf_header[14]);
74 if (width_ == 0 || height_ == 0) {
75 RTC_LOG(LS_ERROR) << "Invalid IVF header: width or height is 0";
76 return false;
77 }
78
79 uint32_t time_scale = ByteReader<uint32_t>::ReadLittleEndian(&ivf_header[16]);
80 if (time_scale == 1000) {
81 using_capture_timestamps_ = true;
82 } else if (time_scale == 90000) {
83 using_capture_timestamps_ = false;
84 } else {
85 RTC_LOG(LS_ERROR) << "Invalid IVF header: Unknown time scale";
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(INFO) << "Opened IVF file with codec data of type " << codec_name
107 << " at resolution " << width_ << " x " << height_ << ", using "
108 << (using_capture_timestamps_ ? "1" : "90")
109 << "kHz clock resolution.";
110
111 return true;
112}
113
114absl::optional<EncodedImage> IvfFileReader::NextFrame() {
115 if (has_error_ || !HasMoreFrames()) {
116 return absl::nullopt;
117 }
118
119 rtc::scoped_refptr<EncodedImageBuffer> payload = EncodedImageBuffer::Create();
120 std::vector<size_t> layer_sizes;
121 // next_frame_header_ have to be presented by the way how it was loaded. If it
122 // is missing it means there is a bug in error handling.
123 RTC_DCHECK(next_frame_header_);
124 int64_t current_timestamp = next_frame_header_->timestamp;
125 while (next_frame_header_ &&
126 current_timestamp == next_frame_header_->timestamp) {
127 // Resize payload to fit next spatial layer.
128 size_t current_layer_size = next_frame_header_->frame_size;
129 size_t current_layer_start_pos = payload->size();
130 payload->Realloc(payload->size() + current_layer_size);
131 layer_sizes.push_back(current_layer_size);
132
133 // Read next layer into payload
134 size_t read = file_.Read(&payload->data()[current_layer_start_pos],
135 current_layer_size);
136 if (read != current_layer_size) {
137 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
138 << ": failed to read frame payload";
139 has_error_ = true;
140 return absl::nullopt;
141 }
142 num_read_frames_++;
143
144 current_timestamp = next_frame_header_->timestamp;
145 next_frame_header_ = ReadNextFrameHeader();
146 }
147 if (!next_frame_header_) {
148 // If EOF was reached, we need to check that all frames were met.
149 if (!has_error_ && num_read_frames_ != num_frames_) {
150 RTC_LOG(LS_ERROR) << "Unexpected EOF";
151 has_error_ = true;
152 return absl::nullopt;
153 }
154 }
155
156 EncodedImage image;
157 if (using_capture_timestamps_) {
158 image.capture_time_ms_ = current_timestamp;
159 image.SetTimestamp(static_cast<uint32_t>(90 * current_timestamp));
160 } else {
161 image.SetTimestamp(static_cast<uint32_t>(current_timestamp));
162 }
163 image.SetEncodedData(payload);
164 image.SetSpatialIndex(static_cast<int>(layer_sizes.size()));
165 for (size_t i = 0; i < layer_sizes.size(); ++i) {
166 image.SetSpatialLayerFrameSize(static_cast<int>(i), layer_sizes[i]);
167 }
168
169 return image;
170}
171
172bool IvfFileReader::Close() {
173 if (!file_.is_open())
174 return false;
175
176 file_.Close();
177 return true;
178}
179
180absl::optional<VideoCodecType> IvfFileReader::ParseCodecType(uint8_t* buffer,
181 size_t start_pos) {
182 if (memcmp(&buffer[start_pos], kVp8Header, kCodecTypeBytesCount) == 0) {
183 return VideoCodecType::kVideoCodecVP8;
184 }
185 if (memcmp(&buffer[start_pos], kVp9Header, kCodecTypeBytesCount) == 0) {
186 return VideoCodecType::kVideoCodecVP9;
187 }
188 if (memcmp(&buffer[start_pos], kH264Header, kCodecTypeBytesCount) == 0) {
189 return VideoCodecType::kVideoCodecH264;
190 }
191 has_error_ = true;
192 RTC_LOG(LS_ERROR) << "Unknown codec type: "
193 << std::string(
194 reinterpret_cast<char const*>(&buffer[start_pos]),
195 kCodecTypeBytesCount);
196 return absl::nullopt;
197}
198
199absl::optional<IvfFileReader::FrameHeader>
200IvfFileReader::ReadNextFrameHeader() {
201 uint8_t ivf_frame_header[kIvfFrameHeaderSize] = {0};
202 size_t read = file_.Read(&ivf_frame_header, kIvfFrameHeaderSize);
203 if (read != kIvfFrameHeaderSize) {
204 if (read != 0 || !file_.ReadEof()) {
205 has_error_ = true;
206 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
207 << ": failed to read IVF frame header";
208 }
209 return absl::nullopt;
210 }
211 FrameHeader header;
212 header.frame_size = static_cast<size_t>(
213 ByteReader<uint32_t>::ReadLittleEndian(&ivf_frame_header[0]));
214 header.timestamp =
215 ByteReader<uint64_t>::ReadLittleEndian(&ivf_frame_header[4]);
216
217 if (header.frame_size == 0) {
218 has_error_ = true;
219 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
220 << ": invalid frame size";
221 return absl::nullopt;
222 }
223
224 if (header.timestamp < 0) {
225 has_error_ = true;
226 RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
227 << ": negative timestamp";
228 return absl::nullopt;
229 }
230
231 return header;
232}
233
234} // namespace webrtc