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 <memory>
21 #include <new>
22 #include <string>
23 #include <vector>
24
25 #if defined(_WIN32)
26 #include <fcntl.h>
27 #include <io.h>
28 #endif
29
30 #include "examples/file_reader_constants.h"
31 #include "examples/file_reader_factory.h"
32 #include "examples/file_reader_interface.h"
33 #include "examples/ivf_parser.h"
34 #include "examples/logging.h"
35
36 namespace libgav1 {
37 namespace {
38
SetBinaryMode(FILE * stream)39 FILE* SetBinaryMode(FILE* stream) {
40 #if defined(_WIN32)
41 _setmode(_fileno(stream), _O_BINARY);
42 #endif
43 return stream;
44 }
45
46 } // namespace
47
48 bool FileReader::registered_in_factory_ =
49 FileReaderFactory::RegisterReader(FileReader::Open);
50
~FileReader()51 FileReader::~FileReader() {
52 if (owns_file_) fclose(file_);
53 }
54
Open(const std::string & file_name,const bool error_tolerant)55 std::unique_ptr<FileReaderInterface> FileReader::Open(
56 const std::string& file_name, const bool error_tolerant) {
57 if (file_name.empty()) return nullptr;
58
59 FILE* raw_file_ptr;
60
61 bool owns_file = true;
62 if (file_name == "-") {
63 raw_file_ptr = SetBinaryMode(stdin);
64 owns_file = false; // stdin is owned by the Standard C Library.
65 } else {
66 raw_file_ptr = fopen(file_name.c_str(), "rb");
67 }
68
69 if (raw_file_ptr == nullptr) {
70 return nullptr;
71 }
72
73 std::unique_ptr<FileReader> file(
74 new (std::nothrow) FileReader(raw_file_ptr, owns_file, error_tolerant));
75 if (file == nullptr) {
76 LIBGAV1_EXAMPLES_LOG_ERROR("Out of memory");
77 if (owns_file) fclose(raw_file_ptr);
78 return nullptr;
79 }
80
81 if (!file->ReadIvfFileHeader()) {
82 LIBGAV1_EXAMPLES_LOG_ERROR("Unsupported file type");
83 return nullptr;
84 }
85
86 // With C++11, to return |file|, an explicit move is required as the return
87 // type differs from the local variable. Overload resolution isn't guaranteed
88 // in this case, though some compilers may adopt the C++14 behavior (C++
89 // Standard Core Language Issue #1579, Return by converting move
90 // constructor):
91 // https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579
92 // To keep things simple we opt for the following compatible form.
93 return std::unique_ptr<FileReaderInterface>(file.release());
94 }
95
96 // IVF Frame Header format, from https://wiki.multimedia.cx/index.php/IVF
97 // bytes 0-3 size of frame in bytes (not including the 12-byte header)
98 // bytes 4-11 64-bit presentation timestamp
99 // bytes 12.. frame data
ReadTemporalUnit(std::vector<uint8_t> * const tu_data,int64_t * const timestamp)100 bool FileReader::ReadTemporalUnit(std::vector<uint8_t>* const tu_data,
101 int64_t* const timestamp) {
102 if (tu_data == nullptr) return false;
103 tu_data->clear();
104
105 uint8_t header_buffer[kIvfFrameHeaderSize];
106 const size_t num_read = fread(header_buffer, 1, kIvfFrameHeaderSize, file_);
107
108 if (IsEndOfFile()) {
109 if (num_read != 0) {
110 LIBGAV1_EXAMPLES_LOG_ERROR(
111 "Cannot read IVF frame header: Not enough data available");
112 return false;
113 }
114
115 return true;
116 }
117
118 IvfFrameHeader ivf_frame_header;
119 if (!ParseIvfFrameHeader(header_buffer, &ivf_frame_header)) {
120 LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF frame header");
121 if (error_tolerant_) {
122 ivf_frame_header.frame_size =
123 std::min(ivf_frame_header.frame_size, size_t{kMaxTemporalUnitSize});
124 } else {
125 return false;
126 }
127 }
128
129 if (timestamp != nullptr) *timestamp = ivf_frame_header.timestamp;
130
131 tu_data->resize(ivf_frame_header.frame_size);
132 const size_t size_read =
133 fread(tu_data->data(), 1, ivf_frame_header.frame_size, file_);
134 if (size_read != ivf_frame_header.frame_size) {
135 LIBGAV1_EXAMPLES_LOG_ERROR(
136 "Unexpected EOF or I/O error reading frame data");
137 if (error_tolerant_) {
138 tu_data->resize(size_read);
139 } else {
140 return false;
141 }
142 }
143 return true;
144 }
145
146 // Attempt to read an IVF file header. Returns true for success, and false for
147 // failure.
148 //
149 // IVF File Header format, from https://wiki.multimedia.cx/index.php/IVF
150 // bytes 0-3 signature: 'DKIF'
151 // bytes 4-5 version (should be 0)
152 // bytes 6-7 length of header in bytes
153 // bytes 8-11 codec FourCC (e.g., 'VP80')
154 // bytes 12-13 width in pixels
155 // bytes 14-15 height in pixels
156 // bytes 16-19 frame rate
157 // bytes 20-23 time scale
158 // bytes 24-27 number of frames in file
159 // bytes 28-31 unused
160 //
161 // Note: The rate and scale fields correspond to the numerator and denominator
162 // of frame rate (fps) or time base (the reciprocal of frame rate) as follows:
163 //
164 // bytes 16-19 frame rate timebase.den framerate.numerator
165 // bytes 20-23 time scale timebase.num framerate.denominator
ReadIvfFileHeader()166 bool FileReader::ReadIvfFileHeader() {
167 uint8_t header_buffer[kIvfFileHeaderSize];
168 const size_t num_read = fread(header_buffer, 1, kIvfFileHeaderSize, file_);
169 if (num_read != kIvfFileHeaderSize) {
170 LIBGAV1_EXAMPLES_LOG_ERROR(
171 "Cannot read IVF header: Not enough data available");
172 return false;
173 }
174
175 IvfFileHeader ivf_file_header;
176 if (!ParseIvfFileHeader(header_buffer, &ivf_file_header)) {
177 LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF file header");
178 if (error_tolerant_) {
179 ivf_file_header = {};
180 } else {
181 return false;
182 }
183 }
184
185 width_ = ivf_file_header.width;
186 height_ = ivf_file_header.height;
187 frame_rate_ = ivf_file_header.frame_rate_numerator;
188 time_scale_ = ivf_file_header.frame_rate_denominator;
189 type_ = kFileTypeIvf;
190
191 return true;
192 }
193
194 } // namespace libgav1
195