diff options
Diffstat (limited to 'examples/file_reader.cc')
-rw-r--r-- | examples/file_reader.cc | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/examples/file_reader.cc b/examples/file_reader.cc new file mode 100644 index 0000000..b096722 --- /dev/null +++ b/examples/file_reader.cc @@ -0,0 +1,186 @@ +// 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 |