1 // Copyright 2019 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "examples/file_reader.h"
16
17 #include <algorithm>
18 #include <cstdint>
19 #include <cstdio>
20 #include <new>
21 #include <string>
22 #include <vector>
23
24 #if defined(_WIN32)
25 #include <fcntl.h>
26 #include <io.h>
27 #endif
28
29 #include "examples/file_reader_constants.h"
30 #include "examples/file_reader_factory.h"
31 #include "examples/file_reader_interface.h"
32 #include "examples/ivf_parser.h"
33 #include "examples/logging.h"
34
35 namespace libgav1 {
36 namespace {
37
SetBinaryMode(FILE * stream)38 FILE* SetBinaryMode(FILE* stream) {
39 #if defined(_WIN32)
40 _setmode(_fileno(stream), _O_BINARY);
41 #endif
42 return stream;
43 }
44
45 } // namespace
46
47 bool FileReader::registered_in_factory_ =
48 FileReaderFactory::RegisterReader(FileReader::Open);
49
~FileReader()50 FileReader::~FileReader() {
51 if (owns_file_) fclose(file_);
52 }
53
Open(const std::string & file_name,const bool error_tolerant)54 std::unique_ptr<FileReaderInterface> FileReader::Open(
55 const std::string& file_name, const bool error_tolerant) {
56 if (file_name.empty()) return nullptr;
57
58 FILE* raw_file_ptr;
59
60 bool owns_file = true;
61 if (file_name == "-") {
62 raw_file_ptr = SetBinaryMode(stdin);
63 owns_file = false; // stdin is owned by the Standard C Library.
64 } else {
65 raw_file_ptr = fopen(file_name.c_str(), "rb");
66 }
67
68 if (raw_file_ptr == nullptr) {
69 return nullptr;
70 }
71
72 std::unique_ptr<FileReader> file(
73 new (std::nothrow) FileReader(raw_file_ptr, owns_file, error_tolerant));
74 if (file == nullptr) {
75 LIBGAV1_EXAMPLES_LOG_ERROR("Out of memory");
76 if (owns_file) fclose(raw_file_ptr);
77 return nullptr;
78 }
79
80 if (!file->ReadIvfFileHeader()) {
81 LIBGAV1_EXAMPLES_LOG_ERROR("Unsupported file type");
82 return nullptr;
83 }
84
85 return file;
86 }
87
88 // IVF Frame Header format, from https://wiki.multimedia.cx/index.php/IVF
89 // bytes 0-3 size of frame in bytes (not including the 12-byte header)
90 // bytes 4-11 64-bit presentation timestamp
91 // bytes 12.. frame data
ReadTemporalUnit(std::vector<uint8_t> * const tu_data,int64_t * const timestamp)92 bool FileReader::ReadTemporalUnit(std::vector<uint8_t>* const tu_data,
93 int64_t* const timestamp) {
94 if (tu_data == nullptr) return false;
95 tu_data->clear();
96
97 uint8_t header_buffer[kIvfFrameHeaderSize];
98 const size_t num_read = fread(header_buffer, 1, kIvfFrameHeaderSize, file_);
99
100 if (IsEndOfFile()) {
101 if (num_read != 0) {
102 LIBGAV1_EXAMPLES_LOG_ERROR(
103 "Cannot read IVF frame header: Not enough data available");
104 return false;
105 }
106
107 return true;
108 }
109
110 IvfFrameHeader ivf_frame_header;
111 if (!ParseIvfFrameHeader(header_buffer, &ivf_frame_header)) {
112 LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF frame header");
113 if (error_tolerant_) {
114 ivf_frame_header.frame_size =
115 std::min(ivf_frame_header.frame_size, size_t{kMaxTemporalUnitSize});
116 } else {
117 return false;
118 }
119 }
120
121 if (timestamp != nullptr) *timestamp = ivf_frame_header.timestamp;
122
123 tu_data->resize(ivf_frame_header.frame_size);
124 const size_t size_read =
125 fread(tu_data->data(), 1, ivf_frame_header.frame_size, file_);
126 if (size_read != ivf_frame_header.frame_size) {
127 LIBGAV1_EXAMPLES_LOG_ERROR(
128 "Unexpected EOF or I/O error reading frame data");
129 if (error_tolerant_) {
130 tu_data->resize(size_read);
131 } else {
132 return false;
133 }
134 }
135 return true;
136 }
137
138 // Attempt to read an IVF file header. Returns true for success, and false for
139 // failure.
140 //
141 // IVF File Header format, from https://wiki.multimedia.cx/index.php/IVF
142 // bytes 0-3 signature: 'DKIF'
143 // bytes 4-5 version (should be 0)
144 // bytes 6-7 length of header in bytes
145 // bytes 8-11 codec FourCC (e.g., 'VP80')
146 // bytes 12-13 width in pixels
147 // bytes 14-15 height in pixels
148 // bytes 16-19 frame rate
149 // bytes 20-23 time scale
150 // bytes 24-27 number of frames in file
151 // bytes 28-31 unused
152 //
153 // Note: The rate and scale fields correspond to the numerator and denominator
154 // of frame rate (fps) or time base (the reciprocal of frame rate) as follows:
155 //
156 // bytes 16-19 frame rate timebase.den framerate.numerator
157 // bytes 20-23 time scale timebase.num framerate.denominator
ReadIvfFileHeader()158 bool FileReader::ReadIvfFileHeader() {
159 uint8_t header_buffer[kIvfFileHeaderSize];
160 const size_t num_read = fread(header_buffer, 1, kIvfFileHeaderSize, file_);
161 if (num_read != kIvfFileHeaderSize) {
162 LIBGAV1_EXAMPLES_LOG_ERROR(
163 "Cannot read IVF header: Not enough data available");
164 return false;
165 }
166
167 IvfFileHeader ivf_file_header;
168 if (!ParseIvfFileHeader(header_buffer, &ivf_file_header)) {
169 LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF file header");
170 if (error_tolerant_) {
171 ivf_file_header = {};
172 } else {
173 return false;
174 }
175 }
176
177 width_ = ivf_file_header.width;
178 height_ = ivf_file_header.height;
179 frame_rate_ = ivf_file_header.frame_rate_numerator;
180 time_scale_ = ivf_file_header.frame_rate_denominator;
181 type_ = kFileTypeIvf;
182
183 return true;
184 }
185
186 } // namespace libgav1
187