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 "absl/memory/memory.h"
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(absl::string_view file_name,const bool error_tolerant)55 std::unique_ptr<FileReaderInterface> FileReader::Open(
56     absl::string_view file_name, const bool error_tolerant) {
57   if (file_name.empty()) return nullptr;
58 
59   const std::string fopen_file_name = std::string(file_name);
60   FILE* raw_file_ptr;
61 
62   bool owns_file = true;
63   if (file_name == "-") {
64     raw_file_ptr = SetBinaryMode(stdin);
65     owns_file = false;  // stdin is owned by the Standard C Library.
66   } else {
67     raw_file_ptr = fopen(fopen_file_name.c_str(), "rb");
68   }
69 
70   if (raw_file_ptr == nullptr) {
71     return nullptr;
72   }
73 
74   auto file = absl::WrapUnique(
75       new (std::nothrow) FileReader(raw_file_ptr, owns_file, error_tolerant));
76   if (file == nullptr) {
77     LIBGAV1_EXAMPLES_LOG_ERROR("Out of memory");
78     if (owns_file) fclose(raw_file_ptr);
79     return nullptr;
80   }
81 
82   if (!file->ReadIvfFileHeader()) {
83     LIBGAV1_EXAMPLES_LOG_ERROR("Unsupported file type");
84     return nullptr;
85   }
86 
87   return file;
88 }
89 
90 // IVF Frame Header format, from https://wiki.multimedia.cx/index.php/IVF
91 // bytes 0-3    size of frame in bytes (not including the 12-byte header)
92 // bytes 4-11   64-bit presentation timestamp
93 // bytes 12..   frame data
ReadTemporalUnit(std::vector<uint8_t> * const tu_data,int64_t * const timestamp)94 bool FileReader::ReadTemporalUnit(std::vector<uint8_t>* const tu_data,
95                                   int64_t* const timestamp) {
96   if (tu_data == nullptr) return false;
97   tu_data->clear();
98 
99   uint8_t header_buffer[kIvfFrameHeaderSize];
100   const size_t num_read = fread(header_buffer, 1, kIvfFrameHeaderSize, file_);
101 
102   if (IsEndOfFile()) {
103     if (num_read != 0) {
104       LIBGAV1_EXAMPLES_LOG_ERROR(
105           "Cannot read IVF frame header: Not enough data available");
106       return false;
107     }
108 
109     return true;
110   }
111 
112   IvfFrameHeader ivf_frame_header;
113   if (!ParseIvfFrameHeader(header_buffer, &ivf_frame_header)) {
114     LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF frame header");
115     if (error_tolerant_) {
116       ivf_frame_header.frame_size =
117           std::min(ivf_frame_header.frame_size, size_t{kMaxTemporalUnitSize});
118     } else {
119       return false;
120     }
121   }
122 
123   if (timestamp != nullptr) *timestamp = ivf_frame_header.timestamp;
124 
125   tu_data->resize(ivf_frame_header.frame_size);
126   const size_t size_read =
127       fread(tu_data->data(), 1, ivf_frame_header.frame_size, file_);
128   if (size_read != ivf_frame_header.frame_size) {
129     LIBGAV1_EXAMPLES_LOG_ERROR(
130         "Unexpected EOF or I/O error reading frame data");
131     if (error_tolerant_) {
132       tu_data->resize(size_read);
133     } else {
134       return false;
135     }
136   }
137   return true;
138 }
139 
140 // Attempt to read an IVF file header. Returns true for success, and false for
141 // failure.
142 //
143 // IVF File Header format, from https://wiki.multimedia.cx/index.php/IVF
144 // bytes 0-3    signature: 'DKIF'
145 // bytes 4-5    version (should be 0)
146 // bytes 6-7    length of header in bytes
147 // bytes 8-11   codec FourCC (e.g., 'VP80')
148 // bytes 12-13  width in pixels
149 // bytes 14-15  height in pixels
150 // bytes 16-19  frame rate
151 // bytes 20-23  time scale
152 // bytes 24-27  number of frames in file
153 // bytes 28-31  unused
154 //
155 // Note: The rate and scale fields correspond to the numerator and denominator
156 // of frame rate (fps) or time base (the reciprocal of frame rate) as follows:
157 //
158 // bytes 16-19  frame rate  timebase.den  framerate.numerator
159 // bytes 20-23  time scale  timebase.num  framerate.denominator
ReadIvfFileHeader()160 bool FileReader::ReadIvfFileHeader() {
161   uint8_t header_buffer[kIvfFileHeaderSize];
162   const size_t num_read = fread(header_buffer, 1, kIvfFileHeaderSize, file_);
163   if (num_read != kIvfFileHeaderSize) {
164     LIBGAV1_EXAMPLES_LOG_ERROR(
165         "Cannot read IVF header: Not enough data available");
166     return false;
167   }
168 
169   IvfFileHeader ivf_file_header;
170   if (!ParseIvfFileHeader(header_buffer, &ivf_file_header)) {
171     LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF file header");
172     if (error_tolerant_) {
173       ivf_file_header = {};
174     } else {
175       return false;
176     }
177   }
178 
179   width_ = ivf_file_header.width;
180   height_ = ivf_file_header.height;
181   frame_rate_ = ivf_file_header.frame_rate_numerator;
182   time_scale_ = ivf_file_header.frame_rate_denominator;
183   type_ = kFileTypeIvf;
184 
185   return true;
186 }
187 
188 }  // namespace libgav1
189