187 lines
5.3 KiB
C++
187 lines
5.3 KiB
C++
|
|
// 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
|