int main(int argc, char **argv) { int frame_cnt = 0; FILE *outfile = NULL; aom_codec_ctx_t codec; AvxVideoReader *reader = NULL; const AvxInterface *decoder = NULL; const AvxVideoInfo *info = NULL; exec_name = argv[0]; if (argc != 3) die("Invalid number of arguments."); reader = aom_video_reader_open(argv[1]); if (!reader) die("Failed to open %s for reading.", argv[1]); if (!(outfile = fopen(argv[2], "wb"))) die("Failed to open %s for writing.", argv[2]); info = aom_video_reader_get_info(reader); decoder = get_aom_decoder_by_fourcc(info->codec_fourcc); if (!decoder) die("Unknown input codec."); 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."); while (aom_video_reader_read_frame(reader)) { aom_codec_iter_t iter = NULL; aom_image_t *img = NULL; size_t frame_size = 0; const unsigned char *frame = aom_video_reader_get_frame(reader, &frame_size); if (aom_codec_decode(&codec, frame, frame_size, NULL)) die_codec(&codec, "Failed to decode frame."); while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) { aom_img_write(img, outfile); ++frame_cnt; } } printf("Processed %d frames.\n", frame_cnt); if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec"); printf("Play: ffplay -f rawvideo -pix_fmt yuv420p -s %dx%d %s\n", info->frame_width, info->frame_height, argv[2]); aom_video_reader_close(reader); fclose(outfile); return EXIT_SUCCESS; }
EMSCRIPTEN_KEEPALIVE int open_file(char *file) { if (file == NULL) { file = "/tmp/input.ivf"; } reader = aom_video_reader_open(file); if (!reader) die("Failed to open %s for reading.", file); info = aom_video_reader_get_info(reader); decoder = get_aom_decoder_by_fourcc(info->codec_fourcc); if (!decoder) die("Unknown input codec."); 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."); init_analyzer(); aom_codec_control(&codec, ANALYZER_SET_DATA, &analyzer_data); printf("Opened file %s okay.\n", file); return EXIT_SUCCESS; }
static int main_loop(int argc, const char **argv_) { aom_codec_ctx_t decoder; char *fn = NULL; int i; int ret = EXIT_FAILURE; uint8_t *buf = NULL; size_t bytes_in_buffer = 0, buffer_size = 0; FILE *infile; int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0; int do_md5 = 0, progress = 0, frame_parallel = 0; int stop_after = 0, postproc = 0, summary = 0, quiet = 1; int arg_skip = 0; int ec_enabled = 0; int keep_going = 0; const AvxInterface *interface = NULL; const AvxInterface *fourcc_interface = NULL; uint64_t dx_time = 0; struct arg arg; char **argv, **argi, **argj; int single_file; int use_y4m = 1; int opt_yv12 = 0; int opt_i420 = 0; aom_codec_dec_cfg_t cfg = { 0, 0, 0 }; #if CONFIG_AOM_HIGHBITDEPTH unsigned int output_bit_depth = 0; #endif #if CONFIG_EXT_TILE int tile_row = -1; int tile_col = -1; #endif // CONFIG_EXT_TILE int frames_corrupted = 0; int dec_flags = 0; int do_scale = 0; aom_image_t *scaled_img = NULL; #if CONFIG_AOM_HIGHBITDEPTH aom_image_t *img_shifted = NULL; #endif int frame_avail, got_data, flush_decoder = 0; int num_external_frame_buffers = 0; struct ExternalFrameBufferList ext_fb_list = { 0, NULL }; const char *outfile_pattern = NULL; char outfile_name[PATH_MAX] = { 0 }; FILE *outfile = NULL; FILE *framestats_file = NULL; MD5Context md5_ctx; unsigned char md5_digest[16]; struct AvxDecInputContext input = { NULL, NULL }; struct AvxInputContext aom_input_ctx; #if CONFIG_WEBM_IO struct WebmInputContext webm_ctx; memset(&(webm_ctx), 0, sizeof(webm_ctx)); input.webm_ctx = &webm_ctx; #endif input.aom_input_ctx = &aom_input_ctx; /* Parse command line */ exec_name = argv_[0]; argv = argv_dup(argc - 1, argv_ + 1); for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { memset(&arg, 0, sizeof(arg)); arg.argv_step = 1; if (arg_match(&arg, &codecarg, argi)) { interface = get_aom_decoder_by_name(arg.val); if (!interface) die("Error: Unrecognized argument (%s) to --codec\n", arg.val); } else if (arg_match(&arg, &looparg, argi)) { // no-op } else if (arg_match(&arg, &outputfile, argi)) { outfile_pattern = arg.val; } else if (arg_match(&arg, &use_yv12, argi)) { use_y4m = 0; flipuv = 1; opt_yv12 = 1; } else if (arg_match(&arg, &use_i420, argi)) { use_y4m = 0; flipuv = 0; opt_i420 = 1; } else if (arg_match(&arg, &rawvideo, argi)) { use_y4m = 0; } else if (arg_match(&arg, &flipuvarg, argi)) { flipuv = 1; } else if (arg_match(&arg, &noblitarg, argi)) { noblit = 1; } else if (arg_match(&arg, &progressarg, argi)) { progress = 1; } else if (arg_match(&arg, &limitarg, argi)) { stop_after = arg_parse_uint(&arg); } else if (arg_match(&arg, &skiparg, argi)) { arg_skip = arg_parse_uint(&arg); } else if (arg_match(&arg, &postprocarg, argi)) { postproc = 1; } else if (arg_match(&arg, &md5arg, argi)) { do_md5 = 1; } else if (arg_match(&arg, &framestatsarg, argi)) { framestats_file = fopen(arg.val, "w"); if (!framestats_file) { die("Error: Could not open --framestats file (%s) for writing.\n", arg.val); } } else if (arg_match(&arg, &summaryarg, argi)) { summary = 1; } else if (arg_match(&arg, &threadsarg, argi)) { cfg.threads = arg_parse_uint(&arg); } #if CONFIG_AV1_DECODER else if (arg_match(&arg, &frameparallelarg, argi)) frame_parallel = 1; #endif else if (arg_match(&arg, &verbosearg, argi)) quiet = 0; else if (arg_match(&arg, &scalearg, argi)) do_scale = 1; else if (arg_match(&arg, &fb_arg, argi)) num_external_frame_buffers = arg_parse_uint(&arg); else if (arg_match(&arg, &continuearg, argi)) keep_going = 1; #if CONFIG_AOM_HIGHBITDEPTH else if (arg_match(&arg, &outbitdeptharg, argi)) { output_bit_depth = arg_parse_uint(&arg); } #endif #if CONFIG_EXT_TILE else if (arg_match(&arg, &tiler, argi)) tile_row = arg_parse_int(&arg); else if (arg_match(&arg, &tilec, argi)) tile_col = arg_parse_int(&arg); #endif // CONFIG_EXT_TILE else argj++; } /* Check for unrecognized options */ for (argi = argv; *argi; argi++) if (argi[0][0] == '-' && strlen(argi[0]) > 1) die("Error: Unrecognized option %s\n", *argi); /* Handle non-option arguments */ fn = argv[0]; if (!fn) { free(argv); usage_exit(); } /* Open file */ infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin); if (!infile) { fatal("Failed to open input file '%s'", strcmp(fn, "-") ? fn : "stdin"); } #if CONFIG_OS_SUPPORT /* Make sure we don't dump to the terminal, unless forced to with -o - */ if (!outfile_pattern && isatty(STDOUT_FILENO) && !do_md5 && !noblit) { fprintf(stderr, "Not dumping raw video to your terminal. Use '-o -' to " "override.\n"); return EXIT_FAILURE; } #endif input.aom_input_ctx->file = infile; if (file_is_ivf(input.aom_input_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_IVF; #if CONFIG_WEBM_IO else if (file_is_webm(input.webm_ctx, input.aom_input_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_WEBM; #endif else if (file_is_raw(input.aom_input_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_RAW; else { fprintf(stderr, "Unrecognized input file type.\n"); #if !CONFIG_WEBM_IO fprintf(stderr, "aomdec was built without WebM container support.\n"); #endif return EXIT_FAILURE; } outfile_pattern = outfile_pattern ? outfile_pattern : "-"; single_file = is_single_file(outfile_pattern); if (!noblit && single_file) { generate_filename(outfile_pattern, outfile_name, PATH_MAX, aom_input_ctx.width, aom_input_ctx.height, 0); if (do_md5) MD5Init(&md5_ctx); else outfile = open_outfile(outfile_name); } if (use_y4m && !noblit) { if (!single_file) { fprintf(stderr, "YUV4MPEG2 not supported with output patterns," " try --i420 or --yv12 or --rawvideo.\n"); return EXIT_FAILURE; } #if CONFIG_WEBM_IO if (aom_input_ctx.file_type == FILE_TYPE_WEBM) { if (webm_guess_framerate(input.webm_ctx, input.aom_input_ctx)) { fprintf(stderr, "Failed to guess framerate -- error parsing " "webm file?\n"); return EXIT_FAILURE; } } #endif } fourcc_interface = get_aom_decoder_by_fourcc(aom_input_ctx.fourcc); if (interface && fourcc_interface && interface != fourcc_interface) warn("Header indicates codec: %s\n", fourcc_interface->name); else interface = fourcc_interface; if (!interface) interface = get_aom_decoder_by_index(0); dec_flags = (postproc ? AOM_CODEC_USE_POSTPROC : 0) | (ec_enabled ? AOM_CODEC_USE_ERROR_CONCEALMENT : 0) | (frame_parallel ? AOM_CODEC_USE_FRAME_THREADING : 0); if (aom_codec_dec_init(&decoder, interface->codec_interface(), &cfg, dec_flags)) { fprintf(stderr, "Failed to initialize decoder: %s\n", aom_codec_error(&decoder)); goto fail2; } if (!quiet) fprintf(stderr, "%s\n", decoder.name); #if CONFIG_AV1_DECODER && CONFIG_EXT_TILE if (aom_codec_control(&decoder, AV1_SET_DECODE_TILE_ROW, tile_row)) { fprintf(stderr, "Failed to set decode_tile_row: %s\n", aom_codec_error(&decoder)); goto fail; } if (aom_codec_control(&decoder, AV1_SET_DECODE_TILE_COL, tile_col)) { fprintf(stderr, "Failed to set decode_tile_col: %s\n", aom_codec_error(&decoder)); goto fail; } #endif if (arg_skip) fprintf(stderr, "Skipping first %d frames.\n", arg_skip); while (arg_skip) { if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) break; arg_skip--; } if (num_external_frame_buffers > 0) { ext_fb_list.num_external_frame_buffers = num_external_frame_buffers; ext_fb_list.ext_fb = (struct ExternalFrameBuffer *)calloc( num_external_frame_buffers, sizeof(*ext_fb_list.ext_fb)); if (aom_codec_set_frame_buffer_functions(&decoder, get_av1_frame_buffer, release_av1_frame_buffer, &ext_fb_list)) { fprintf(stderr, "Failed to configure external frame buffers: %s\n", aom_codec_error(&decoder)); goto fail; } } frame_avail = 1; got_data = 0; if (framestats_file) fprintf(framestats_file, "bytes,qp\r\n"); /* Decode file */ while (frame_avail || got_data) { aom_codec_iter_t iter = NULL; aom_image_t *img; struct aom_usec_timer timer; int corrupted = 0; frame_avail = 0; if (!stop_after || frame_in < stop_after) { if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) { frame_avail = 1; frame_in++; aom_usec_timer_start(&timer); if (aom_codec_decode(&decoder, buf, (unsigned int)bytes_in_buffer, NULL, 0)) { const char *detail = aom_codec_error_detail(&decoder); warn("Failed to decode frame %d: %s", frame_in, aom_codec_error(&decoder)); if (detail) warn("Additional information: %s", detail); if (!keep_going) goto fail; } if (framestats_file) { int qp; if (aom_codec_control(&decoder, AOMD_GET_LAST_QUANTIZER, &qp)) { warn("Failed AOMD_GET_LAST_QUANTIZER: %s", aom_codec_error(&decoder)); if (!keep_going) goto fail; } fprintf(framestats_file, "%d,%d\r\n", (int)bytes_in_buffer, qp); } aom_usec_timer_mark(&timer); dx_time += aom_usec_timer_elapsed(&timer); } else { flush_decoder = 1; } } else { flush_decoder = 1; } aom_usec_timer_start(&timer); if (flush_decoder) { // Flush the decoder in frame parallel decode. if (aom_codec_decode(&decoder, NULL, 0, NULL, 0)) { warn("Failed to flush decoder: %s", aom_codec_error(&decoder)); } } got_data = 0; if ((img = aom_codec_get_frame(&decoder, &iter))) { ++frame_out; got_data = 1; } aom_usec_timer_mark(&timer); dx_time += (unsigned int)aom_usec_timer_elapsed(&timer); if (!frame_parallel && aom_codec_control(&decoder, AOMD_GET_FRAME_CORRUPTED, &corrupted)) { warn("Failed AOM_GET_FRAME_CORRUPTED: %s", aom_codec_error(&decoder)); if (!keep_going) goto fail; } frames_corrupted += corrupted; if (progress) show_progress(frame_in, frame_out, dx_time); if (!noblit && img) { const int PLANES_YUV[] = { AOM_PLANE_Y, AOM_PLANE_U, AOM_PLANE_V }; const int PLANES_YVU[] = { AOM_PLANE_Y, AOM_PLANE_V, AOM_PLANE_U }; const int *planes = flipuv ? PLANES_YVU : PLANES_YUV; if (do_scale) { if (frame_out == 1) { // If the output frames are to be scaled to a fixed display size then // use the width and height specified in the container. If either of // these is set to 0, use the display size set in the first frame // header. If that is unavailable, use the raw decoded size of the // first decoded frame. int render_width = aom_input_ctx.width; int render_height = aom_input_ctx.height; if (!render_width || !render_height) { int render_size[2]; if (aom_codec_control(&decoder, AV1D_GET_DISPLAY_SIZE, render_size)) { // As last resort use size of first frame as display size. render_width = img->d_w; render_height = img->d_h; } else { render_width = render_size[0]; render_height = render_size[1]; } } scaled_img = aom_img_alloc(NULL, img->fmt, render_width, render_height, 16); scaled_img->bit_depth = img->bit_depth; } if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) { #if CONFIG_LIBYUV libyuv_scale(img, scaled_img, kFilterBox); img = scaled_img; #else fprintf(stderr, "Failed to scale output frame: %s.\n" "Scaling is disabled in this configuration. " "To enable scaling, configure with --enable-libyuv\n", aom_codec_error(&decoder)); goto fail; #endif } } #if CONFIG_AOM_HIGHBITDEPTH // Default to codec bit depth if output bit depth not set if (!output_bit_depth && single_file && !do_md5) { output_bit_depth = img->bit_depth; } // Shift up or down if necessary if (output_bit_depth != 0 && output_bit_depth != img->bit_depth) { const aom_img_fmt_t shifted_fmt = output_bit_depth == 8 ? img->fmt ^ (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) : img->fmt | AOM_IMG_FMT_HIGHBITDEPTH; if (img_shifted && img_shifted_realloc_required(img, img_shifted, shifted_fmt)) { aom_img_free(img_shifted); img_shifted = NULL; } if (!img_shifted) { img_shifted = aom_img_alloc(NULL, shifted_fmt, img->d_w, img->d_h, 16); img_shifted->bit_depth = output_bit_depth; } if (output_bit_depth > img->bit_depth) { aom_img_upshift(img_shifted, img, output_bit_depth - img->bit_depth); } else { aom_img_downshift(img_shifted, img, img->bit_depth - output_bit_depth); } img = img_shifted; } #endif #if CONFIG_EXT_TILE aom_input_ctx.width = img->d_w; aom_input_ctx.height = img->d_h; #endif // CONFIG_EXT_TILE if (single_file) { if (use_y4m) { char y4m_buf[Y4M_BUFFER_SIZE] = { 0 }; size_t len = 0; if (img->fmt == AOM_IMG_FMT_I440 || img->fmt == AOM_IMG_FMT_I44016) { fprintf(stderr, "Cannot produce y4m output for 440 sampling.\n"); goto fail; } if (frame_out == 1) { // Y4M file header len = y4m_write_file_header( y4m_buf, sizeof(y4m_buf), aom_input_ctx.width, aom_input_ctx.height, &aom_input_ctx.framerate, img->fmt, img->bit_depth); if (do_md5) { MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len); } else { fputs(y4m_buf, outfile); } } // Y4M frame header len = y4m_write_frame_header(y4m_buf, sizeof(y4m_buf)); if (do_md5) { MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len); } else { fputs(y4m_buf, outfile); } } else { if (frame_out == 1) { // Check if --yv12 or --i420 options are consistent with the // bit-stream decoded if (opt_i420) { if (img->fmt != AOM_IMG_FMT_I420 && img->fmt != AOM_IMG_FMT_I42016) { fprintf(stderr, "Cannot produce i420 output for bit-stream.\n"); goto fail; } } if (opt_yv12) { if ((img->fmt != AOM_IMG_FMT_I420 && img->fmt != AOM_IMG_FMT_YV12) || img->bit_depth != 8) { fprintf(stderr, "Cannot produce yv12 output for bit-stream.\n"); goto fail; } } } } if (do_md5) { update_image_md5(img, planes, &md5_ctx); } else { write_image_file(img, planes, outfile); } } else { generate_filename(outfile_pattern, outfile_name, PATH_MAX, img->d_w, img->d_h, frame_in); if (do_md5) { MD5Init(&md5_ctx); update_image_md5(img, planes, &md5_ctx); MD5Final(md5_digest, &md5_ctx); print_md5(md5_digest, outfile_name); } else { outfile = open_outfile(outfile_name); write_image_file(img, planes, outfile); fclose(outfile); } } } } if (summary || progress) { show_progress(frame_in, frame_out, dx_time); fprintf(stderr, "\n"); } if (frames_corrupted) { fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted); } else { ret = EXIT_SUCCESS; } fail: if (aom_codec_destroy(&decoder)) { fprintf(stderr, "Failed to destroy decoder: %s\n", aom_codec_error(&decoder)); } fail2: if (!noblit && single_file) { if (do_md5) { MD5Final(md5_digest, &md5_ctx); print_md5(md5_digest, outfile_name); } else { fclose(outfile); } } #if CONFIG_WEBM_IO if (input.aom_input_ctx->file_type == FILE_TYPE_WEBM) webm_free(input.webm_ctx); #endif if (input.aom_input_ctx->file_type != FILE_TYPE_WEBM) free(buf); if (scaled_img) aom_img_free(scaled_img); #if CONFIG_AOM_HIGHBITDEPTH if (img_shifted) aom_img_free(img_shifted); #endif for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) { free(ext_fb_list.ext_fb[i].data); } free(ext_fb_list.ext_fb); fclose(infile); if (framestats_file) fclose(framestats_file); free(argv); return ret; }
int main(int argc, char **argv) { aom_codec_ctx_t codec; AvxVideoReader *reader = NULL; AvxVideoWriter *writer = NULL; const AvxInterface *decoder = NULL; const AvxVideoInfo *info = NULL; int num_references; int i; aom_codec_pts_t pts; const char *tile_list_file = NULL; exec_name = argv[0]; if (argc != 5) die("Invalid number of arguments."); reader = aom_video_reader_open(argv[1]); if (!reader) die("Failed to open %s for reading.", argv[1]); num_references = (int)strtol(argv[3], NULL, 0); info = aom_video_reader_get_info(reader); // The writer to write out ivf file in tile list OBU, which can be decoded by // AV1 decoder. writer = aom_video_writer_open(argv[2], kContainerIVF, info); if (!writer) die("Failed to open %s for writing", argv[2]); tile_list_file = argv[4]; decoder = get_aom_decoder_by_fourcc(info->codec_fourcc); if (!decoder) die("Unknown input codec."); 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."); // Decode anchor frames. aom_codec_control_(&codec, AV1_SET_TILE_MODE, 0); printf("Reading %d reference images.\n", num_references); for (i = 0; i < num_references; ++i) { aom_video_reader_read_frame(reader); size_t frame_size = 0; const unsigned char *frame = aom_video_reader_get_frame(reader, &frame_size); pts = (aom_codec_pts_t)aom_video_reader_get_frame_pts(reader); // Copy references bitstream directly. if (!aom_video_writer_write_frame(writer, frame, frame_size, pts)) die_codec(&codec, "Failed to copy compressed anchor frame."); if (aom_codec_decode(&codec, frame, frame_size, NULL)) die_codec(&codec, "Failed to decode frame."); } // Decode camera frames. aom_codec_control_(&codec, AV1_SET_TILE_MODE, 1); aom_codec_control_(&codec, AV1D_EXT_TILE_DEBUG, 1); FILE *infile = aom_video_reader_get_file(reader); // Record the offset of the first camera image. const FileOffset camera_frame_pos = ftello(infile); printf("Loading compressed frames into memory.\n"); // Count the frames in the lightfield. int num_frames = 0; while (aom_video_reader_read_frame(reader)) { ++num_frames; } if (num_frames < 1) die("Input light field has no frames."); // Read all of the lightfield frames into memory. unsigned char **frames = (unsigned char **)malloc(num_frames * sizeof(unsigned char *)); size_t *frame_sizes = (size_t *)malloc(num_frames * sizeof(size_t)); // Seek to the first camera image. fseeko(infile, camera_frame_pos, SEEK_SET); for (int f = 0; f < num_frames; ++f) { aom_video_reader_read_frame(reader); size_t frame_size = 0; const unsigned char *frame = aom_video_reader_get_frame(reader, &frame_size); frames[f] = (unsigned char *)malloc(frame_size * sizeof(unsigned char)); memcpy(frames[f], frame, frame_size); frame_sizes[f] = frame_size; } printf("Read %d frames.\n", num_frames); // Copy first camera frame for getting camera frame header. This is done // only once. { size_t frame_size = frame_sizes[0]; const unsigned char *frame = frames[0]; pts = num_references; aom_tile_data frame_header_info = { 0, NULL, 0 }; // Need to decode frame header to get camera frame header info. So, here // decoding 1 tile is enough. aom_codec_control_(&codec, AV1_SET_DECODE_TILE_ROW, 0); aom_codec_control_(&codec, AV1_SET_DECODE_TILE_COL, 0); aom_codec_err_t aom_status = aom_codec_decode(&codec, frame, frame_size, NULL); if (aom_status) die_codec(&codec, "Failed to decode tile."); aom_codec_control_(&codec, AV1D_GET_FRAME_HEADER_INFO, &frame_header_info); size_t obu_size_offset = (uint8_t *)frame_header_info.coded_tile_data - frame; size_t length_field_size = frame_header_info.coded_tile_data_size; // Remove ext-tile tile info. uint32_t frame_header_size = (uint32_t)frame_header_info.extra_size - 1; size_t bytes_to_copy = obu_size_offset + length_field_size + frame_header_size; unsigned char *frame_hdr_buf = (unsigned char *)malloc(bytes_to_copy); if (frame_hdr_buf == NULL) die_codec(&codec, "Failed to allocate frame header buffer."); memcpy(frame_hdr_buf, frame, bytes_to_copy); // Update frame header OBU size. size_t bytes_written = 0; if (aom_uleb_encode_fixed_size( frame_header_size, length_field_size, length_field_size, frame_hdr_buf + obu_size_offset, &bytes_written)) die_codec(&codec, "Failed to encode the tile list obu size."); // Copy camera frame header bitstream. if (!aom_video_writer_write_frame(writer, frame_hdr_buf, bytes_to_copy, pts)) die_codec(&codec, "Failed to copy compressed camera frame header."); free(frame_hdr_buf); } // Read out the image format. aom_img_fmt_t ref_fmt = 0; if (aom_codec_control(&codec, AV1D_GET_IMG_FORMAT, &ref_fmt)) die_codec(&codec, "Failed to get the image format"); const int bps = get_image_bps(ref_fmt); if (!bps) die_codec(&codec, "Invalid image format."); // read out the tile size. unsigned int tile_size = 0; if (aom_codec_control(&codec, AV1D_GET_TILE_SIZE, &tile_size)) die_codec(&codec, "Failed to get the tile size"); const unsigned int tile_width = tile_size >> 16; const unsigned int tile_height = tile_size & 65535; // Allocate a buffer to store tile list bitstream. const size_t data_sz = MAX_TILES * ALIGN_POWER_OF_TWO(tile_width, 5) * ALIGN_POWER_OF_TWO(tile_height, 5) * bps / 8; unsigned char *tl_buf = (unsigned char *)malloc(data_sz); if (tl_buf == NULL) die_codec(&codec, "Failed to allocate tile list buffer."); aom_codec_pts_t tl_pts = num_references; const uint8_t output_frame_width_in_tiles_minus_1 = output_frame_width / tile_width - 1; const uint8_t output_frame_height_in_tiles_minus_1 = output_frame_height / tile_height - 1; printf("Reading tile list from file.\n"); char line[1024]; FILE *tile_list_fptr = fopen(tile_list_file, "r"); if (!tile_list_fptr) die_codec(&codec, "Failed to open tile list file."); int num_tiles = 0; TILE_LIST_INFO tiles[MAX_TILES]; while ((fgets(line, 1024, tile_list_fptr)) != NULL) { if (line[0] == 'F' || num_tiles >= MAX_TILES) { // Flush existing tile list and start another, either because we hit a // new render frame or because we've hit our max number of tiles per list. if (num_tiles > 0) { process_tile_list(tiles, num_tiles, tl_pts, frames, frame_sizes, &codec, tl_buf, writer, output_frame_width_in_tiles_minus_1, output_frame_height_in_tiles_minus_1); ++tl_pts; } num_tiles = 0; } if (line[0] == 'F') { continue; } if (sscanf(line, "%d %d %d %d", &tiles[num_tiles].image_idx, &tiles[num_tiles].reference_idx, &tiles[num_tiles].tile_col, &tiles[num_tiles].tile_row) == 4) { if (tiles[num_tiles].image_idx >= num_frames) { die("Tile list image_idx out of bounds: %d >= %d.", tiles[num_tiles].image_idx, num_frames); } if (tiles[num_tiles].reference_idx >= num_references) { die("Tile list reference_idx out of bounds: %d >= %d.", tiles[num_tiles].reference_idx, num_references); } ++num_tiles; } } if (num_tiles > 0) { // Flush out the last tile list. process_tile_list(tiles, num_tiles, tl_pts, frames, frame_sizes, &codec, tl_buf, writer, output_frame_width_in_tiles_minus_1, output_frame_height_in_tiles_minus_1); ++tl_pts; } const int num_tile_lists = (int)(tl_pts - pts); printf("Finished processing tile lists. Num tile lists: %d.\n", num_tile_lists); free(tl_buf); for (int f = 0; f < num_frames; ++f) { free(frames[f]); } free(frame_sizes); free(frames); if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec"); aom_video_writer_close(writer); aom_video_reader_close(reader); return EXIT_SUCCESS; }
static int main_loop(int argc, const char **argv_) { aom_codec_ctx_t decoder; char *fn = NULL; int i; int ret = EXIT_FAILURE; uint8_t *buf = NULL; size_t bytes_in_buffer = 0, buffer_size = 0; FILE *infile; int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0; int do_md5 = 0, progress = 0; int stop_after = 0, postproc = 0, summary = 0, quiet = 1; int arg_skip = 0; int keep_going = 0; const AvxInterface *interface = NULL; const AvxInterface *fourcc_interface = NULL; uint64_t dx_time = 0; struct arg arg; char **argv, **argi, **argj; int single_file; int use_y4m = 1; int opt_yv12 = 0; int opt_i420 = 0; int opt_raw = 0; aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH, { 1 } }; unsigned int fixed_output_bit_depth = 0; unsigned int is_annexb = 0; int frames_corrupted = 0; int dec_flags = 0; int do_scale = 0; int operating_point = 0; int output_all_layers = 0; int skip_film_grain = 0; aom_image_t *scaled_img = NULL; aom_image_t *img_shifted = NULL; int frame_avail, got_data, flush_decoder = 0; int num_external_frame_buffers = 0; struct ExternalFrameBufferList ext_fb_list = { 0, NULL }; const char *outfile_pattern = NULL; char outfile_name[PATH_MAX] = { 0 }; FILE *outfile = NULL; FILE *framestats_file = NULL; MD5Context md5_ctx; unsigned char md5_digest[16]; struct AvxDecInputContext input = { NULL, NULL, NULL }; struct AvxInputContext aom_input_ctx; memset(&aom_input_ctx, 0, sizeof(aom_input_ctx)); #if CONFIG_WEBM_IO struct WebmInputContext webm_ctx; memset(&webm_ctx, 0, sizeof(webm_ctx)); input.webm_ctx = &webm_ctx; #endif struct ObuDecInputContext obu_ctx = { NULL, NULL, 0, 0, 0 }; obu_ctx.avx_ctx = &aom_input_ctx; input.obu_ctx = &obu_ctx; input.aom_input_ctx = &aom_input_ctx; /* Parse command line */ exec_name = argv_[0]; argv = argv_dup(argc - 1, argv_ + 1); for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { memset(&arg, 0, sizeof(arg)); arg.argv_step = 1; if (arg_match(&arg, &help, argi)) { show_help(stdout, 0); exit(EXIT_SUCCESS); } else if (arg_match(&arg, &codecarg, argi)) { interface = get_aom_decoder_by_name(arg.val); if (!interface) die("Error: Unrecognized argument (%s) to --codec\n", arg.val); } else if (arg_match(&arg, &looparg, argi)) { // no-op } else if (arg_match(&arg, &outputfile, argi)) { outfile_pattern = arg.val; } else if (arg_match(&arg, &use_yv12, argi)) { use_y4m = 0; flipuv = 1; opt_yv12 = 1; opt_i420 = 0; opt_raw = 0; } else if (arg_match(&arg, &use_i420, argi)) { use_y4m = 0; flipuv = 0; opt_yv12 = 0; opt_i420 = 1; opt_raw = 0; } else if (arg_match(&arg, &rawvideo, argi)) { use_y4m = 0; opt_yv12 = 0; opt_i420 = 0; opt_raw = 1; } else if (arg_match(&arg, &flipuvarg, argi)) { flipuv = 1; } else if (arg_match(&arg, &noblitarg, argi)) { noblit = 1; } else if (arg_match(&arg, &progressarg, argi)) { progress = 1; } else if (arg_match(&arg, &limitarg, argi)) { stop_after = arg_parse_uint(&arg); } else if (arg_match(&arg, &skiparg, argi)) { arg_skip = arg_parse_uint(&arg); } else if (arg_match(&arg, &postprocarg, argi)) { postproc = 1; } else if (arg_match(&arg, &md5arg, argi)) { do_md5 = 1; } else if (arg_match(&arg, &framestatsarg, argi)) { framestats_file = fopen(arg.val, "w"); if (!framestats_file) { die("Error: Could not open --framestats file (%s) for writing.\n", arg.val); } } else if (arg_match(&arg, &summaryarg, argi)) { summary = 1; } else if (arg_match(&arg, &threadsarg, argi)) { cfg.threads = arg_parse_uint(&arg); #if !CONFIG_MULTITHREAD if (cfg.threads > 1) { die("Error: --threads=%d is not supported when CONFIG_MULTITHREAD = " "0.\n", cfg.threads); } #endif } else if (arg_match(&arg, &verbosearg, argi)) { quiet = 0; } else if (arg_match(&arg, &scalearg, argi)) { do_scale = 1; } else if (arg_match(&arg, &fb_arg, argi)) { num_external_frame_buffers = arg_parse_uint(&arg); } else if (arg_match(&arg, &continuearg, argi)) { keep_going = 1; } else if (arg_match(&arg, &outbitdeptharg, argi)) { fixed_output_bit_depth = arg_parse_uint(&arg); } else if (arg_match(&arg, &isannexb, argi)) { is_annexb = 1; input.obu_ctx->is_annexb = 1; } else if (arg_match(&arg, &oppointarg, argi)) { operating_point = arg_parse_int(&arg); } else if (arg_match(&arg, &outallarg, argi)) { output_all_layers = 1; } else if (arg_match(&arg, &skipfilmgrain, argi)) { skip_film_grain = 1; } else { argj++; } } /* Check for unrecognized options */ for (argi = argv; *argi; argi++) if (argi[0][0] == '-' && strlen(argi[0]) > 1) die("Error: Unrecognized option %s\n", *argi); /* Handle non-option arguments */ fn = argv[0]; if (!fn) { free(argv); fprintf(stderr, "No input file specified!\n"); usage_exit(); } /* Open file */ infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin); if (!infile) { fatal("Failed to open input file '%s'", strcmp(fn, "-") ? fn : "stdin"); } #if CONFIG_OS_SUPPORT /* Make sure we don't dump to the terminal, unless forced to with -o - */ if (!outfile_pattern && isatty(STDOUT_FILENO) && !do_md5 && !noblit) { fprintf(stderr, "Not dumping raw video to your terminal. Use '-o -' to " "override.\n"); return EXIT_FAILURE; } #endif input.aom_input_ctx->filename = fn; input.aom_input_ctx->file = infile; if (file_is_ivf(input.aom_input_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_IVF; #if CONFIG_WEBM_IO else if (file_is_webm(input.webm_ctx, input.aom_input_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_WEBM; #endif else if (file_is_obu(&obu_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_OBU; else if (file_is_raw(input.aom_input_ctx)) input.aom_input_ctx->file_type = FILE_TYPE_RAW; else { fprintf(stderr, "Unrecognized input file type.\n"); #if !CONFIG_WEBM_IO fprintf(stderr, "aomdec was built without WebM container support.\n"); #endif return EXIT_FAILURE; } outfile_pattern = outfile_pattern ? outfile_pattern : "-"; single_file = is_single_file(outfile_pattern); if (!noblit && single_file) { generate_filename(outfile_pattern, outfile_name, PATH_MAX, aom_input_ctx.width, aom_input_ctx.height, 0); if (do_md5) MD5Init(&md5_ctx); else outfile = open_outfile(outfile_name); } if (use_y4m && !noblit) { if (!single_file) { fprintf(stderr, "YUV4MPEG2 not supported with output patterns," " try --i420 or --yv12 or --rawvideo.\n"); return EXIT_FAILURE; } #if CONFIG_WEBM_IO if (aom_input_ctx.file_type == FILE_TYPE_WEBM) { if (webm_guess_framerate(input.webm_ctx, input.aom_input_ctx)) { fprintf(stderr, "Failed to guess framerate -- error parsing " "webm file?\n"); return EXIT_FAILURE; } } #endif } fourcc_interface = get_aom_decoder_by_fourcc(aom_input_ctx.fourcc); if (interface && fourcc_interface && interface != fourcc_interface) warn("Header indicates codec: %s\n", fourcc_interface->name); else interface = fourcc_interface; if (!interface) interface = get_aom_decoder_by_index(0); dec_flags = (postproc ? AOM_CODEC_USE_POSTPROC : 0); if (aom_codec_dec_init(&decoder, interface->codec_interface(), &cfg, dec_flags)) { fprintf(stderr, "Failed to initialize decoder: %s\n", aom_codec_error(&decoder)); goto fail2; } if (!quiet) fprintf(stderr, "%s\n", decoder.name); if (aom_codec_control(&decoder, AV1D_SET_IS_ANNEXB, is_annexb)) { fprintf(stderr, "Failed to set is_annexb: %s\n", aom_codec_error(&decoder)); goto fail; } if (aom_codec_control(&decoder, AV1D_SET_OPERATING_POINT, operating_point)) { fprintf(stderr, "Failed to set operating_point: %s\n", aom_codec_error(&decoder)); goto fail; } if (aom_codec_control(&decoder, AV1D_SET_OUTPUT_ALL_LAYERS, output_all_layers)) { fprintf(stderr, "Failed to set output_all_layers: %s\n", aom_codec_error(&decoder)); goto fail; } if (aom_codec_control(&decoder, AV1D_SET_SKIP_FILM_GRAIN, skip_film_grain)) { fprintf(stderr, "Failed to set skip_film_grain: %s\n", aom_codec_error(&decoder)); goto fail; } if (arg_skip) fprintf(stderr, "Skipping first %d frames.\n", arg_skip); while (arg_skip) { if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) break; arg_skip--; } if (num_external_frame_buffers > 0) { ext_fb_list.num_external_frame_buffers = num_external_frame_buffers; ext_fb_list.ext_fb = (struct ExternalFrameBuffer *)calloc( num_external_frame_buffers, sizeof(*ext_fb_list.ext_fb)); if (aom_codec_set_frame_buffer_functions(&decoder, get_av1_frame_buffer, release_av1_frame_buffer, &ext_fb_list)) { fprintf(stderr, "Failed to configure external frame buffers: %s\n", aom_codec_error(&decoder)); goto fail; } } frame_avail = 1; got_data = 0; if (framestats_file) fprintf(framestats_file, "bytes,qp\r\n"); /* Decode file */ while (frame_avail || got_data) { aom_codec_iter_t iter = NULL; aom_image_t *img; struct aom_usec_timer timer; int corrupted = 0; frame_avail = 0; if (!stop_after || frame_in < stop_after) { if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) { frame_avail = 1; frame_in++; aom_usec_timer_start(&timer); if (aom_codec_decode(&decoder, buf, bytes_in_buffer, NULL)) { const char *detail = aom_codec_error_detail(&decoder); warn("Failed to decode frame %d: %s", frame_in, aom_codec_error(&decoder)); if (detail) warn("Additional information: %s", detail); if (!keep_going) goto fail; } if (framestats_file) { int qp; if (aom_codec_control(&decoder, AOMD_GET_LAST_QUANTIZER, &qp)) { warn("Failed AOMD_GET_LAST_QUANTIZER: %s", aom_codec_error(&decoder)); if (!keep_going) goto fail; } fprintf(framestats_file, "%d,%d\r\n", (int)bytes_in_buffer, qp); } aom_usec_timer_mark(&timer); dx_time += aom_usec_timer_elapsed(&timer); } else { flush_decoder = 1; } } else { flush_decoder = 1; } aom_usec_timer_start(&timer); if (flush_decoder) { // Flush the decoder. if (aom_codec_decode(&decoder, NULL, 0, NULL)) { warn("Failed to flush decoder: %s", aom_codec_error(&decoder)); } } aom_usec_timer_mark(&timer); dx_time += aom_usec_timer_elapsed(&timer); got_data = 0; while ((img = aom_codec_get_frame(&decoder, &iter))) { ++frame_out; got_data = 1; if (aom_codec_control(&decoder, AOMD_GET_FRAME_CORRUPTED, &corrupted)) { warn("Failed AOM_GET_FRAME_CORRUPTED: %s", aom_codec_error(&decoder)); if (!keep_going) goto fail; } frames_corrupted += corrupted; if (progress) show_progress(frame_in, frame_out, dx_time); if (!noblit) { const int PLANES_YUV[] = { AOM_PLANE_Y, AOM_PLANE_U, AOM_PLANE_V }; const int PLANES_YVU[] = { AOM_PLANE_Y, AOM_PLANE_V, AOM_PLANE_U }; const int *planes = flipuv ? PLANES_YVU : PLANES_YUV; if (do_scale) { if (frame_out == 1) { // If the output frames are to be scaled to a fixed display size // then use the width and height specified in the container. If // either of these is set to 0, use the display size set in the // first frame header. If that is unavailable, use the raw decoded // size of the first decoded frame. int render_width = aom_input_ctx.width; int render_height = aom_input_ctx.height; if (!render_width || !render_height) { int render_size[2]; if (aom_codec_control(&decoder, AV1D_GET_DISPLAY_SIZE, render_size)) { // As last resort use size of first frame as display size. render_width = img->d_w; render_height = img->d_h; } else { render_width = render_size[0]; render_height = render_size[1]; } } scaled_img = aom_img_alloc(NULL, img->fmt, render_width, render_height, 16); scaled_img->bit_depth = img->bit_depth; scaled_img->monochrome = img->monochrome; scaled_img->csp = img->csp; } if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) { #if CONFIG_LIBYUV libyuv_scale(img, scaled_img, kFilterBox); img = scaled_img; #else fprintf( stderr, "Failed to scale output frame: %s.\n" "libyuv is required for scaling but is currently disabled.\n" "Be sure to specify -DCONFIG_LIBYUV=1 when running cmake.\n", aom_codec_error(&decoder)); goto fail; #endif } } // Default to codec bit depth if output bit depth not set unsigned int output_bit_depth; if (!fixed_output_bit_depth && single_file && !do_md5) { output_bit_depth = img->bit_depth; } else { output_bit_depth = fixed_output_bit_depth; } // Shift up or down if necessary if (output_bit_depth != 0) aom_shift_img(output_bit_depth, &img, &img_shifted); aom_input_ctx.width = img->d_w; aom_input_ctx.height = img->d_h; int num_planes = (opt_raw && img->monochrome) ? 1 : 3; if (single_file) { if (use_y4m) { char y4m_buf[Y4M_BUFFER_SIZE] = { 0 }; size_t len = 0; if (frame_out == 1) { // Y4M file header len = y4m_write_file_header( y4m_buf, sizeof(y4m_buf), aom_input_ctx.width, aom_input_ctx.height, &aom_input_ctx.framerate, img->monochrome, img->csp, img->fmt, img->bit_depth); if (img->csp == AOM_CSP_COLOCATED) { fprintf(stderr, "Warning: Y4M lacks a colorspace for colocated " "chroma. Using a placeholder.\n"); } if (do_md5) { MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len); } else { fputs(y4m_buf, outfile); } } // Y4M frame header len = y4m_write_frame_header(y4m_buf, sizeof(y4m_buf)); if (do_md5) { MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len); y4m_update_image_md5(img, planes, &md5_ctx); } else { fputs(y4m_buf, outfile); y4m_write_image_file(img, planes, outfile); } } else { if (frame_out == 1) { // Check if --yv12 or --i420 options are consistent with the // bit-stream decoded if (opt_i420) { if (img->fmt != AOM_IMG_FMT_I420 && img->fmt != AOM_IMG_FMT_I42016) { fprintf(stderr, "Cannot produce i420 output for bit-stream.\n"); goto fail; } } if (opt_yv12) { if ((img->fmt != AOM_IMG_FMT_I420 && img->fmt != AOM_IMG_FMT_YV12) || img->bit_depth != 8) { fprintf(stderr, "Cannot produce yv12 output for bit-stream.\n"); goto fail; } } } if (do_md5) { raw_update_image_md5(img, planes, num_planes, &md5_ctx); } else { raw_write_image_file(img, planes, num_planes, outfile); } } } else { generate_filename(outfile_pattern, outfile_name, PATH_MAX, img->d_w, img->d_h, frame_in); if (do_md5) { MD5Init(&md5_ctx); if (use_y4m) { y4m_update_image_md5(img, planes, &md5_ctx); } else { raw_update_image_md5(img, planes, num_planes, &md5_ctx); } MD5Final(md5_digest, &md5_ctx); print_md5(md5_digest, outfile_name); } else { outfile = open_outfile(outfile_name); if (use_y4m) { y4m_write_image_file(img, planes, outfile); } else { raw_write_image_file(img, planes, num_planes, outfile); } fclose(outfile); } } } } } if (summary || progress) { show_progress(frame_in, frame_out, dx_time); fprintf(stderr, "\n"); } if (frames_corrupted) { fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted); } else { ret = EXIT_SUCCESS; } fail: if (aom_codec_destroy(&decoder)) { fprintf(stderr, "Failed to destroy decoder: %s\n", aom_codec_error(&decoder)); } fail2: if (!noblit && single_file) { if (do_md5) { MD5Final(md5_digest, &md5_ctx); print_md5(md5_digest, outfile_name); } else { fclose(outfile); } } #if CONFIG_WEBM_IO if (input.aom_input_ctx->file_type == FILE_TYPE_WEBM) webm_free(input.webm_ctx); #endif if (input.aom_input_ctx->file_type == FILE_TYPE_OBU) obudec_free(input.obu_ctx); if (input.aom_input_ctx->file_type != FILE_TYPE_WEBM) free(buf); if (scaled_img) aom_img_free(scaled_img); if (img_shifted) aom_img_free(img_shifted); for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) { free(ext_fb_list.ext_fb[i].data); } free(ext_fb_list.ext_fb); fclose(infile); if (framestats_file) fclose(framestats_file); free(argv); return ret; }