• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "rtc_base/logging.h"
19 
20 namespace webrtc {
21 namespace {
22 
23 constexpr size_t kIvfHeaderSize = 32;
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 kH264Header[kCodecTypeBytesCount] = {'H', '2', '6', '4'};
31 
32 }  // namespace
33 
Create(FileWrapper file)34 std::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 }
~IvfFileReader()42 IvfFileReader::~IvfFileReader() {
43   Close();
44 }
45 
Reset()46 bool 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 
NextFrame()114 absl::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   // The first frame from the file should be marked as Key frame.
126   bool is_first_frame = num_read_frames_ == 0;
127   while (next_frame_header_ &&
128          current_timestamp == next_frame_header_->timestamp) {
129     // Resize payload to fit next spatial layer.
130     size_t current_layer_size = next_frame_header_->frame_size;
131     size_t current_layer_start_pos = payload->size();
132     payload->Realloc(payload->size() + current_layer_size);
133     layer_sizes.push_back(current_layer_size);
134 
135     // Read next layer into payload
136     size_t read = file_.Read(&payload->data()[current_layer_start_pos],
137                              current_layer_size);
138     if (read != current_layer_size) {
139       RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
140                         << ": failed to read frame payload";
141       has_error_ = true;
142       return absl::nullopt;
143     }
144     num_read_frames_++;
145 
146     current_timestamp = next_frame_header_->timestamp;
147     next_frame_header_ = ReadNextFrameHeader();
148   }
149   if (!next_frame_header_) {
150     // If EOF was reached, we need to check that all frames were met.
151     if (!has_error_ && num_read_frames_ != num_frames_) {
152       RTC_LOG(LS_ERROR) << "Unexpected EOF";
153       has_error_ = true;
154       return absl::nullopt;
155     }
156   }
157 
158   EncodedImage image;
159   if (using_capture_timestamps_) {
160     image.capture_time_ms_ = current_timestamp;
161     image.SetTimestamp(static_cast<uint32_t>(90 * current_timestamp));
162   } else {
163     image.SetTimestamp(static_cast<uint32_t>(current_timestamp));
164   }
165   image.SetEncodedData(payload);
166   image.SetSpatialIndex(static_cast<int>(layer_sizes.size()));
167   for (size_t i = 0; i < layer_sizes.size(); ++i) {
168     image.SetSpatialLayerFrameSize(static_cast<int>(i), layer_sizes[i]);
169   }
170   if (is_first_frame) {
171     image._frameType = VideoFrameType::kVideoFrameKey;
172   }
173   image._completeFrame = true;
174 
175   return image;
176 }
177 
Close()178 bool IvfFileReader::Close() {
179   if (!file_.is_open())
180     return false;
181 
182   file_.Close();
183   return true;
184 }
185 
ParseCodecType(uint8_t * buffer,size_t start_pos)186 absl::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   }
194   if (memcmp(&buffer[start_pos], kH264Header, kCodecTypeBytesCount) == 0) {
195     return VideoCodecType::kVideoCodecH264;
196   }
197   has_error_ = true;
198   RTC_LOG(LS_ERROR) << "Unknown codec type: "
199                     << std::string(
200                            reinterpret_cast<char const*>(&buffer[start_pos]),
201                            kCodecTypeBytesCount);
202   return absl::nullopt;
203 }
204 
205 absl::optional<IvfFileReader::FrameHeader>
ReadNextFrameHeader()206 IvfFileReader::ReadNextFrameHeader() {
207   uint8_t ivf_frame_header[kIvfFrameHeaderSize] = {0};
208   size_t read = file_.Read(&ivf_frame_header, kIvfFrameHeaderSize);
209   if (read != kIvfFrameHeaderSize) {
210     if (read != 0 || !file_.ReadEof()) {
211       has_error_ = true;
212       RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
213                         << ": failed to read IVF frame header";
214     }
215     return absl::nullopt;
216   }
217   FrameHeader header;
218   header.frame_size = static_cast<size_t>(
219       ByteReader<uint32_t>::ReadLittleEndian(&ivf_frame_header[0]));
220   header.timestamp =
221       ByteReader<uint64_t>::ReadLittleEndian(&ivf_frame_header[4]);
222 
223   if (header.frame_size == 0) {
224     has_error_ = true;
225     RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
226                       << ": invalid frame size";
227     return absl::nullopt;
228   }
229 
230   if (header.timestamp < 0) {
231     has_error_ = true;
232     RTC_LOG(LS_ERROR) << "Frame #" << num_read_frames_
233                       << ": negative timestamp";
234     return absl::nullopt;
235   }
236 
237   return header;
238 }
239 
240 }  // namespace webrtc
241