1 // Copyright 2019 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <cerrno>
16 #include <cstddef>
17 #include <cstdint>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <deque>
22 #include <memory>
23 #include <new>
24 #include <vector>
25
26 #include "absl/strings/numbers.h"
27 #include "absl/time/clock.h"
28 #include "absl/time/time.h"
29 #include "examples/file_reader_factory.h"
30 #include "examples/file_reader_interface.h"
31 #include "examples/file_writer.h"
32 #include "gav1/decoder.h"
33
34 #ifdef GAV1_DECODE_USE_CV_PIXEL_BUFFER_POOL
35 #include "examples/gav1_decode_cv_pixel_buffer_pool.h"
36 #endif
37
38 namespace {
39
40 struct Options {
41 const char* input_file_name = nullptr;
42 const char* output_file_name = nullptr;
43 const char* frame_timing_file_name = nullptr;
44 libgav1::FileWriter::FileType output_file_type =
45 libgav1::FileWriter::kFileTypeRaw;
46 uint8_t post_filter_mask = 0x1f;
47 int threads = 1;
48 bool frame_parallel = false;
49 bool output_all_layers = false;
50 bool parse_only = false;
51 int operating_point = 0;
52 int limit = 0;
53 int skip = 0;
54 int verbose = 0;
55 };
56
57 struct Timing {
58 absl::Duration input;
59 absl::Duration dequeue;
60 };
61
62 struct FrameTiming {
63 absl::Time enqueue;
64 absl::Time dequeue;
65 };
66
PrintHelp(FILE * const fout)67 void PrintHelp(FILE* const fout) {
68 fprintf(fout,
69 "Usage: gav1_decode [options] <input file>"
70 " [-o <output file>]\n");
71 fprintf(fout, "\n");
72 fprintf(fout, "Options:\n");
73 fprintf(fout, " -h, --help This help message.\n");
74 fprintf(fout, " --threads <positive integer> (Default 1).\n");
75 fprintf(fout, " --frame_parallel.\n");
76 fprintf(fout,
77 " --limit <integer> Stop decoding after N frames (0 = all).\n");
78 fprintf(fout, " --skip <integer> Skip initial N frames (Default 0).\n");
79 fprintf(fout, " --version.\n");
80 fprintf(fout, " --y4m (Default false).\n");
81 fprintf(fout, " --raw (Default true).\n");
82 fprintf(fout, " -v logging verbosity, can be used multiple times.\n");
83 fprintf(fout, " --all_layers.\n");
84 fprintf(fout,
85 " --parse_only, only parses the encoded video without producing "
86 "decoded frames.\n");
87 fprintf(fout,
88 " --operating_point <integer between 0 and 31> (Default 0).\n");
89 fprintf(fout,
90 " --frame_timing <file> Output per-frame timing to <file> in tsv"
91 " format.\n Yields meaningful results only when frame parallel is"
92 " off.\n");
93 fprintf(fout, "\nAdvanced settings:\n");
94 fprintf(fout, " --post_filter_mask <integer> (Default 0x1f).\n");
95 fprintf(fout,
96 " Mask indicating which post filters should be applied to the"
97 " reconstructed\n frame. This may be given as octal, decimal or"
98 " hexadecimal. From LSB:\n");
99 fprintf(fout, " Bit 0: Loop filter (deblocking filter)\n");
100 fprintf(fout, " Bit 1: Cdef\n");
101 fprintf(fout, " Bit 2: SuperRes\n");
102 fprintf(fout, " Bit 3: Loop Restoration\n");
103 fprintf(fout, " Bit 4: Film Grain Synthesis\n");
104 }
105
ParseOptions(int argc,char * argv[],Options * const options)106 void ParseOptions(int argc, char* argv[], Options* const options) {
107 for (int i = 1; i < argc; ++i) {
108 int32_t value;
109 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
110 PrintHelp(stdout);
111 exit(EXIT_SUCCESS);
112 } else if (strcmp(argv[i], "-o") == 0) {
113 if (++i >= argc) {
114 fprintf(stderr, "Missing argument for '-o'\n");
115 PrintHelp(stderr);
116 exit(EXIT_FAILURE);
117 }
118 options->output_file_name = argv[i];
119 } else if (strcmp(argv[i], "--frame_timing") == 0) {
120 if (++i >= argc) {
121 fprintf(stderr, "Missing argument for '--frame_timing'\n");
122 PrintHelp(stderr);
123 exit(EXIT_FAILURE);
124 }
125 options->frame_timing_file_name = argv[i];
126 } else if (strcmp(argv[i], "--version") == 0) {
127 printf("gav1_decode, a libgav1 based AV1 decoder\n");
128 printf("libgav1 %s\n", libgav1::GetVersionString());
129 printf("max bitdepth: %d\n", libgav1::Decoder::GetMaxBitdepth());
130 printf("build configuration: %s\n", libgav1::GetBuildConfiguration());
131 exit(EXIT_SUCCESS);
132 } else if (strcmp(argv[i], "-v") == 0) {
133 ++options->verbose;
134 } else if (strcmp(argv[i], "--raw") == 0) {
135 options->output_file_type = libgav1::FileWriter::kFileTypeRaw;
136 } else if (strcmp(argv[i], "--y4m") == 0) {
137 options->output_file_type = libgav1::FileWriter::kFileTypeY4m;
138 } else if (strcmp(argv[i], "--threads") == 0) {
139 if (++i >= argc || !absl::SimpleAtoi(argv[i], &value)) {
140 fprintf(stderr, "Missing/Invalid value for --threads.\n");
141 PrintHelp(stderr);
142 exit(EXIT_FAILURE);
143 }
144 options->threads = value;
145 } else if (strcmp(argv[i], "--frame_parallel") == 0) {
146 options->frame_parallel = true;
147 } else if (strcmp(argv[i], "--parse_only") == 0) {
148 options->parse_only = true;
149 } else if (strcmp(argv[i], "--all_layers") == 0) {
150 options->output_all_layers = true;
151 } else if (strcmp(argv[i], "--operating_point") == 0) {
152 if (++i >= argc || !absl::SimpleAtoi(argv[i], &value) || value < 0 ||
153 value >= 32) {
154 fprintf(stderr, "Missing/Invalid value for --operating_point.\n");
155 PrintHelp(stderr);
156 exit(EXIT_FAILURE);
157 }
158 options->operating_point = value;
159 } else if (strcmp(argv[i], "--limit") == 0) {
160 if (++i >= argc || !absl::SimpleAtoi(argv[i], &value) || value < 0) {
161 fprintf(stderr, "Missing/Invalid value for --limit.\n");
162 PrintHelp(stderr);
163 exit(EXIT_FAILURE);
164 }
165 options->limit = value;
166 } else if (strcmp(argv[i], "--skip") == 0) {
167 if (++i >= argc || !absl::SimpleAtoi(argv[i], &value) || value < 0) {
168 fprintf(stderr, "Missing/Invalid value for --skip.\n");
169 PrintHelp(stderr);
170 exit(EXIT_FAILURE);
171 }
172 options->skip = value;
173 } else if (strcmp(argv[i], "--post_filter_mask") == 0) {
174 errno = 0;
175 char* endptr = nullptr;
176 value = (++i >= argc) ? -1
177 // NOLINTNEXTLINE(runtime/deprecated_fn)
178 : static_cast<int32_t>(strtol(argv[i], &endptr, 0));
179 // Only the last 5 bits of the mask can be set.
180 if ((value & ~31) != 0 || errno != 0 || endptr == argv[i]) {
181 fprintf(stderr, "Invalid value for --post_filter_mask.\n");
182 PrintHelp(stderr);
183 exit(EXIT_FAILURE);
184 }
185 options->post_filter_mask = value;
186 } else if (strlen(argv[i]) > 1 && argv[i][0] == '-') {
187 fprintf(stderr, "Unknown option '%s'!\n", argv[i]);
188 exit(EXIT_FAILURE);
189 } else {
190 if (options->input_file_name == nullptr) {
191 options->input_file_name = argv[i];
192 } else {
193 fprintf(stderr, "Found invalid parameter: \"%s\".\n", argv[i]);
194 PrintHelp(stderr);
195 exit(EXIT_FAILURE);
196 }
197 }
198 }
199
200 if (argc < 2 || options->input_file_name == nullptr) {
201 fprintf(stderr, "Input file is required!\n");
202 PrintHelp(stderr);
203 exit(EXIT_FAILURE);
204 }
205
206 if (options->parse_only &&
207 (options->threads > 1 || options->frame_parallel)) {
208 fprintf(stderr,
209 "Neither --threads nor --frame_parallel can be set together "
210 "with the --parse_only option.\n");
211 PrintHelp(stderr);
212 exit(EXIT_FAILURE);
213 }
214 }
215
216 using InputBuffer = std::vector<uint8_t>;
217
218 class InputBuffers {
219 public:
~InputBuffers()220 ~InputBuffers() {
221 for (auto buffer : free_buffers_) {
222 delete buffer;
223 }
224 }
GetFreeBuffer()225 InputBuffer* GetFreeBuffer() {
226 if (free_buffers_.empty()) {
227 auto* const buffer = new (std::nothrow) InputBuffer();
228 if (buffer == nullptr) {
229 fprintf(stderr, "Failed to create input buffer.\n");
230 return nullptr;
231 }
232 free_buffers_.push_back(buffer);
233 }
234 InputBuffer* const buffer = free_buffers_.front();
235 free_buffers_.pop_front();
236 return buffer;
237 }
238
ReleaseInputBuffer(InputBuffer * buffer)239 void ReleaseInputBuffer(InputBuffer* buffer) {
240 free_buffers_.push_back(buffer);
241 }
242
243 private:
244 std::deque<InputBuffer*> free_buffers_;
245 };
246
ReleaseInputBuffer(void * callback_private_data,void * buffer_private_data)247 void ReleaseInputBuffer(void* callback_private_data,
248 void* buffer_private_data) {
249 auto* const input_buffers = static_cast<InputBuffers*>(callback_private_data);
250 input_buffers->ReleaseInputBuffer(
251 static_cast<InputBuffer*>(buffer_private_data));
252 }
253
CloseFile(FILE * stream)254 int CloseFile(FILE* stream) { return (stream == nullptr) ? 0 : fclose(stream); }
255
256 } // namespace
257
main(int argc,char * argv[])258 int main(int argc, char* argv[]) {
259 Options options;
260 ParseOptions(argc, argv, &options);
261
262 auto file_reader =
263 libgav1::FileReaderFactory::OpenReader(options.input_file_name);
264 if (file_reader == nullptr) {
265 fprintf(stderr, "Cannot open input file!\n");
266 return EXIT_FAILURE;
267 }
268
269 std::unique_ptr<FILE, decltype(&CloseFile)> frame_timing_file(nullptr,
270 &CloseFile);
271 if (options.frame_timing_file_name != nullptr) {
272 frame_timing_file.reset(fopen(options.frame_timing_file_name, "wb"));
273 if (frame_timing_file == nullptr) {
274 fprintf(stderr, "Cannot open frame timing file '%s'!\n",
275 options.frame_timing_file_name);
276 return EXIT_FAILURE;
277 }
278 }
279
280 #ifdef GAV1_DECODE_USE_CV_PIXEL_BUFFER_POOL
281 // Reference frames + 1 scratch frame (for either the current frame or the
282 // film grain frame).
283 constexpr int kNumBuffers = 8 + 1;
284 std::unique_ptr<Gav1DecodeCVPixelBufferPool> cv_pixel_buffers =
285 Gav1DecodeCVPixelBufferPool::Create(kNumBuffers);
286 if (cv_pixel_buffers == nullptr) {
287 fprintf(stderr, "Cannot create Gav1DecodeCVPixelBufferPool!\n");
288 return EXIT_FAILURE;
289 }
290 #endif
291
292 InputBuffers input_buffers;
293 libgav1::Decoder decoder;
294 libgav1::DecoderSettings settings;
295 settings.post_filter_mask = options.post_filter_mask;
296 settings.threads = options.threads;
297 settings.frame_parallel = options.frame_parallel;
298 settings.parse_only = options.parse_only;
299 settings.output_all_layers = options.output_all_layers;
300 settings.operating_point = options.operating_point;
301 settings.blocking_dequeue = true;
302 settings.callback_private_data = &input_buffers;
303 settings.release_input_buffer = ReleaseInputBuffer;
304 #ifdef GAV1_DECODE_USE_CV_PIXEL_BUFFER_POOL
305 settings.on_frame_buffer_size_changed = Gav1DecodeOnCVPixelBufferSizeChanged;
306 settings.get_frame_buffer = Gav1DecodeGetCVPixelBuffer;
307 settings.release_frame_buffer = Gav1DecodeReleaseCVPixelBuffer;
308 settings.callback_private_data = cv_pixel_buffers.get();
309 settings.release_input_buffer = nullptr;
310 // TODO(vigneshv): Support frame parallel mode to be used with
311 // CVPixelBufferPool.
312 settings.frame_parallel = false;
313 #endif
314 libgav1::StatusCode status = decoder.Init(&settings);
315 if (status != libgav1::kStatusOk) {
316 fprintf(stderr, "Error initializing decoder: %s\n",
317 libgav1::GetErrorString(status));
318 return EXIT_FAILURE;
319 }
320
321 fprintf(stderr, "decoding '%s'\n", options.input_file_name);
322 if (options.verbose > 0 && options.skip > 0) {
323 fprintf(stderr, "skipping %d frame(s).\n", options.skip);
324 }
325
326 int input_frames = 0;
327 int decoded_frames = 0;
328 int parsed_frames = 0;
329 Timing timing = {};
330 std::vector<FrameTiming> frame_timing;
331 const bool record_frame_timing = frame_timing_file != nullptr;
332 std::unique_ptr<libgav1::FileWriter> file_writer;
333 InputBuffer* input_buffer = nullptr;
334 bool limit_reached = false;
335 bool dequeue_finished = false;
336 const absl::Time decode_loop_start = absl::Now();
337 do {
338 if (input_buffer == nullptr && !file_reader->IsEndOfFile() &&
339 !limit_reached) {
340 input_buffer = input_buffers.GetFreeBuffer();
341 if (input_buffer == nullptr) return EXIT_FAILURE;
342 const absl::Time read_start = absl::Now();
343 if (!file_reader->ReadTemporalUnit(input_buffer,
344 /*timestamp=*/nullptr)) {
345 fprintf(stderr, "Error reading input file.\n");
346 return EXIT_FAILURE;
347 }
348 timing.input += absl::Now() - read_start;
349 }
350
351 if (++input_frames <= options.skip) {
352 input_buffers.ReleaseInputBuffer(input_buffer);
353 input_buffer = nullptr;
354 continue;
355 }
356
357 if (input_buffer != nullptr) {
358 if (input_buffer->empty()) {
359 input_buffers.ReleaseInputBuffer(input_buffer);
360 input_buffer = nullptr;
361 continue;
362 }
363
364 const absl::Time enqueue_start = absl::Now();
365 status = decoder.EnqueueFrame(input_buffer->data(), input_buffer->size(),
366 static_cast<int64_t>(frame_timing.size()),
367 /*buffer_private_data=*/input_buffer);
368 if (status == libgav1::kStatusOk) {
369 if (options.verbose > 1) {
370 fprintf(stderr, "enqueue frame (length %zu)\n", input_buffer->size());
371 }
372 if (record_frame_timing) {
373 FrameTiming enqueue_time = {enqueue_start, absl::UnixEpoch()};
374 frame_timing.emplace_back(enqueue_time);
375 }
376
377 input_buffer = nullptr;
378 // Continue to enqueue frames until we get a kStatusTryAgain status.
379 continue;
380 }
381 if (status != libgav1::kStatusTryAgain) {
382 fprintf(stderr, "Unable to enqueue frame: %s\n",
383 libgav1::GetErrorString(status));
384 return EXIT_FAILURE;
385 }
386 }
387
388 const libgav1::DecoderBuffer* buffer;
389 status = decoder.DequeueFrame(&buffer);
390 if (status == libgav1::kStatusNothingToDequeue) {
391 dequeue_finished = true;
392 continue;
393 }
394 if (status != libgav1::kStatusOk) {
395 fprintf(stderr, "Unable to dequeue frame: %s\n",
396 libgav1::GetErrorString(status));
397 return EXIT_FAILURE;
398 }
399 if (options.parse_only) {
400 // Example of how the QP values per frame in decoding/parsing
401 // order can be obtained.
402 std::vector<int> qp_vec = decoder.GetFramesMeanQpInTemporalUnit();
403 if (qp_vec.empty()) {
404 fprintf(stderr,
405 "The latest temporal unit did not contain any decodable "
406 "frames. Hence, no QP values to show.");
407 } else {
408 fprintf(
409 stderr,
410 "The QP values for the frames in the latest temporal unit are: ");
411 }
412 while (!qp_vec.empty()) {
413 fprintf(stderr, "%d, ", qp_vec.front());
414 qp_vec.erase(qp_vec.begin());
415 ++parsed_frames;
416 }
417 fprintf(stderr, "\n");
418 }
419 dequeue_finished = false;
420 if (buffer == nullptr) continue;
421 ++decoded_frames;
422 if (options.verbose > 1) {
423 fprintf(stderr, "buffer dequeued\n");
424 }
425
426 if (record_frame_timing) {
427 frame_timing[static_cast<int>(buffer->user_private_data)].dequeue =
428 absl::Now();
429 }
430
431 if (options.output_file_name != nullptr && file_writer == nullptr) {
432 libgav1::FileWriter::Y4mParameters y4m_parameters;
433 y4m_parameters.width = buffer->displayed_width[0];
434 y4m_parameters.height = buffer->displayed_height[0];
435 y4m_parameters.frame_rate_numerator = file_reader->frame_rate();
436 y4m_parameters.frame_rate_denominator = file_reader->time_scale();
437 y4m_parameters.chroma_sample_position = buffer->chroma_sample_position;
438 y4m_parameters.image_format = buffer->image_format;
439 y4m_parameters.bitdepth = static_cast<size_t>(buffer->bitdepth);
440 file_writer = libgav1::FileWriter::Open(
441 options.output_file_name, options.output_file_type, &y4m_parameters);
442 if (file_writer == nullptr) {
443 fprintf(stderr, "Cannot open output file!\n");
444 return EXIT_FAILURE;
445 }
446 }
447
448 if (!limit_reached && file_writer != nullptr &&
449 !file_writer->WriteFrame(*buffer)) {
450 fprintf(stderr, "Error writing output file.\n");
451 return EXIT_FAILURE;
452 }
453 if (options.limit > 0 && options.limit == decoded_frames) {
454 limit_reached = true;
455 if (input_buffer != nullptr) {
456 input_buffers.ReleaseInputBuffer(input_buffer);
457 }
458 input_buffer = nullptr;
459 // Clear any in progress frames to ensure the output frame limit is
460 // respected.
461 decoder.SignalEOS();
462 }
463 } while (input_buffer != nullptr ||
464 (!file_reader->IsEndOfFile() && !limit_reached) ||
465 !dequeue_finished);
466 timing.dequeue = absl::Now() - decode_loop_start - timing.input;
467
468 if (record_frame_timing) {
469 // Note timing for frame parallel will be skewed by the time spent queueing
470 // additional frames and in the output queue waiting for previous frames,
471 // the values reported won't be that meaningful.
472 fprintf(frame_timing_file.get(), "frame number\tdecode time us\n");
473 for (size_t i = 0; i < frame_timing.size(); ++i) {
474 const int decode_time_us = static_cast<int>(absl::ToInt64Microseconds(
475 frame_timing[i].dequeue - frame_timing[i].enqueue));
476 fprintf(frame_timing_file.get(), "%zu\t%d\n", i, decode_time_us);
477 }
478 }
479
480 if (options.verbose > 0) {
481 fprintf(stderr, "time to read input: %d us\n",
482 static_cast<int>(absl::ToInt64Microseconds(timing.input)));
483 const int process_time_us =
484 static_cast<int>(absl::ToInt64Microseconds(timing.dequeue));
485 if (options.parse_only) {
486 const double parse_fps = (process_time_us == 0)
487 ? 0.0
488 : 1.0e6 * parsed_frames / process_time_us;
489 fprintf(stderr, "time to parse input: %d us (%d frames, %.2f fps)\n",
490 process_time_us, parsed_frames, parse_fps);
491 } else {
492 const double decode_fps = (process_time_us == 0)
493 ? 0.0
494 : 1.0e6 * decoded_frames / process_time_us;
495 fprintf(stderr, "time to decode input: %d us (%d frames, %.2f fps)\n",
496 process_time_us, decoded_frames, decode_fps);
497 }
498 }
499
500 return EXIT_SUCCESS;
501 }
502