text: /*
   * Copyright (c) 2018, Alliance for Open Media. All rights reserved
   *
   * This source code is subject to the terms of the BSD 2 Clause License and
   * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
   * was not distributed with this source code in the LICENSE file, you can
   * obtain it at www.aomedia.org/license/software. If the Alliance for Open
   * Media Patent License 1.0 was not distributed with this source code in the
   * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
   */
  
  // Scalable Decoder
  // ==============
  //
  // This is an example of a scalable decoder loop. It takes a 2-spatial-layer
  // input file
  // containing the compressed data (in OBU format), passes it through the
  // decoder, and writes the decompressed frames to disk. The base layer and
  // enhancement layers are stored as separate files, lyr0.yuv and lyr1.yuv,
  // respectively.
  //
  // Standard Includes
  // -----------------
  // For decoders, you only have to include `aom_decoder.h` and then any
  // header files for the specific codecs you use. In this case, we're using
  // av1.
  //
  // Initializing The Codec
  // ----------------------
  // The libaom decoder is initialized by the call to aom_codec_dec_init().
  // Determining the codec interface to use is handled by AvxVideoReader and the
  // functions prefixed with aom_video_reader_. Discussion of those functions is
  // beyond the scope of this example, but the main gist is to open the input file
  // and parse just enough of it to determine if it's a AVx file and which AVx
  // codec is contained within the file.
  // Note the NULL pointer passed to aom_codec_dec_init(). We do that in this
  // example because we want the algorithm to determine the stream configuration
  // (width/height) and allocate memory automatically.
  //
  // Decoding A Frame
  // ----------------
  // Once the frame has been read into memory, it is decoded using the
  // `aom_codec_decode` function. The call takes a pointer to the data
  // (`frame`) and the length of the data (`frame_size`). No application data
  // is associated with the frame in this example, so the `user_priv`
  // parameter is NULL. The `deadline` parameter is left at zero for this
  // example. This parameter is generally only used when doing adaptive post
  // processing.
  //
  // Codecs may produce a variable number of output frames for every call to
  // `aom_codec_decode`. These frames are retrieved by the
  // `aom_codec_get_frame` iterator function. The iterator variable `iter` is
  // initialized to NULL each time `aom_codec_decode` is called.
  // `aom_codec_get_frame` is called in a loop, returning a pointer to a
  // decoded image or NULL to indicate the end of list.
  //
  // Processing The Decoded Data
  // ---------------------------
  // In this example, we simply write the encoded data to disk. It is
  // important to honor the image's `stride` values.
  //
  // Cleanup
  // -------
  // The `aom_codec_destroy` call frees any memory allocated by the codec.
  //
  // Error Handling
  // --------------
  // This example does not special case any error return codes. If there was
  // an error, a descriptive message is printed and the program exits. With
  // few exceptions, aom_codec functions return an enumerated error status,
  // with the value `0` indicating success.
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "aom/aom_decoder.h"
  
  #include "../tools_common.h"
  #include "../video_reader.h"
  #include "./aom_config.h"
  #include "./obudec.h"
  
  static const char *exec_name;
  
  #define MAX_LAYERS 5
  
  void usage_exit(void) {
    fprintf(stderr, "Usage: %s <infile>\n", exec_name);
    exit(EXIT_FAILURE);
  }
  
  int main(int argc, char **argv) {
    int frame_cnt = 0;
    FILE *outfile[MAX_LAYERS];
    char filename[80];
    aom_codec_ctx_t codec;
    const AvxInterface *decoder = NULL;
    FILE *inputfile = NULL;
    uint8_t *buf = NULL;
    size_t bytes_in_buffer = 0;
    size_t buffer_size = 0;
    int next_layer_id = 0;
    struct AvxInputContext aom_input_ctx;
    struct ObuDecInputContext obu_ctx = { &aom_input_ctx, NULL, 0, 0 };
    aom_codec_stream_info_t si;
    uint8_t tmpbuf[32];
    unsigned int i;
  
    exec_name = argv[0];
  
    if (argc != 2) die("Invalid number of arguments.");
  
    if (!(inputfile = fopen(argv[1], "rb")))
      die("Failed to open %s for read.", argv[1]);
    obu_ctx.avx_ctx->file = inputfile;
    obu_ctx.avx_ctx->filename = argv[1];
  
    decoder = get_aom_decoder_by_index(0);
    printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
  
    if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
      die_codec(&codec, "Failed to initialize decoder.");
  
    if (!file_is_obu(&obu_ctx))
      die_codec(&codec, "Input is not a valid obu file");
  
    // peak sequence header OBU to get enhancement layer count, if any
    const size_t ret = fread(tmpbuf, 1, 32, inputfile);
    if (ret != 32) die_codec(&codec, "Input is not a valid obu file");
    if (aom_codec_peek_stream_info(decoder->codec_interface(), tmpbuf, 32, &si)) {
      die_codec(&codec, "Input is not a valid obu file");
    }
    fseek(inputfile, -32, SEEK_CUR);
  
    // open output yuv files
    for (i = 0; i <= si.enhancement_layers_cnt; i++) {
      snprintf(filename, sizeof(filename), "out_lyr%d.yuv", i);
      if (!(outfile[i] = fopen(filename, "wb")))
        die("Failed to open output for writing.");
    }
  
    while (!obudec_read_temporal_unit(&obu_ctx, &buf, &bytes_in_buffer,
                                      &buffer_size, next_layer_id)) {
      aom_codec_iter_t iter = NULL;
      aom_image_t *img = NULL;
      if (aom_codec_decode(&codec, buf, (unsigned int)bytes_in_buffer, NULL))
        die_codec(&codec, "Failed to decode frame.");
  
      while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) {
        if (img->enhancement_id == 0) {
          printf("Writing       base layer 0 %d\n", frame_cnt);
          aom_img_write(img, outfile[0]);
          next_layer_id++;
        } else if (img->enhancement_id <= (int)si.enhancement_layers_cnt) {
          printf("Writing enhancemnt layer %d %d\n", img->enhancement_id,
                 frame_cnt);
          aom_img_write(img, outfile[img->enhancement_id]);
          if (img->enhancement_id == (int)si.enhancement_layers_cnt)
            next_layer_id = 0;
          else
            next_layer_id++;
        } else {
          die_codec(&codec, "Invalid bitstream.  Layer id exceeds layer count");
        }
        if (img->enhancement_id == (int)si.enhancement_layers_cnt) ++frame_cnt;
      }
    }
  
    printf("Processed %d frames.\n", frame_cnt);
    if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
  
    for (i = 0; i <= si.enhancement_layers_cnt; i++) fclose(outfile[i]);
  
    fclose(inputfile);
  
    return EXIT_SUCCESS;
  }
  