| // Copyright 2019 The libgav1 Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "examples/file_reader.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <cstdio> |
| #include <new> |
| #include <string> |
| #include <vector> |
| |
| #if defined(_WIN32) |
| #include <fcntl.h> |
| #include <io.h> |
| #endif |
| |
| #include "examples/file_reader_constants.h" |
| #include "examples/file_reader_factory.h" |
| #include "examples/file_reader_interface.h" |
| #include "examples/ivf_parser.h" |
| #include "examples/logging.h" |
| |
| namespace libgav1 { |
| namespace { |
| |
| FILE* SetBinaryMode(FILE* stream) { |
| #if defined(_WIN32) |
| _setmode(_fileno(stream), _O_BINARY); |
| #endif |
| return stream; |
| } |
| |
| } // namespace |
| |
| bool FileReader::registered_in_factory_ = |
| FileReaderFactory::RegisterReader(FileReader::Open); |
| |
| FileReader::~FileReader() { |
| if (owns_file_) fclose(file_); |
| } |
| |
| std::unique_ptr<FileReaderInterface> FileReader::Open( |
| const std::string& file_name, const bool error_tolerant) { |
| if (file_name.empty()) return nullptr; |
| |
| FILE* raw_file_ptr; |
| |
| bool owns_file = true; |
| if (file_name == "-") { |
| raw_file_ptr = SetBinaryMode(stdin); |
| owns_file = false; // stdin is owned by the Standard C Library. |
| } else { |
| raw_file_ptr = fopen(file_name.c_str(), "rb"); |
| } |
| |
| if (raw_file_ptr == nullptr) { |
| return nullptr; |
| } |
| |
| std::unique_ptr<FileReader> file( |
| new (std::nothrow) FileReader(raw_file_ptr, owns_file, error_tolerant)); |
| if (file == nullptr) { |
| LIBGAV1_EXAMPLES_LOG_ERROR("Out of memory"); |
| if (owns_file) fclose(raw_file_ptr); |
| return nullptr; |
| } |
| |
| if (!file->ReadIvfFileHeader()) { |
| LIBGAV1_EXAMPLES_LOG_ERROR("Unsupported file type"); |
| return nullptr; |
| } |
| |
| return file; |
| } |
| |
| // IVF Frame Header format, from https://wiki.multimedia.cx/index.php/IVF |
| // bytes 0-3 size of frame in bytes (not including the 12-byte header) |
| // bytes 4-11 64-bit presentation timestamp |
| // bytes 12.. frame data |
| bool FileReader::ReadTemporalUnit(std::vector<uint8_t>* const tu_data, |
| int64_t* const timestamp) { |
| if (tu_data == nullptr) return false; |
| tu_data->clear(); |
| |
| uint8_t header_buffer[kIvfFrameHeaderSize]; |
| const size_t num_read = fread(header_buffer, 1, kIvfFrameHeaderSize, file_); |
| |
| if (IsEndOfFile()) { |
| if (num_read != 0) { |
| LIBGAV1_EXAMPLES_LOG_ERROR( |
| "Cannot read IVF frame header: Not enough data available"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| IvfFrameHeader ivf_frame_header; |
| if (!ParseIvfFrameHeader(header_buffer, &ivf_frame_header)) { |
| LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF frame header"); |
| if (error_tolerant_) { |
| ivf_frame_header.frame_size = |
| std::min(ivf_frame_header.frame_size, size_t{kMaxTemporalUnitSize}); |
| } else { |
| return false; |
| } |
| } |
| |
| if (timestamp != nullptr) *timestamp = ivf_frame_header.timestamp; |
| |
| tu_data->resize(ivf_frame_header.frame_size); |
| const size_t size_read = |
| fread(tu_data->data(), 1, ivf_frame_header.frame_size, file_); |
| if (size_read != ivf_frame_header.frame_size) { |
| LIBGAV1_EXAMPLES_LOG_ERROR( |
| "Unexpected EOF or I/O error reading frame data"); |
| if (error_tolerant_) { |
| tu_data->resize(size_read); |
| } else { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Attempt to read an IVF file header. Returns true for success, and false for |
| // failure. |
| // |
| // IVF File Header format, from https://wiki.multimedia.cx/index.php/IVF |
| // bytes 0-3 signature: 'DKIF' |
| // bytes 4-5 version (should be 0) |
| // bytes 6-7 length of header in bytes |
| // bytes 8-11 codec FourCC (e.g., 'VP80') |
| // bytes 12-13 width in pixels |
| // bytes 14-15 height in pixels |
| // bytes 16-19 frame rate |
| // bytes 20-23 time scale |
| // bytes 24-27 number of frames in file |
| // bytes 28-31 unused |
| // |
| // Note: The rate and scale fields correspond to the numerator and denominator |
| // of frame rate (fps) or time base (the reciprocal of frame rate) as follows: |
| // |
| // bytes 16-19 frame rate timebase.den framerate.numerator |
| // bytes 20-23 time scale timebase.num framerate.denominator |
| bool FileReader::ReadIvfFileHeader() { |
| uint8_t header_buffer[kIvfFileHeaderSize]; |
| const size_t num_read = fread(header_buffer, 1, kIvfFileHeaderSize, file_); |
| if (num_read != kIvfFileHeaderSize) { |
| LIBGAV1_EXAMPLES_LOG_ERROR( |
| "Cannot read IVF header: Not enough data available"); |
| return false; |
| } |
| |
| IvfFileHeader ivf_file_header; |
| if (!ParseIvfFileHeader(header_buffer, &ivf_file_header)) { |
| LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF file header"); |
| if (error_tolerant_) { |
| ivf_file_header = {}; |
| } else { |
| return false; |
| } |
| } |
| |
| width_ = ivf_file_header.width; |
| height_ = ivf_file_header.height; |
| frame_rate_ = ivf_file_header.frame_rate_numerator; |
| time_scale_ = ivf_file_header.frame_rate_denominator; |
| type_ = kFileTypeIvf; |
| |
| return true; |
| } |
| |
| } // namespace libgav1 |