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