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