xref: /aosp_15_r20/external/libgav1/examples/file_reader.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
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   // With C++11, to return |file|, an explicit move is required as the return
86   // type differs from the local variable. Overload resolution isn't guaranteed
87   // in this case, though some compilers may adopt the C++14 behavior (C++
88   // Standard Core Language Issue #1579, Return by converting move
89   // constructor):
90   // https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579
91   // To keep things simple we opt for the following compatible form.
92   return std::unique_ptr<FileReaderInterface>(file.release());
93 }
94 
95 // IVF Frame Header format, from https://wiki.multimedia.cx/index.php/IVF
96 // bytes 0-3    size of frame in bytes (not including the 12-byte header)
97 // bytes 4-11   64-bit presentation timestamp
98 // bytes 12..   frame data
ReadTemporalUnit(std::vector<uint8_t> * const tu_data,int64_t * const timestamp)99 bool FileReader::ReadTemporalUnit(std::vector<uint8_t>* const tu_data,
100                                   int64_t* const timestamp) {
101   if (tu_data == nullptr) return false;
102   tu_data->clear();
103 
104   uint8_t header_buffer[kIvfFrameHeaderSize];
105   const size_t num_read = fread(header_buffer, 1, kIvfFrameHeaderSize, file_);
106 
107   if (IsEndOfFile()) {
108     if (num_read != 0) {
109       LIBGAV1_EXAMPLES_LOG_ERROR(
110           "Cannot read IVF frame header: Not enough data available");
111       return false;
112     }
113 
114     return true;
115   }
116 
117   IvfFrameHeader ivf_frame_header;
118   if (!ParseIvfFrameHeader(header_buffer, &ivf_frame_header)) {
119     LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF frame header");
120     if (error_tolerant_) {
121       ivf_frame_header.frame_size =
122           std::min(ivf_frame_header.frame_size, size_t{kMaxTemporalUnitSize});
123     } else {
124       return false;
125     }
126   }
127 
128   if (timestamp != nullptr) *timestamp = ivf_frame_header.timestamp;
129 
130   tu_data->resize(ivf_frame_header.frame_size);
131   const size_t size_read =
132       fread(tu_data->data(), 1, ivf_frame_header.frame_size, file_);
133   if (size_read != ivf_frame_header.frame_size) {
134     LIBGAV1_EXAMPLES_LOG_ERROR(
135         "Unexpected EOF or I/O error reading frame data");
136     if (error_tolerant_) {
137       tu_data->resize(size_read);
138     } else {
139       return false;
140     }
141   }
142   return true;
143 }
144 
145 // Attempt to read an IVF file header. Returns true for success, and false for
146 // failure.
147 //
148 // IVF File Header format, from https://wiki.multimedia.cx/index.php/IVF
149 // bytes 0-3    signature: 'DKIF'
150 // bytes 4-5    version (should be 0)
151 // bytes 6-7    length of header in bytes
152 // bytes 8-11   codec FourCC (e.g., 'VP80')
153 // bytes 12-13  width in pixels
154 // bytes 14-15  height in pixels
155 // bytes 16-19  frame rate
156 // bytes 20-23  time scale
157 // bytes 24-27  number of frames in file
158 // bytes 28-31  unused
159 //
160 // Note: The rate and scale fields correspond to the numerator and denominator
161 // of frame rate (fps) or time base (the reciprocal of frame rate) as follows:
162 //
163 // bytes 16-19  frame rate  timebase.den  framerate.numerator
164 // bytes 20-23  time scale  timebase.num  framerate.denominator
ReadIvfFileHeader()165 bool FileReader::ReadIvfFileHeader() {
166   uint8_t header_buffer[kIvfFileHeaderSize];
167   const size_t num_read = fread(header_buffer, 1, kIvfFileHeaderSize, file_);
168   if (num_read != kIvfFileHeaderSize) {
169     LIBGAV1_EXAMPLES_LOG_ERROR(
170         "Cannot read IVF header: Not enough data available");
171     return false;
172   }
173 
174   IvfFileHeader ivf_file_header;
175   if (!ParseIvfFileHeader(header_buffer, &ivf_file_header)) {
176     LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF file header");
177     if (error_tolerant_) {
178       ivf_file_header = {};
179     } else {
180       return false;
181     }
182   }
183 
184   width_ = ivf_file_header.width;
185   height_ = ivf_file_header.height;
186   frame_rate_ = ivf_file_header.frame_rate_numerator;
187   time_scale_ = ivf_file_header.frame_rate_denominator;
188   type_ = kFileTypeIvf;
189 
190   return true;
191 }
192 
193 }  // namespace libgav1
194