blob: 181a65df1f3911dd9a32e3953a7920f4ce6863e8 [file] [log] [blame]
/*
* Copyright (c) 2021, 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.
*/
#include "aom/aom_codec.h"
#include "aom/aomdx.h"
#include "av1/encoder/firstpass.h"
#include "av1/encoder/thirdpass.h"
#include "av1/common/blockd.h"
#include "common/video_reader.h"
static int open_file(THIRD_PASS_DEC_CTX *ctx) {
if (ctx->file == NULL) {
die("No third pass input specified. ");
}
ctx->reader = aom_video_reader_open(ctx->file);
if (!ctx->reader) die("Failed to open %s for third pass.", ctx->file);
aom_codec_iface_t *decoder =
get_aom_decoder_by_fourcc(ctx->reader->info.codec_fourcc);
if (!decoder) die("Unknown input codec.");
if (aom_codec_dec_init(&ctx->codec, decoder, NULL, 0))
die("Failed to initialize decoder.");
return 0;
}
static int read_frame(THIRD_PASS_DEC_CTX *ctx) {
if (ctx->reader == NULL) {
open_file(ctx);
}
if (!ctx->have_frame) {
if (!aom_video_reader_read_frame(ctx->reader)) return EXIT_FAILURE;
ctx->frame = aom_video_reader_get_frame(ctx->reader, &ctx->frame_size);
ctx->end_frame = ctx->frame + ctx->frame_size;
ctx->have_frame = 1;
}
Av1DecodeReturn adr;
if (aom_codec_decode(&ctx->codec, ctx->frame, (unsigned int)ctx->frame_size,
&adr) != AOM_CODEC_OK) {
die_codec(&ctx->codec, "Failed to decode frame for third pass.");
}
ctx->frame = adr.buf;
ctx->frame_size = ctx->end_frame - ctx->frame;
if (ctx->frame == ctx->end_frame) ctx->have_frame = 0;
return 0;
}
static int get_frame_gop_info(THIRD_PASS_DEC_CTX *ctx) {
int cur = ctx->num_gop_info_left;
int frame_type_flags = 0;
if (aom_codec_control(&ctx->codec, AOMD_GET_FRAME_FLAGS, &frame_type_flags) !=
AOM_CODEC_OK) {
die("failed to read frame flags.");
}
if (frame_type_flags & AOM_FRAME_IS_KEY) {
ctx->frame_info[cur].frame_type = KEY_FRAME;
} else if (frame_type_flags & AOM_FRAME_IS_INTRAONLY) {
ctx->frame_info[cur].frame_type = INTRA_ONLY_FRAME;
} else if (frame_type_flags & AOM_FRAME_IS_SWITCH) {
ctx->frame_info[cur].frame_type = S_FRAME;
} else {
ctx->frame_info[cur].frame_type = INTER_FRAME;
}
if (aom_codec_control(&ctx->codec, AOMD_GET_BASE_Q_IDX,
&ctx->frame_info[cur].base_q_idx) != AOM_CODEC_OK) {
die("failed to read base q index.");
}
if (aom_codec_control(&ctx->codec, AOMD_GET_SHOW_EXISTING_FRAME_FLAG,
&ctx->frame_info[cur].is_show_existing) !=
AOM_CODEC_OK) {
die("failed to read show existing frame flag.");
}
if (aom_codec_control(&ctx->codec, AOMD_GET_SHOW_FRAME_FLAG,
&ctx->frame_info[cur].is_show_frame) != AOM_CODEC_OK) {
die("failed to read show frame flag.");
}
if (aom_codec_control(&ctx->codec, AOMD_GET_ORDER_HINT,
&ctx->frame_info[cur].order_hint) != AOM_CODEC_OK) {
die("failed to read order hint.");
}
ctx->num_gop_info_left++;
return 0;
}
// TODO(bohanli): define an enum for this return
// return :
// -1: failed
// 1: no arf
// 2: with arf
static int read_gop_frames(THIRD_PASS_DEC_CTX *ctx, int max_num,
int *last_idx) {
*last_idx = 0;
int cur_idx = 0;
int arf_order_hint = -1;
// int kf_offset = 0;
// int has_kf = 0;
int num_show_frames = 0;
while (num_show_frames < max_num) {
// read in from bitstream if needed
if (cur_idx >= ctx->num_gop_info_left) {
read_frame(ctx);
get_frame_gop_info(ctx);
}
// TODO(bohanli): verify that fwd_kf works here.
if (ctx->frame_info[cur_idx].frame_type == KEY_FRAME &&
ctx->frame_info[cur_idx].is_show_frame == 1) {
// if this is a key frame,
if (cur_idx != 0) {
// we have reached the next key frame. Stop here.
*last_idx = cur_idx - 1;
return 1;
}
} else if (ctx->frame_info[cur_idx].is_show_frame == 0 &&
arf_order_hint == -1) {
// if this is an arf (the first no show)
if (num_show_frames <= 1) {
// this is an arf and we should end the GOP with its overlay
arf_order_hint = ctx->frame_info[cur_idx].order_hint;
} else {
// we treat the frames previous to this arf as a gop.
*last_idx = cur_idx - 1;
return 1;
}
} else if (arf_order_hint >= 0 &&
ctx->frame_info[cur_idx].order_hint == arf_order_hint) {
// if this is the overlay/show existing of the arf
assert(ctx->frame_info[cur_idx].is_show_frame);
*last_idx = cur_idx;
return 2;
} else {
// this frame is part of the GOP.
if (ctx->frame_info[cur_idx].is_show_frame) num_show_frames++;
}
cur_idx++;
}
assert(arf_order_hint < 0);
*last_idx = max_num - 1;
return 1;
}
int av1_set_gop_third_pass(THIRD_PASS_DEC_CTX *ctx, GF_GROUP *gf_group) {
(void)gf_group;
// Read in future frames and find the last frame in the GOP
int last_idx;
read_gop_frames(ctx, 32, &last_idx);
// TODO(bohanli):Define and set the GOP structure
printf("\narf length is %d\n",
(ctx->frame_info[last_idx].order_hint - ctx->last_end_gop) % (1 << 7));
ctx->last_end_gop = ctx->frame_info[last_idx].order_hint;
// Reset the frame info ptr to the next frame.
ctx->num_gop_info_left -= (last_idx + 1);
for (int i = 0; i < ctx->num_gop_info_left; i++) {
ctx->frame_info[i] = ctx->frame_info[i + last_idx + 1];
}
return 0;
}
void av1_init_thirdpass_ctx(THIRD_PASS_DEC_CTX **ctx, char *file) {
if (*ctx == NULL) {
*ctx = aom_malloc(sizeof(**ctx));
}
THIRD_PASS_DEC_CTX *ctx_ptr = *ctx;
ctx_ptr->file = file;
ctx_ptr->reader = NULL;
ctx_ptr->num_gop_info_left = 0;
ctx_ptr->last_end_gop = -1;
ctx_ptr->have_frame = 0;
}
void av1_free_thirdpass_ctx(THIRD_PASS_DEC_CTX *ctx) {
if (ctx == NULL) return;
if (ctx->codec.iface && aom_codec_destroy(&ctx->codec)) {
die("Failded to destroy decoder codec for thirdpass.");
}
aom_video_reader_close(ctx->reader);
ctx->reader = NULL;
ctx->num_gop_info_left = 0;
aom_free(ctx);
}