aboutsummaryrefslogtreecommitdiff
path: root/examples/file_reader.cc
blob: b0967227ef8c4bf55e71e4389e3cb6a3c4023993 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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