aom: update libaom to 0ec86ac7ae1e32a7e70410fa4972a655ec3670a4

This commit is contained in:
Roy Tam 2019-02-22 21:52:01 +08:00
commit eb361970c5
438 changed files with 52661 additions and 21905 deletions

View file

@ -162,7 +162,7 @@ bool AV1Decoder::setInspectionCallback() {
void AV1Decoder::inspect(void *pbi, void *data) {
AV1Decoder *decoder = (AV1Decoder *)data;
ifd_inspect(&decoder->frame_data, pbi);
ifd_inspect(&decoder->frame_data, pbi, 0);
}
#define MIN_ZOOM (1)

View file

@ -141,9 +141,7 @@ static int encode_frame(aom_codec_ctx_t *ecodec, aom_image_t *img,
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
if (!(pkt->data.frame.flags & AOM_FRAME_IS_FRAGMENT)) {
*frame_out += 1;
}
++*frame_out;
if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf,
pkt->data.frame.sz,

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019, 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.
*/
/*
* See build_av1_dec_fuzzer.sh for building instructions.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include "config/aom_config.h"
#include "aom/aom_decoder.h"
#include "aom/aomdx.h"
#include "aom_ports/mem_ops.h"
#include "common/ivfdec.h"
static void close_file(FILE *file) { fclose(file); }
extern "C" void usage_exit(void) { exit(EXIT_FAILURE); }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::unique_ptr<FILE, decltype(&close_file)> file(
fmemopen((void *)data, size, "rb"), &close_file);
if (file == nullptr) {
return 0;
}
char header[32];
if (fread(header, 1, 32, file.get()) != 32) {
return 0;
}
const AvxInterface *decoder = get_aom_decoder_by_name("av1");
if (decoder == nullptr) {
return 0;
}
aom_codec_ctx_t codec;
// Set thread count in the range [1, 64].
const unsigned int threads = (header[0] & 0x3f) + 1;
aom_codec_dec_cfg_t cfg = { threads, 0, 0, CONFIG_LOWBITDEPTH };
if (aom_codec_dec_init(&codec, decoder->codec_interface(), &cfg, 0)) {
return 0;
}
uint8_t *buffer = nullptr;
size_t buffer_size = 0;
size_t frame_size = 0;
while (!ivf_read_frame(file.get(), &buffer, &frame_size, &buffer_size,
nullptr)) {
const aom_codec_err_t err =
aom_codec_decode(&codec, buffer, frame_size, nullptr);
static_cast<void>(err);
aom_codec_iter_t iter = nullptr;
aom_image_t *img = nullptr;
while ((img = aom_codec_get_frame(&codec, &iter)) != nullptr) {
}
}
aom_codec_destroy(&codec);
free(buffer);
return 0;
}

View file

@ -0,0 +1,78 @@
#!/bin/bash
#
# Copyright (c) 2019, 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.
#
###############################################################################
# Fuzzer for libaom decoder.
# ==========================
# Requirements
# ---------------------
# Clang6.0 or above (must support -fsanitize=fuzzer)
#
# References:
# ---------------------
# http://llvm.org/docs/LibFuzzer.html
# https://github.com/google/oss-fuzz
#
# Steps to build / run
# ---------------------
set -eu
# Have a copy of AOM and a build directory ready.
if [[ $# -ne 2 ]]; then
echo "Pass in the AOM source tree as first argument, and a build directory "
echo "as the second argument. The AOM source tree can be obtained via: "
echo " git clone https://aomedia.googlesource.com/aom"
exit 2
fi
if [[ -z "$CC" ]]; then
echo "Set the CC environment variable to point to your C compiler."
exit 2
fi
if [[ -z "$CXX" ]]; then
echo "Set the CXX environment variable to point to your C++ compiler."
exit 2
fi
AOM_DIR=$1
BUILD_DIR=$2
# Run CMake with address sanitizer enabled and build the codec.
# Enable DO_RANGE_CHECK_CLAMP to suppress the noise of integer overflows
# in the transform functions. Also set memory limits.
EXTRA_C_FLAGS='-DDO_RANGE_CHECK_CLAMP=1 -DAOM_MAX_ALLOCABLE_MEMORY=1073741824'
cd "${BUILD_DIR}"
cmake "${AOM_DIR}" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONFIG_PIC=1 \
-DCONFIG_SCALABILITY=0 -DCONFIG_LOWBITDEPTH=1 -DCONFIG_AV1_ENCODER=0 \
-DENABLE_EXAMPLES=0 -DENABLE_DOCS=0 -DENABLE_TESTS=0 -DCONFIG_SIZE_LIMIT=1 \
-DDECODE_HEIGHT_LIMIT=12288 -DDECODE_WIDTH_LIMIT=12288 \
-DAOM_EXTRA_C_FLAGS="${EXTRA_C_FLAGS}" \
-DAOM_EXTRA_CXX_FLAGS="${EXTRA_C_FLAGS}" -DSANITIZE=address
# Build the codec.
make -j$(nproc)
# Build some libaom utils that are not part of the core lib.
$CC -std=c99 -c -I${AOM_DIR} -I${BUILD_DIR} \
${AOM_DIR}/common/ivfdec.c -o ${BUILD_DIR}/ivfdec.o
$CC -std=c99 -c -I${AOM_DIR} -I${BUILD_DIR} \
${AOM_DIR}/common/tools_common.c -o ${BUILD_DIR}/tools_common.o
# Build the av1 fuzzer
$CXX -std=c++11 -DDECODER=av1 -I${AOM_DIR} -I${BUILD_DIR} \
-fsanitize=fuzzer -Wl,--start-group \
${AOM_DIR}/examples/av1_dec_fuzzer.cc -o ${BUILD_DIR}/av1_dec_fuzzer \
${BUILD_DIR}/libaom.a ${BUILD_DIR}/ivfdec.o ${BUILD_DIR}/tools_common.o \
-Wl,--end-group
echo "Fuzzer built at ${BUILD_DIR}/av1_dec_fuzzer."
echo "Create a corpus directory, copy IVF files in there, and run:"
echo " av1_dec_fuzzer CORPUS_DIR"

View file

@ -60,7 +60,9 @@ typedef enum {
DUAL_FILTER_LAYER = 1 << 12,
Q_INDEX_LAYER = 1 << 13,
SEGMENT_ID_LAYER = 1 << 14,
ALL_LAYERS = (1 << 15) - 1
MOTION_MODE_LAYER = 1 << 15,
COMPOUND_TYPE_LAYER = 1 << 16,
ALL_LAYERS = (1 << 17) - 1
} LayerType;
static LayerType layers = 0;
@ -84,6 +86,10 @@ static const arg_def_t dump_transform_size_arg =
static const arg_def_t dump_transform_type_arg =
ARG_DEF("tt", "transformType", 0, "Dump Transform Type");
static const arg_def_t dump_mode_arg = ARG_DEF("m", "mode", 0, "Dump Mode");
static const arg_def_t dump_motion_mode_arg =
ARG_DEF("mm", "motion_mode", 0, "Dump Motion Modes");
static const arg_def_t dump_compound_type_arg =
ARG_DEF("ct", "compound_type", 0, "Dump Compound Types");
static const arg_def_t dump_uv_mode_arg =
ARG_DEF("uvm", "uv_mode", 0, "Dump UV Intra Prediction Modes");
static const arg_def_t dump_skip_arg = ARG_DEF("s", "skip", 0, "Dump Skip");
@ -101,6 +107,13 @@ static const arg_def_t dump_delta_q_arg =
static const arg_def_t dump_seg_id_arg =
ARG_DEF("si", "seg_id", 0, "Dump Segment ID");
static const arg_def_t usage_arg = ARG_DEF("h", "help", 0, "Help");
static const arg_def_t skip_non_transform_arg = ARG_DEF(
"snt", "skip_non_transform", 1, "Skip is counted as a non transform.");
static const arg_def_t combined_arg =
ARG_DEF("comb", "combined", 1, "combinining parameters into one output.");
int combined_parm_list[5];
int combined_parm_count = 0;
static const arg_def_t *main_args[] = { &limit_arg,
&dump_all_arg,
@ -113,6 +126,8 @@ static const arg_def_t *main_args[] = { &limit_arg,
&dump_transform_type_arg,
&dump_mode_arg,
&dump_uv_mode_arg,
&dump_motion_mode_arg,
&dump_compound_type_arg,
&dump_skip_arg,
&dump_filter_arg,
&dump_cdef_arg,
@ -123,6 +138,8 @@ static const arg_def_t *main_args[] = { &limit_arg,
&dump_delta_q_arg,
&dump_seg_id_arg,
&usage_arg,
&skip_non_transform_arg,
&combined_arg,
NULL };
#define ENUM(name) \
{ #name, name }
@ -150,12 +167,14 @@ const map_entry block_size_map[] = {
ENUM(BLOCK_64X16), LAST_ENUM
};
#define TX_SKIP -1
const map_entry tx_size_map[] = {
ENUM(TX_4X4), ENUM(TX_8X8), ENUM(TX_16X16), ENUM(TX_32X32),
ENUM(TX_64X64), ENUM(TX_4X8), ENUM(TX_8X4), ENUM(TX_8X16),
ENUM(TX_16X8), ENUM(TX_16X32), ENUM(TX_32X16), ENUM(TX_32X64),
ENUM(TX_64X32), ENUM(TX_4X16), ENUM(TX_16X4), ENUM(TX_8X32),
ENUM(TX_32X8), LAST_ENUM
ENUM(TX_32X8), ENUM(TX_16X64), ENUM(TX_64X16), LAST_ENUM
};
const map_entry tx_type_map[] = { ENUM(DCT_DCT),
@ -193,6 +212,15 @@ const map_entry prediction_mode_map[] = {
ENUM(NEW_NEWMV), ENUM(INTRA_INVALID), LAST_ENUM
};
const map_entry motion_mode_map[] = { ENUM(SIMPLE_TRANSLATION),
ENUM(OBMC_CAUSAL), // 2-sided OBMC
ENUM(WARPED_CAUSAL), // 2-sided WARPED
LAST_ENUM };
const map_entry compound_type_map[] = { ENUM(COMPOUND_AVERAGE),
ENUM(COMPOUND_WEDGE),
ENUM(COMPOUND_DIFFWTD), LAST_ENUM };
const map_entry uv_prediction_mode_map[] = {
ENUM(UV_DC_PRED), ENUM(UV_V_PRED),
ENUM(UV_H_PRED), ENUM(UV_D45_PRED),
@ -212,6 +240,43 @@ const map_entry config_map[] = { ENUM(MI_SIZE), LAST_ENUM };
static const char *exec_name;
struct parm_offset {
char parm[60];
char offset;
};
struct parm_offset parm_offsets[] = {
{ "blockSize", offsetof(insp_mi_data, sb_type) },
{ "transformSize", offsetof(insp_mi_data, tx_size) },
{ "transformType", offsetof(insp_mi_data, tx_type) },
{ "dualFilterType", offsetof(insp_mi_data, dual_filter_type) },
{ "mode", offsetof(insp_mi_data, mode) },
{ "uv_mode", offsetof(insp_mi_data, uv_mode) },
{ "motion_mode", offsetof(insp_mi_data, motion_mode) },
{ "compound_type", offsetof(insp_mi_data, compound_type) },
{ "referenceFrame", offsetof(insp_mi_data, ref_frame) },
{ "skip", offsetof(insp_mi_data, skip) },
};
int parm_count = sizeof(parm_offsets) / sizeof(parm_offsets[0]);
int convert_to_indices(char *str, int *indices, int maxCount, int *count) {
*count = 0;
do {
char *comma = strchr(str, ',');
int length = (comma ? (int)(comma - str) : (int)strlen(str));
int i;
for (i = 0; i < parm_count; ++i) {
if (!strncmp(str, parm_offsets[i].parm, length)) {
break;
}
}
if (i == parm_count) return 0;
indices[(*count)++] = i;
if (*count > maxCount) return 0;
str += length + 1;
} while (strlen(str) > 0);
return 1;
}
insp_frame_data frame_data;
int frame_count = 0;
int decoded_frame_count = 0;
@ -382,6 +447,38 @@ int put_motion_vectors(char *buffer) {
return (int)(buf - buffer);
}
int put_combined(char *buffer) {
const int mi_rows = frame_data.mi_rows;
const int mi_cols = frame_data.mi_cols;
char *buf = buffer;
int r, c, p;
buf += put_str(buf, " \"");
for (p = 0; p < combined_parm_count; ++p) {
if (p) buf += put_str(buf, "&");
buf += put_str(buf, parm_offsets[combined_parm_list[p]].parm);
}
buf += put_str(buf, "\": [");
for (r = 0; r < mi_rows; ++r) {
*(buf++) = '[';
for (c = 0; c < mi_cols; ++c) {
insp_mi_data *mi = &frame_data.mi_grid[r * mi_cols + c];
*(buf++) = '[';
for (p = 0; p < combined_parm_count; ++p) {
if (p) *(buf++) = ',';
int16_t *v = (int16_t *)(((int8_t *)mi) +
parm_offsets[combined_parm_list[p]].offset);
buf += put_num(buf, 0, v[0], 0);
}
*(buf++) = ']';
if (c < mi_cols - 1) *(buf++) = ',';
}
*(buf++) = ']';
if (r < mi_rows - 1) *(buf++) = ',';
}
buf += put_str(buf, "],\n");
return (int)(buf - buffer);
}
int put_block_info(char *buffer, const map_entry *map, const char *name,
size_t offset, int len) {
const int mi_rows = frame_data.mi_rows;
@ -490,9 +587,16 @@ int put_accounting(char *buffer) {
}
#endif
int skip_non_transform = 0;
void inspect(void *pbi, void *data) {
/* Fetch frame data. */
ifd_inspect(&frame_data, pbi);
ifd_inspect(&frame_data, pbi, skip_non_transform);
// Show existing frames just show a reference buffer we've already decoded.
// There's no information to show.
if (frame_data.show_existing_frame) return;
(void)data;
// We allocate enough space and hope we don't write out of bounds. Totally
// unsafe but this speeds things up, especially when compiled to Javascript.
@ -523,6 +627,14 @@ void inspect(void *pbi, void *data) {
buf += put_block_info(buf, uv_prediction_mode_map, "uv_mode",
offsetof(insp_mi_data, uv_mode), 0);
}
if (layers & MOTION_MODE_LAYER) {
buf += put_block_info(buf, motion_mode_map, "motion_mode",
offsetof(insp_mi_data, motion_mode), 0);
}
if (layers & COMPOUND_TYPE_LAYER) {
buf += put_block_info(buf, compound_type_map, "compound_type",
offsetof(insp_mi_data, compound_type), 0);
}
if (layers & SKIP_LAYER) {
buf +=
put_block_info(buf, skip_map, "skip", offsetof(insp_mi_data, skip), 0);
@ -554,6 +666,7 @@ void inspect(void *pbi, void *data) {
if (layers & MOTION_VECTORS_LAYER) {
buf += put_motion_vectors(buf);
}
if (combined_parm_count > 0) buf += put_combined(buf);
if (layers & REFERENCE_FRAME_LAYER) {
buf += put_block_info(buf, refs_map, "referenceFrame",
offsetof(insp_mi_data, ref_frame), 2);
@ -563,7 +676,8 @@ void inspect(void *pbi, void *data) {
buf += put_accounting(buf);
}
#endif
buf += snprintf(buf, MAX_BUFFER, " \"frame\": %d,\n", decoded_frame_count);
buf +=
snprintf(buf, MAX_BUFFER, " \"frame\": %d,\n", frame_data.frame_number);
buf += snprintf(buf, MAX_BUFFER, " \"showFrame\": %d,\n",
frame_data.show_frame);
buf += snprintf(buf, MAX_BUFFER, " \"frameType\": %d,\n",
@ -618,21 +732,51 @@ int open_file(char *file) {
return EXIT_SUCCESS;
}
Av1DecodeReturn adr;
int have_frame = 0;
const unsigned char *frame;
const unsigned char *end_frame;
size_t frame_size = 0;
EMSCRIPTEN_KEEPALIVE
int read_frame() {
if (!aom_video_reader_read_frame(reader)) return EXIT_FAILURE;
img = NULL;
aom_codec_iter_t iter = NULL;
size_t frame_size = 0;
const unsigned char *frame = aom_video_reader_get_frame(reader, &frame_size);
if (aom_codec_decode(&codec, frame, (unsigned int)frame_size, NULL) !=
AOM_CODEC_OK) {
die_codec(&codec, "Failed to decode frame.");
}
// This loop skips over any frames that are show_existing_frames, as
// there is nothing to analyze.
do {
if (!have_frame) {
if (!aom_video_reader_read_frame(reader)) return EXIT_FAILURE;
frame = aom_video_reader_get_frame(reader, &frame_size);
have_frame = 1;
end_frame = frame + frame_size;
}
if (aom_codec_decode(&codec, frame, (unsigned int)frame_size, &adr) !=
AOM_CODEC_OK) {
die_codec(&codec, "Failed to decode frame.");
}
frame = adr.buf;
if (frame == end_frame) have_frame = 0;
} while (adr.show_existing);
int got_any_frames = 0;
aom_image_t *frame_img;
while ((frame_img = aom_codec_get_frame(&codec, &iter))) {
img = frame_img;
struct av1_ref_frame ref_dec;
ref_dec.idx = adr.idx;
// ref_dec.idx is the index to the reference buffer idx to AV1_GET_REFERENCE
// if its -1 the decoder didn't update any reference buffer and the only
// way to see the frame is aom_codec_get_frame.
if (ref_dec.idx == -1) {
aom_codec_iter_t iter = NULL;
img = frame_img = aom_codec_get_frame(&codec, &iter);
++frame_count;
got_any_frames = 1;
} else if (!aom_codec_control(&codec, AV1_GET_REFERENCE, &ref_dec)) {
img = frame_img = &ref_dec.img;
++frame_count;
got_any_frames = 1;
}
@ -692,6 +836,10 @@ static void parse_args(char **argv) {
layers |= MODE_LAYER;
else if (arg_match(&arg, &dump_uv_mode_arg, argi))
layers |= UV_MODE_LAYER;
else if (arg_match(&arg, &dump_motion_mode_arg, argi))
layers |= MOTION_MODE_LAYER;
else if (arg_match(&arg, &dump_compound_type_arg, argi))
layers |= COMPOUND_TYPE_LAYER;
else if (arg_match(&arg, &dump_skip_arg, argi))
layers |= SKIP_LAYER;
else if (arg_match(&arg, &dump_filter_arg, argi))
@ -718,6 +866,13 @@ static void parse_args(char **argv) {
usage_exit();
else if (arg_match(&arg, &limit_arg, argi))
stop_after = arg_parse_uint(&arg);
else if (arg_match(&arg, &skip_non_transform_arg, argi))
skip_non_transform = arg_parse_uint(&arg);
else if (arg_match(&arg, &combined_arg, argi))
convert_to_indices(
(char *)arg.val, combined_parm_list,
sizeof(combined_parm_list) / sizeof(combined_parm_list[0]),
&combined_parm_count);
else
argj++;
}

View file

@ -13,13 +13,28 @@
// ============================
//
// This is a lightfield bitstream parsing example. It takes an input file
// containing the whole compressed lightfield bitstream(ivf file), and parses it
// and constructs and outputs a new bitstream that can be decoded by an AV1
// decoder. The output bitstream contains reference frames(i.e. anchor frames),
// camera frame header, and tile list OBUs. num_references is the number of
// anchor frames coded at the beginning of the light field file.
// After running the lightfield encoder, run lightfield bitstream parsing:
// containing the whole compressed lightfield bitstream(ivf file) and a text
// file containing a stream of tiles to decode and then constructs and outputs
// a new bitstream that can be decoded by an AV1 decoder. The output bitstream
// contains reference frames(i.e. anchor frames), camera frame header, and
// tile list OBUs. num_references is the number of anchor frames coded at the
// beginning of the light field file. After running the lightfield encoder,
// run lightfield bitstream parsing:
// examples/lightfield_bitstream_parsing vase10x10.ivf vase_tile_list.ivf 4
// tile_list.txt
//
// The tile_list.txt is expected to be of the form:
// Frame <frame_index0>
// <image_index0> <anchor_index0> <tile_col0> <tile_row0>
// <image_index1> <anchor_index1> <tile_col1> <tile_row1>
// ...
// Frame <frame_index1)
// ...
//
// The "Frame" markers indicate a new render frame and thus a new tile list
// will be started and the old one flushed. The image_indexN, anchor_indexN,
// tile_colN, and tile_rowN identify an individual tile to be decoded and
// to use anchor_indexN anchor image for MCP.
#include <stdio.h>
#include <stdlib.h>
@ -39,7 +54,7 @@
static const char *exec_name;
void usage_exit(void) {
fprintf(stderr, "Usage: %s <infile> <outfile> <num_references> \n",
fprintf(stderr, "Usage: %s <infile> <outfile> <num_references> <tile_list>\n",
exec_name);
exit(EXIT_FAILURE);
}
@ -47,9 +62,8 @@ void usage_exit(void) {
#define ALIGN_POWER_OF_TWO(value, n) \
(((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1))
// SB size: 64x64
const uint8_t output_frame_width_in_tiles_minus_1 = 512 / 64 - 1;
const uint8_t output_frame_height_in_tiles_minus_1 = 512 / 64 - 1;
const int output_frame_width = 512;
const int output_frame_height = 512;
// Spec:
// typedef struct {
@ -68,32 +82,6 @@ typedef struct {
int tile_row;
} TILE_LIST_INFO;
// M references: 0 - M-1; N images(including references): 0 - N-1;
// Note: order the image index incrementally, so that we only go through the
// bitstream once to construct the tile list.
const int num_tile_lists = 2;
const uint16_t tile_count_minus_1 = 9 - 1;
const TILE_LIST_INFO tile_list[2][9] = {
{ { 16, 0, 4, 5 },
{ 83, 3, 13, 2 },
{ 57, 2, 2, 6 },
{ 31, 1, 11, 5 },
{ 2, 0, 7, 4 },
{ 77, 3, 9, 9 },
{ 49, 1, 0, 1 },
{ 6, 0, 3, 10 },
{ 63, 2, 5, 8 } },
{ { 65, 2, 11, 1 },
{ 42, 1, 3, 7 },
{ 88, 3, 8, 4 },
{ 76, 3, 1, 15 },
{ 1, 0, 2, 2 },
{ 19, 0, 5, 6 },
{ 60, 2, 4, 0 },
{ 25, 1, 11, 15 },
{ 50, 2, 5, 4 } },
};
static int get_image_bps(aom_img_fmt_t fmt) {
switch (fmt) {
case AOM_IMG_FMT_I420: return 12;
@ -107,6 +95,102 @@ static int get_image_bps(aom_img_fmt_t fmt) {
return 0;
}
void process_tile_list(const TILE_LIST_INFO *tiles, int num_tiles,
aom_codec_pts_t tl_pts, unsigned char **frames,
const size_t *frame_sizes, aom_codec_ctx_t *codec,
unsigned char *tl_buf, AvxVideoWriter *writer,
uint8_t output_frame_width_in_tiles_minus_1,
uint8_t output_frame_height_in_tiles_minus_1) {
unsigned char *tl = tl_buf;
struct aom_write_bit_buffer wb = { tl, 0 };
unsigned char *saved_obu_size_loc = NULL;
uint32_t tile_list_obu_header_size = 0;
uint32_t tile_list_obu_size = 0;
int num_tiles_minus_1 = num_tiles - 1;
int i;
// Write the tile list OBU header that is 1 byte long.
aom_wb_write_literal(&wb, 0, 1); // forbidden bit.
aom_wb_write_literal(&wb, 8, 4); // tile list OBU: "1000"
aom_wb_write_literal(&wb, 0, 1); // obu_extension = 0
aom_wb_write_literal(&wb, 1, 1); // obu_has_size_field
aom_wb_write_literal(&wb, 0, 1); // reserved
tl++;
tile_list_obu_header_size++;
// Write the OBU size using a fixed length_field_size of 4 bytes.
saved_obu_size_loc = tl;
// aom_wb_write_unsigned_literal(&wb, data, bits) requires that bits <= 32.
aom_wb_write_unsigned_literal(&wb, 0, 32);
tl += 4;
tile_list_obu_header_size += 4;
// write_tile_list_obu()
aom_wb_write_literal(&wb, output_frame_width_in_tiles_minus_1, 8);
aom_wb_write_literal(&wb, output_frame_height_in_tiles_minus_1, 8);
aom_wb_write_literal(&wb, num_tiles_minus_1, 16);
tl += 4;
tile_list_obu_size += 4;
// Write each tile's data
for (i = 0; i <= num_tiles_minus_1; i++) {
aom_tile_data tile_data = { 0, NULL, 0 };
int image_idx = tiles[i].image_idx;
int ref_idx = tiles[i].reference_idx;
int tc = tiles[i].tile_col;
int tr = tiles[i].tile_row;
// Reset bit writer to the right location.
wb.bit_buffer = tl;
wb.bit_offset = 0;
size_t frame_size = frame_sizes[image_idx];
const unsigned char *frame = frames[image_idx];
aom_codec_control_(codec, AV1_SET_DECODE_TILE_ROW, tr);
aom_codec_control_(codec, AV1_SET_DECODE_TILE_COL, tc);
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_TILE_DATA, &tile_data);
// Copy over tile info.
// uint8_t anchor_frame_idx;
// uint8_t tile_row;
// uint8_t tile_col;
// uint16_t coded_tile_data_size_minus_1;
// uint8_t *coded_tile_data;
uint32_t tile_info_bytes = 5;
aom_wb_write_literal(&wb, ref_idx, 8);
aom_wb_write_literal(&wb, tr, 8);
aom_wb_write_literal(&wb, tc, 8);
aom_wb_write_literal(&wb, (int)tile_data.coded_tile_data_size - 1, 16);
tl += tile_info_bytes;
memcpy(tl, (uint8_t *)tile_data.coded_tile_data,
tile_data.coded_tile_data_size);
tl += tile_data.coded_tile_data_size;
tile_list_obu_size +=
tile_info_bytes + (uint32_t)tile_data.coded_tile_data_size;
}
// Write tile list OBU size.
size_t bytes_written = 0;
if (aom_uleb_encode_fixed_size(tile_list_obu_size, 4, 4, saved_obu_size_loc,
&bytes_written))
die_codec(codec, "Failed to encode the tile list obu size.");
// Copy the tile list.
if (!aom_video_writer_write_frame(
writer, tl_buf, tile_list_obu_header_size + tile_list_obu_size,
tl_pts))
die_codec(codec, "Failed to copy compressed tile list.");
}
int main(int argc, char **argv) {
aom_codec_ctx_t codec;
AvxVideoReader *reader = NULL;
@ -114,11 +198,12 @@ int main(int argc, char **argv) {
const AvxInterface *decoder = NULL;
const AvxVideoInfo *info = NULL;
int num_references;
int n, i;
int i;
aom_codec_pts_t pts;
const char *tile_list_file = NULL;
exec_name = argv[0];
if (argc != 4) die("Invalid number of arguments.");
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]);
@ -126,11 +211,15 @@ int main(int argc, char **argv) {
num_references = (int)strtol(argv[3], NULL, 0);
info = aom_video_reader_get_info(reader);
aom_video_reader_set_fourcc(reader, AV1_FOURCC);
// 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()));
@ -141,6 +230,7 @@ int main(int argc, char **argv) {
// 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);
@ -165,16 +255,38 @@ int main(int argc, char **argv) {
// Record the offset of the first camera image.
const FileOffset camera_frame_pos = ftello(infile);
// Read out the first camera frame.
aom_video_reader_read_frame(reader);
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 = 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);
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
@ -231,115 +343,68 @@ int main(int argc, char **argv) {
// 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 = pts;
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;
// Process 1 tile list.
for (n = 0; n < num_tile_lists; n++) {
unsigned char *tl = tl_buf;
struct aom_write_bit_buffer wb = { tl, 0 };
unsigned char *saved_obu_size_loc = NULL;
uint32_t tile_list_obu_header_size = 0;
uint32_t tile_list_obu_size = 0;
// Write the tile list OBU header that is 1 byte long.
aom_wb_write_literal(&wb, 0, 1); // forbidden bit.
aom_wb_write_literal(&wb, 8, 4); // tile list OBU: "1000"
aom_wb_write_literal(&wb, 0, 1); // obu_extension = 0
aom_wb_write_literal(&wb, 1, 1); // obu_has_size_field
aom_wb_write_literal(&wb, 0, 1); // reserved
tl++;
tile_list_obu_header_size++;
// Write the OBU size using a fixed length_field_size of 4 bytes.
saved_obu_size_loc = tl;
// aom_wb_write_unsigned_literal(&wb, data, bits) requires that bits <= 32.
aom_wb_write_unsigned_literal(&wb, 0, 32);
tl += 4;
tile_list_obu_header_size += 4;
// write_tile_list_obu()
aom_wb_write_literal(&wb, output_frame_width_in_tiles_minus_1, 8);
aom_wb_write_literal(&wb, output_frame_height_in_tiles_minus_1, 8);
aom_wb_write_literal(&wb, tile_count_minus_1, 16);
tl += 4;
tile_list_obu_size += 4;
// Write each tile's data
for (i = 0; i <= tile_count_minus_1; i++) {
aom_tile_data tile_data = { 0, NULL, 0 };
int image_idx = tile_list[n][i].image_idx;
int ref_idx = tile_list[n][i].reference_idx;
int tc = tile_list[n][i].tile_col;
int tr = tile_list[n][i].tile_row;
int frame_cnt = -1;
// Reset bit writer to the right location.
wb.bit_buffer = tl;
wb.bit_offset = 0;
// Seek to the first camera image.
fseeko(infile, camera_frame_pos, SEEK_SET);
// Read out the camera image
while (frame_cnt != image_idx) {
aom_video_reader_read_frame(reader);
frame_cnt++;
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;
}
size_t frame_size = 0;
const unsigned char *frame =
aom_video_reader_get_frame(reader, &frame_size);
aom_codec_control_(&codec, AV1_SET_DECODE_TILE_ROW, tr);
aom_codec_control_(&codec, AV1_SET_DECODE_TILE_COL, tc);
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_TILE_DATA, &tile_data);
// Copy over tile info.
// uint8_t anchor_frame_idx;
// uint8_t tile_row;
// uint8_t tile_col;
// uint16_t coded_tile_data_size_minus_1;
// uint8_t *coded_tile_data;
uint32_t tile_info_bytes = 5;
aom_wb_write_literal(&wb, ref_idx, 8);
aom_wb_write_literal(&wb, tr, 8);
aom_wb_write_literal(&wb, tc, 8);
aom_wb_write_literal(&wb, (int)tile_data.coded_tile_data_size - 1, 16);
tl += tile_info_bytes;
memcpy(tl, (uint8_t *)tile_data.coded_tile_data,
tile_data.coded_tile_data_size);
tl += tile_data.coded_tile_data_size;
tile_list_obu_size +=
tile_info_bytes + (uint32_t)tile_data.coded_tile_data_size;
num_tiles = 0;
}
// Write tile list OBU size.
size_t bytes_written = 0;
if (aom_uleb_encode_fixed_size(tile_list_obu_size, 4, 4, saved_obu_size_loc,
&bytes_written))
die_codec(&codec, "Failed to encode the tile list obu size.");
// Copy the tile list.
if (!aom_video_writer_write_frame(
writer, tl_buf, tile_list_obu_header_size + tile_list_obu_size,
tl_pts))
die_codec(&codec, "Failed to copy compressed tile list.");
tl_pts++;
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);

View file

@ -14,10 +14,26 @@
//
// This is an example of a simple lightfield decoder. It builds upon the
// simple_decoder.c example. It takes an input file containing the compressed
// data (in ivf format), treating it as a lightfield instead of a video.
// data (in ivf format), treating it as a lightfield instead of a video; and a
// text file with a list of tiles to decode. There is an optional parameter
// allowing to choose the output format, and the supported formats are
// YUV1D(default), YUV, and NV12.
// After running the lightfield encoder, run lightfield decoder to decode a
// batch of tiles:
// examples/lightfield_decoder vase10x10.ivf vase_reference.yuv 4
// examples/lightfield_decoder vase10x10.ivf vase_reference.yuv 4 tile_list.txt
// 0(optional)
// The tile_list.txt is expected to be of the form:
// Frame <frame_index0>
// <image_index0> <anchor_index0> <tile_col0> <tile_row0>
// <image_index1> <anchor_index1> <tile_col1> <tile_row1>
// ...
// Frame <frame_index1)
// ...
//
// The "Frame" markers indicate a new render frame and thus a new tile list
// will be started and the old one flushed. The image_indexN, anchor_indexN,
// tile_colN, and tile_rowN identify an individual tile to be decoded and
// to use anchor_indexN anchor image for MCP.
#include <stdio.h>
#include <stdlib.h>
@ -33,43 +49,109 @@
static const char *exec_name;
void usage_exit(void) {
fprintf(stderr, "Usage: %s <infile> <outfile> <num_references>\n", exec_name);
fprintf(stderr,
"Usage: %s <infile> <outfile> <num_references> <tile_list> <output "
"format(optional)>\n",
exec_name);
exit(EXIT_FAILURE);
}
// Tile list entry provided by the application
typedef struct {
int image_idx;
int reference_idx;
int tile_col;
int tile_row;
} TILE_LIST_INFO;
// Output frame size
const int output_frame_width = 512;
const int output_frame_height = 512;
// M references: 0 - M-1; N images(including references): 0 - N-1;
// Note: order the image index incrementally, so that we only go through the
// bitstream once to construct the tile list.
const int num_tile_lists = 2;
const uint16_t tile_count_minus_1 = 9 - 1;
const TILE_LIST_INFO tile_list[2][9] = {
{ { 16, 0, 4, 5 },
{ 83, 3, 13, 2 },
{ 57, 2, 2, 6 },
{ 31, 1, 11, 5 },
{ 2, 0, 7, 4 },
{ 77, 3, 9, 9 },
{ 49, 1, 0, 1 },
{ 6, 0, 3, 10 },
{ 63, 2, 5, 8 } },
{ { 65, 2, 11, 1 },
{ 42, 1, 3, 7 },
{ 88, 3, 8, 4 },
{ 76, 3, 1, 15 },
{ 1, 0, 2, 2 },
{ 19, 0, 5, 6 },
{ 60, 2, 4, 0 },
{ 25, 1, 11, 15 },
{ 50, 2, 5, 4 } },
};
static void aom_img_copy_tile(const aom_image_t *src, const aom_image_t *dst,
int dst_row_offset, int dst_col_offset) {
const int shift = (src->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 1 : 0;
int plane;
for (plane = 0; plane < 3; ++plane) {
const unsigned char *src_buf = src->planes[plane];
const int src_stride = src->stride[plane];
unsigned char *dst_buf = dst->planes[plane];
const int dst_stride = dst->stride[plane];
const int roffset =
(plane > 0) ? dst_row_offset >> dst->y_chroma_shift : dst_row_offset;
const int coffset =
(plane > 0) ? dst_col_offset >> dst->x_chroma_shift : dst_col_offset;
// col offset needs to be adjusted for HBD.
dst_buf += roffset * dst_stride + (coffset << shift);
const int w = (aom_img_plane_width(src, plane) << shift);
const int h = aom_img_plane_height(src, plane);
int y;
for (y = 0; y < h; ++y) {
memcpy(dst_buf, src_buf, w);
src_buf += src_stride;
dst_buf += dst_stride;
}
}
}
void decode_tile(aom_codec_ctx_t *codec, const unsigned char *frame,
size_t frame_size, int tr, int tc, int ref_idx,
aom_image_t *reference_images, aom_image_t *output,
int *tile_idx, unsigned int *output_bit_depth,
aom_image_t **img_ptr, int output_format) {
aom_codec_control_(codec, AV1_SET_TILE_MODE, 1);
aom_codec_control_(codec, AV1D_EXT_TILE_DEBUG, 1);
aom_codec_control_(codec, AV1_SET_DECODE_TILE_ROW, tr);
aom_codec_control_(codec, AV1_SET_DECODE_TILE_COL, tc);
av1_ref_frame_t ref;
ref.idx = 0;
ref.use_external_ref = 1;
ref.img = reference_images[ref_idx];
if (aom_codec_control(codec, AV1_SET_REFERENCE, &ref)) {
die_codec(codec, "Failed to set reference frame.");
}
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_iter_t iter = NULL;
aom_image_t *img = aom_codec_get_frame(codec, &iter);
if (!img) die_codec(codec, "Failed to get frame.");
*img_ptr = img;
// aom_img_alloc() sets bit_depth as follows:
// output->bit_depth = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
// Use img->bit_depth(read from bitstream), so that aom_shift_img()
// works as expected.
output->bit_depth = img->bit_depth;
*output_bit_depth = img->bit_depth;
if (output_format != YUV1D) {
// 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;
const uint8_t output_frame_width_in_tiles = output_frame_width / tile_width;
// Copy the tile to the output frame.
const int row_offset =
(*tile_idx / output_frame_width_in_tiles) * tile_height;
const int col_offset =
(*tile_idx % output_frame_width_in_tiles) * tile_width;
aom_img_copy_tile(img, output, row_offset, col_offset);
(*tile_idx)++;
}
}
static void img_write_to_file(const aom_image_t *img, FILE *file,
int output_format) {
if (output_format == YUV)
aom_img_write(img, file);
else if (output_format == NV12)
aom_img_write_nv12(img, file);
else
die("Invalid output format");
}
int main(int argc, char **argv) {
FILE *outfile = NULL;
@ -78,13 +160,18 @@ int main(int argc, char **argv) {
const AvxInterface *decoder = NULL;
const AvxVideoInfo *info = NULL;
int num_references;
aom_img_fmt_t ref_fmt = 0;
aom_image_t reference_images[MAX_EXTERNAL_REFERENCES];
aom_image_t output;
aom_image_t *output_shifted = NULL;
size_t frame_size = 0;
const unsigned char *frame = NULL;
int n, i, j;
int i, j;
const char *tile_list_file = NULL;
int output_format = YUV1D;
exec_name = argv[0];
if (argc != 4) die("Invalid number of arguments.");
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]);
@ -93,11 +180,18 @@ int main(int argc, char **argv) {
die("Failed to open %s for writing.", argv[2]);
num_references = (int)strtol(argv[3], NULL, 0);
tile_list_file = argv[4];
if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
if (output_format < YUV1D || output_format > NV12)
die("Output format out of range [0, 2]");
info = aom_video_reader_get_info(reader);
decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
if (!decoder) die("Unknown input codec.");
if (info->codec_fourcc == LST_FOURCC)
decoder = get_aom_decoder_by_fourcc(AV1_FOURCC);
else
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))
@ -116,7 +210,6 @@ int main(int argc, char **argv) {
die_codec(&codec, "Failed to decode frame.");
if (i == 0) {
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");
@ -127,7 +220,7 @@ int main(int argc, char **argv) {
// Allocate memory to store decoded references. Allocate memory with the
// border so that it can be used as a reference.
for (j = 0; j < num_references; j++) {
unsigned int border = AOM_BORDER_IN_PIXELS;
unsigned int border = AOM_DEC_BORDER_IN_PIXELS;
if (!aom_img_alloc_with_border(&reference_images[j], ref_fmt,
frame_res[0], frame_res[1], 32, 8,
border)) {
@ -156,50 +249,110 @@ int main(int argc, char **argv) {
// Record the offset of the first camera image.
const FileOffset camera_frame_pos = ftello(infile);
// Process 1 tile.
for (n = 0; n < num_tile_lists; n++) {
for (i = 0; i <= tile_count_minus_1; i++) {
int image_idx = tile_list[n][i].image_idx;
int ref_idx = tile_list[n][i].reference_idx;
int tc = tile_list[n][i].tile_col;
int tr = tile_list[n][i].tile_row;
int frame_cnt = -1;
printf("Loading compressed frames into memory.\n");
// Seek to the first camera image.
fseeko(infile, camera_frame_pos, SEEK_SET);
// 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 out the camera image
while (frame_cnt != image_idx) {
aom_video_reader_read_frame(reader);
frame_cnt++;
// 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);
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);
if (output_format != YUV1D) {
// Allocate the output frame.
aom_img_fmt_t out_fmt = ref_fmt;
if (!CONFIG_LOWBITDEPTH) out_fmt |= AOM_IMG_FMT_HIGHBITDEPTH;
if (!aom_img_alloc(&output, out_fmt, output_frame_width,
output_frame_height, 32))
die("Failed to allocate output image.");
}
printf("Decoding tile list from file.\n");
char line[1024];
FILE *tile_list_fptr = fopen(tile_list_file, "r");
int tile_list_cnt = 0;
int tile_list_writes = 0;
int tile_idx = 0;
aom_image_t *out = NULL;
unsigned int output_bit_depth = 0;
while ((fgets(line, 1024, tile_list_fptr)) != NULL) {
if (line[0] == 'F') {
if (output_format != YUV1D) {
// Write out the tile list.
if (tile_list_cnt) {
out = &output;
if (output_bit_depth != 0)
aom_shift_img(output_bit_depth, &out, &output_shifted);
img_write_to_file(out, outfile, output_format);
tile_list_writes++;
}
tile_list_cnt++;
tile_idx = 0;
// Then memset the frame.
memset(output.img_data, 0, output.sz);
}
continue;
}
frame = aom_video_reader_get_frame(reader, &frame_size);
int image_idx, ref_idx, tc, tr;
sscanf(line, "%d %d %d %d", &image_idx, &ref_idx, &tc, &tr);
if (image_idx >= num_frames) {
die("Tile list image_idx out of bounds: %d >= %d.", image_idx,
num_frames);
}
if (ref_idx >= num_references) {
die("Tile list ref_idx out of bounds: %d >= %d.", ref_idx,
num_references);
}
frame = frames[image_idx];
frame_size = frame_sizes[image_idx];
aom_codec_control_(&codec, AV1_SET_TILE_MODE, 1);
aom_codec_control_(&codec, AV1D_EXT_TILE_DEBUG, 1);
aom_codec_control_(&codec, AV1_SET_DECODE_TILE_ROW, tr);
aom_codec_control_(&codec, AV1_SET_DECODE_TILE_COL, tc);
av1_ref_frame_t ref;
ref.idx = 0;
ref.use_external_ref = 1;
ref.img = reference_images[ref_idx];
if (aom_codec_control(&codec, AV1_SET_REFERENCE, &ref)) {
die_codec(&codec, "Failed to set reference frame.");
}
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_iter_t iter = NULL;
aom_image_t *img = aom_codec_get_frame(&codec, &iter);
aom_img_write(img, outfile);
aom_image_t *img = NULL;
decode_tile(&codec, frame, frame_size, tr, tc, ref_idx, reference_images,
&output, &tile_idx, &output_bit_depth, &img, output_format);
if (output_format == YUV1D) {
out = img;
if (output_bit_depth != 0)
aom_shift_img(output_bit_depth, &out, &output_shifted);
aom_img_write(out, outfile);
}
}
if (output_format != YUV1D) {
// Write out the last tile list.
if (tile_list_writes < tile_list_cnt) {
out = &output;
if (output_bit_depth != 0)
aom_shift_img(output_bit_depth, &out, &output_shifted);
img_write_to_file(out, outfile, output_format);
}
}
if (output_shifted) aom_img_free(output_shifted);
if (output_format != YUV1D) aom_img_free(&output);
for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]);
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_reader_close(reader);
fclose(outfile);

View file

@ -275,9 +275,13 @@ static void pass1(aom_image_t *raw, FILE *infile, const char *outfile_name,
aom_img_fmt_t ref_fmt = AOM_IMG_FMT_I420;
if (!CONFIG_LOWBITDEPTH) ref_fmt |= AOM_IMG_FMT_HIGHBITDEPTH;
// Allocate memory with the border so that it can be used as a reference.
int border_in_pixels =
(codec.config.enc->rc_resize_mode || codec.config.enc->rc_superres_mode)
? AOM_BORDER_IN_PIXELS
: AOM_ENC_NO_SCALE_BORDER;
for (i = 0; i < reference_image_num; i++) {
if (!aom_img_alloc_with_border(&reference_images[i], ref_fmt, cfg->g_w,
cfg->g_h, 32, 8, AOM_BORDER_IN_PIXELS)) {
cfg->g_h, 32, 8, border_in_pixels)) {
die("Failed to allocate image.");
}
}
@ -393,6 +397,10 @@ static void pass1(aom_image_t *raw, FILE *infile, const char *outfile_name,
for (i = 0; i < reference_image_num; i++) aom_img_free(&reference_images[i]);
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
// Modify large_scale_file fourcc.
if (cfg->large_scale_tile == 1)
aom_video_writer_set_fourcc(writer, LST_FOURCC);
aom_video_writer_close(writer);
printf("\nSecond pass complete. Processed %d frames.\n", frame_count);

View file

@ -18,10 +18,12 @@
// compressed tile data. This input file is reconstructed from the encoded
// lightfield ivf file, and is decodable by AV1 decoder. num_references is
// the number of anchor frames coded at the beginning of the light field file.
// num_tile_lists is the number of tile lists need to be decoded.
// num_tile_lists is the number of tile lists need to be decoded. There is an
// optional parameter allowing to choose the output format, and the supported
// formats are YUV1D(default), YUV, and NV12.
// Run lightfield tile list decoder to decode an AV1 tile list file:
// examples/lightfield_tile_list_decoder vase_tile_list.ivf vase_tile_list.yuv
// 4 2
// 4 2 0(optional)
#include <stdio.h>
#include <stdlib.h>
@ -39,11 +41,61 @@ static const char *exec_name;
void usage_exit(void) {
fprintf(stderr,
"Usage: %s <infile> <outfile> <num_references> <num_tile_lists>\n",
"Usage: %s <infile> <outfile> <num_references> <num_tile_lists> "
"<output format(optional)>\n",
exec_name);
exit(EXIT_FAILURE);
}
static void write_tile_yuv1d(aom_codec_ctx_t *codec, const aom_image_t *img,
FILE *file) {
// 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;
const uint8_t output_frame_width_in_tiles = img->d_w / tile_width;
unsigned int tile_count = 0;
if (aom_codec_control(codec, AV1D_GET_TILE_COUNT, &tile_count))
die_codec(codec, "Failed to get the tile size");
// Write tile to file.
const int shift = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 1 : 0;
unsigned int tile_idx;
for (tile_idx = 0; tile_idx < tile_count; ++tile_idx) {
const int row_offset =
(tile_idx / output_frame_width_in_tiles) * tile_height;
const int col_offset =
(tile_idx % output_frame_width_in_tiles) * tile_width;
int plane;
for (plane = 0; plane < 3; ++plane) {
const unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
const int roffset =
(plane > 0) ? row_offset >> img->y_chroma_shift : row_offset;
const int coffset =
(plane > 0) ? col_offset >> img->x_chroma_shift : col_offset;
const int w = (plane > 0) ? ((tile_width >> img->x_chroma_shift) << shift)
: (tile_width << shift);
const int h =
(plane > 0) ? (tile_height >> img->y_chroma_shift) : tile_height;
int y;
// col offset needs to be adjusted for HBD.
buf += roffset * stride + (coffset << shift);
for (y = 0; y < h; ++y) {
fwrite(buf, 1, w, file);
buf += stride;
}
}
}
}
int main(int argc, char **argv) {
FILE *outfile = NULL;
aom_codec_ctx_t codec;
@ -55,11 +107,12 @@ int main(int argc, char **argv) {
aom_image_t reference_images[MAX_EXTERNAL_REFERENCES];
size_t frame_size = 0;
const unsigned char *frame = NULL;
int output_format = YUV1D;
int i, j, n;
exec_name = argv[0];
if (argc != 5) die("Invalid number of arguments.");
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]);
@ -70,6 +123,10 @@ int main(int argc, char **argv) {
num_references = (int)strtol(argv[3], NULL, 0);
num_tile_lists = (int)strtol(argv[4], NULL, 0);
if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
if (output_format < YUV1D || output_format > NV12)
die("Output format out of range [0, 2]");
info = aom_video_reader_get_info(reader);
decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
@ -103,7 +160,7 @@ int main(int argc, char **argv) {
// Allocate memory to store decoded references. Allocate memory with the
// border so that it can be used as a reference.
for (j = 0; j < num_references; j++) {
unsigned int border = AOM_BORDER_IN_PIXELS;
unsigned int border = AOM_DEC_BORDER_IN_PIXELS;
if (!aom_img_alloc_with_border(&reference_images[j], ref_fmt,
frame_res[0], frame_res[1], 32, 8,
border)) {
@ -147,9 +204,17 @@ int main(int argc, char **argv) {
if (aom_codec_decode(&codec, frame, frame_size, NULL))
die_codec(&codec, "Failed to decode the tile list.");
aom_codec_iter_t iter = NULL;
aom_image_t *img;
while ((img = aom_codec_get_frame(&codec, &iter)))
fwrite(img->img_data, 1, img->sz, outfile);
aom_image_t *img = aom_codec_get_frame(&codec, &iter);
if (!img) die_codec(&codec, "Failed to get frame.");
if (output_format == YUV1D)
// write the tile to the output file in 1D format.
write_tile_yuv1d(&codec, img, outfile);
else if (output_format == YUV)
aom_img_write(img, outfile);
else
// NV12 output format
aom_img_write_nv12(img, outfile);
}
for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]);

View file

@ -114,7 +114,7 @@ typedef struct {
const char *debug_file;
} noise_model_args_t;
void parse_args(noise_model_args_t *noise_args, int *argc, char **argv) {
static void parse_args(noise_model_args_t *noise_args, int *argc, char **argv) {
struct arg arg;
static const arg_def_t *main_args[] = { &help,
&input_arg,