/* * Copyright (c) 2016, 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.
*/
// This is needed by ext_tile related unit tests. #define EXT_TILE_DEBUG 1 #define MC_TEMP_BUF_PELS \
(((MAX_SB_SIZE)*2 + (AOM_INTERP_EXTEND)*2) * \
((MAX_SB_SIZE)*2 + (AOM_INTERP_EXTEND)*2))
// Checks that the remaining bits start with a 1 and ends with 0s. // It consumes an additional byte, if already byte aligned before the check. int av1_check_trailing_bits(AV1Decoder *pbi, struct aom_read_bit_buffer *rb) { // bit_offset is set to 0 (mod 8) when the reader is already byte aligned int bits_before_alignment = 8 - rb->bit_offset % 8; int trailing = aom_rb_read_literal(rb, bits_before_alignment); if (trailing != (1 << (bits_before_alignment - 1))) {
pbi->error.error_code = AOM_CODEC_CORRUPT_FRAME; return -1;
} return 0;
}
// Use only_chroma = 1 to only set the chroma planes staticinlinevoid set_planes_to_neutral_grey( const SequenceHeader *const seq_params, const YV12_BUFFER_CONFIG *const buf, int only_chroma) { if (seq_params->use_highbitdepth) { constint val = 1 << (seq_params->bit_depth - 1); for (int plane = only_chroma; plane < MAX_MB_PLANE; plane++) { constint is_uv = plane > 0;
uint16_t *const base = CONVERT_TO_SHORTPTR(buf->buffers[plane]); // Set the first row to neutral grey. Then copy the first row to all // subsequent rows. if (buf->crop_heights[is_uv] > 0) {
aom_memset16(base, val, buf->crop_widths[is_uv]); for (int row_idx = 1; row_idx < buf->crop_heights[is_uv]; row_idx++) {
memcpy(&base[row_idx * buf->strides[is_uv]], base, sizeof(*base) * buf->crop_widths[is_uv]);
}
}
}
} else { for (int plane = only_chroma; plane < MAX_MB_PLANE; plane++) { constint is_uv = plane > 0; for (int row_idx = 0; row_idx < buf->crop_heights[is_uv]; row_idx++) {
memset(&buf->buffers[plane][row_idx * buf->strides[is_uv]], 1 << 7,
buf->crop_widths[is_uv]);
}
}
}
}
staticinlinevoid loop_restoration_read_sb_coeffs(const AV1_COMMON *const cm,
MACROBLOCKD *xd,
aom_reader *const r, int plane, int runit_idx);
// Distance of Mb to the various image edges. These are specified to 8th pel // as they are always compared to values that are in 1/8th pel units
set_mi_row_col(xd, tile, mi_row, bh, mi_col, bw, mi_params->mi_rows,
mi_params->mi_cols);
typedefstruct PadBlock { int x0; int x1; int y0; int y1;
} PadBlock;
#if CONFIG_AV1_HIGHBITDEPTH staticinlinevoid highbd_build_mc_border(const uint8_t *src8, int src_stride,
uint8_t *dst8, int dst_stride, int x, int y, int b_w, int b_h, int w, int h) { // Get a pointer to the start of the real data for this row. const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
uint16_t *dst = CONVERT_TO_SHORTPTR(dst8); const uint16_t *ref_row = src - x - y * src_stride;
if (y >= h)
ref_row += (h - 1) * src_stride; elseif (y > 0)
ref_row += y * src_stride;
do { int right = 0, copy; int left = x < 0 ? -x : 0;
if (left > b_w) left = b_w;
if (x + b_w > w) right = x + b_w - w;
if (right > b_w) right = b_w;
copy = b_w - left - right;
if (left) aom_memset16(dst, ref_row[0], left);
if (copy) memcpy(dst + left, ref_row + x + left, copy * sizeof(uint16_t));
if (right) aom_memset16(dst + left + copy, ref_row[w - 1], right);
dst += dst_stride;
++y;
if (y > 0 && y < h) ref_row += src_stride;
} while (--b_h);
} #endif// CONFIG_AV1_HIGHBITDEPTH
staticinlinevoid build_mc_border(const uint8_t *src, int src_stride,
uint8_t *dst, int dst_stride, int x, int y, int b_w, int b_h, int w, int h) { // Get a pointer to the start of the real data for this row. const uint8_t *ref_row = src - x - y * src_stride;
if (y >= h)
ref_row += (h - 1) * src_stride; elseif (y > 0)
ref_row += y * src_stride;
do { int right = 0, copy; int left = x < 0 ? -x : 0;
if (left > b_w) left = b_w;
if (x + b_w > w) right = x + b_w - w;
if (right > b_w) right = b_w;
copy = b_w - left - right;
if (left) memset(dst, ref_row[0], left);
if (copy) memcpy(dst + left, ref_row + x + left, copy);
if (right) memset(dst + left + copy, ref_row[w - 1], right);
dst += dst_stride;
++y;
if (y > 0 && y < h) ref_row += src_stride;
} while (--b_h);
}
staticinlineint update_extend_mc_border_params( conststruct scale_factors *const sf, struct buf_2d *const pre_buf,
MV32 scaled_mv, PadBlock *block, int subpel_x_mv, int subpel_y_mv, int do_warp, int is_intrabc, int *x_pad, int *y_pad) { constint is_scaled = av1_is_scaled(sf); // Get reference width and height. int frame_width = pre_buf->width; int frame_height = pre_buf->height;
// Do border extension if there is motion or // width/height is not a multiple of 8 pixels. if ((!is_intrabc) && (!do_warp) &&
(is_scaled || scaled_mv.col || scaled_mv.row || (frame_width & 0x7) ||
(frame_height & 0x7))) { if (subpel_x_mv || (sf->x_step_q4 != SUBPEL_SHIFTS)) {
block->x0 -= AOM_INTERP_EXTEND - 1;
block->x1 += AOM_INTERP_EXTEND;
*x_pad = 1;
}
staticinlinevoid dec_build_prediction_by_above_preds( const AV1_COMMON *cm, DecoderCodingBlock *dcb,
uint8_t *tmp_buf[MAX_MB_PLANE], int tmp_width[MAX_MB_PLANE], int tmp_height[MAX_MB_PLANE], int tmp_stride[MAX_MB_PLANE]) {
MACROBLOCKD *const xd = &dcb->xd; if (!xd->up_available) return;
// Adjust mb_to_bottom_edge to have the correct value for the OBMC // prediction block. This is half the height of the original block, // except for 128-wide blocks, where we only use a height of 32. constint this_height = xd->height * MI_SIZE; constint pred_height = AOMMIN(this_height / 2, 32);
xd->mb_to_bottom_edge += GET_MV_SUBPEL(this_height - pred_height); struct build_prediction_ctxt ctxt = {
cm, tmp_buf, tmp_width, tmp_height, tmp_stride, xd->mb_to_right_edge, dcb
}; const BLOCK_SIZE bsize = xd->mi[0]->bsize;
foreach_overlappable_nb_above(cm, xd,
max_neighbor_obmc[mi_size_wide_log2[bsize]],
dec_build_prediction_by_above_pred, &ctxt);
staticinlinevoid dec_build_prediction_by_left_preds( const AV1_COMMON *cm, DecoderCodingBlock *dcb,
uint8_t *tmp_buf[MAX_MB_PLANE], int tmp_width[MAX_MB_PLANE], int tmp_height[MAX_MB_PLANE], int tmp_stride[MAX_MB_PLANE]) {
MACROBLOCKD *const xd = &dcb->xd; if (!xd->left_available) return;
// Adjust mb_to_right_edge to have the correct value for the OBMC // prediction block. This is half the width of the original block, // except for 128-wide blocks, where we only use a width of 32. constint this_width = xd->width * MI_SIZE; constint pred_width = AOMMIN(this_width / 2, 32);
xd->mb_to_right_edge += GET_MV_SUBPEL(this_width - pred_width);
// Distance of Mb to the various image edges. These are specified to 8th pel // as they are always compared to values that are in 1/8th pel units
set_mi_row_col(xd, tile, mi_row, bh, mi_col, bw, mi_params->mi_rows,
mi_params->mi_cols);
if (mi_row >= cm->mi_params.mi_rows || mi_col >= cm->mi_params.mi_cols) return;
// parse_decode_flag takes the following values : // 01 - do parse only // 10 - do decode only // 11 - do parse and decode staticconst block_visitor_fn_t block_visit[4] = { NULL, parse_decode_block,
decode_block,
parse_decode_block };
if (parse_decode_flag & 1) { constint num_planes = av1_num_planes(cm); for (int plane = 0; plane < num_planes; ++plane) { int rcol0, rcol1, rrow0, rrow1;
// Skip some unnecessary work if loop restoration is disabled if (cm->rst_info[plane].frame_restoration_type == RESTORE_NONE) continue;
partition = (bsize < BLOCK_8X8) ? PARTITION_NONE
: read_partition(xd, mi_row, mi_col, reader,
has_rows, has_cols, bsize);
} else {
partition = get_partition(cm, mi_row, mi_col, bsize);
}
subsize = get_partition_subsize(bsize, partition); if (subsize == BLOCK_INVALID) { // When an internal error occurs ensure that xd->mi_row is set appropriately // w.r.t. current tile, which is used to signal processing of current row is // done.
xd->mi_row = mi_row;
aom_internal_error(xd->error_info, AOM_CODEC_CORRUPT_FRAME, "Partition is invalid for block size %dx%d",
block_size_wide[bsize], block_size_high[bsize]);
} // Check the bitstream is conformant: if there is subsampling on the // chroma planes, subsize must subsample to a valid block size. conststruct macroblockd_plane *const pd_u = &xd->plane[1]; if (get_plane_block_size(subsize, pd_u->subsampling_x, pd_u->subsampling_y) ==
BLOCK_INVALID) { // When an internal error occurs ensure that xd->mi_row is set appropriately // w.r.t. current tile, which is used to signal processing of current row is // done.
xd->mi_row = mi_row;
aom_internal_error(xd->error_info, AOM_CODEC_CORRUPT_FRAME, "Block size %dx%d invalid with this subsampling mode",
block_size_wide[subsize], block_size_high[subsize]);
}
staticinlinevoid setup_bool_decoder(
MACROBLOCKD *const xd, const uint8_t *data, const uint8_t *data_end, const size_t read_size, struct aom_internal_error_info *error_info,
aom_reader *r, uint8_t allow_update_cdf) { // Validate the calculated partition length. If the buffer // described by the partition can't be fully read, then restrict // it to the portion that can be (for EC mode) or throw an error. if (!read_is_valid(data, read_size, data_end)) { // When internal error occurs ensure that xd->mi_row is set appropriately // w.r.t. current tile, which is used to signal processing of current row is // done in row-mt decoding.
xd->mi_row = xd->tile.mi_row_start;
aom_internal_error(error_info, AOM_CODEC_CORRUPT_FRAME, "Truncated packet or corrupt tile length");
} if (aom_reader_init(r, data, read_size)) { // When internal error occurs ensure that xd->mi_row is set appropriately // w.r.t. current tile, which is used to signal processing of current row is // done in row-mt decoding.
xd->mi_row = xd->tile.mi_row_start;
aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, "Failed to allocate bool decoder %d", 1);
}
r->allow_update_cdf = allow_update_cdf;
}
staticinlinevoid setup_segmentation(AV1_COMMON *const cm, struct aom_read_bit_buffer *rb) { struct segmentation *const seg = &cm->seg;
seg->enabled = aom_rb_read_bit(rb); if (!seg->enabled) { if (cm->cur_frame->seg_map) {
memset(cm->cur_frame->seg_map, 0,
(cm->cur_frame->mi_rows * cm->cur_frame->mi_cols));
}
memset(seg, 0, sizeof(*seg));
segfeatures_copy(&cm->cur_frame->seg, seg); return;
} if (cm->seg.enabled && cm->prev_frame &&
(cm->mi_params.mi_rows == cm->prev_frame->mi_rows) &&
(cm->mi_params.mi_cols == cm->prev_frame->mi_cols)) {
cm->last_frame_seg_map = cm->prev_frame->seg_map;
} else {
cm->last_frame_seg_map = NULL;
} // Read update flags if (cm->features.primary_ref_frame == PRIMARY_REF_NONE) { // These frames can't use previous frames, so must signal map + features
seg->update_map = 1;
seg->temporal_update = 0;
seg->update_data = 1;
} else {
seg->update_map = aom_rb_read_bit(rb); if (seg->update_map) {
seg->temporal_update = aom_rb_read_bit(rb);
} else {
seg->temporal_update = 0;
}
seg->update_data = aom_rb_read_bit(rb);
}
// Segmentation data update if (seg->update_data) {
av1_clearall_segfeatures(seg);
for (int i = 0; i < MAX_SEGMENTS; i++) { for (int j = 0; j < SEG_LVL_MAX; j++) { int data = 0; constint feature_enabled = aom_rb_read_bit(rb); if (feature_enabled) {
av1_enable_segfeature(seg, i, j);
// Read in loop filter deltas applied at the MB level based on mode or ref // frame.
lf->mode_ref_delta_update = 0;
lf->mode_ref_delta_enabled = aom_rb_read_bit(rb); if (lf->mode_ref_delta_enabled) {
lf->mode_ref_delta_update = aom_rb_read_bit(rb); if (lf->mode_ref_delta_update) { for (int i = 0; i < REF_FRAMES; i++) if (aom_rb_read_bit(rb))
lf->ref_deltas[i] = aom_rb_read_inv_signed_literal(rb, 6);
for (int i = 0; i < MAX_MODE_LF_DELTAS; i++) if (aom_rb_read_bit(rb))
lf->mode_deltas[i] = aom_rb_read_inv_signed_literal(rb, 6);
}
}
// TODO(afergs): make "struct aom_read_bit_buffer *const rb"? staticinlinevoid setup_superres(AV1_COMMON *const cm, struct aom_read_bit_buffer *rb, int *width, int *height) {
cm->superres_upscaled_width = *width;
cm->superres_upscaled_height = *height;
const SequenceHeader *const seq_params = cm->seq_params; if (!seq_params->enable_superres) return;
if (aom_rb_read_bit(rb)) {
cm->superres_scale_denominator =
(uint8_t)aom_rb_read_literal(rb, SUPERRES_SCALE_BITS);
cm->superres_scale_denominator += SUPERRES_SCALE_DENOMINATOR_MIN; // Don't edit cm->width or cm->height directly, or the buffers won't get // resized correctly
av1_calculate_scaled_superres_size(width, height,
cm->superres_scale_denominator);
} else { // 1:1 scaling - ie. no scaling, scale not provided
cm->superres_scale_denominator = SCALE_NUMERATOR;
}
}
staticinlinevoid resize_context_buffers(AV1_COMMON *cm, int width, int height) { #if CONFIG_SIZE_LIMIT if (width > DECODE_WIDTH_LIMIT || height > DECODE_HEIGHT_LIMIT)
aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, "Dimensions of %dx%d beyond allowed size of %dx%d.",
width, height, DECODE_WIDTH_LIMIT, DECODE_HEIGHT_LIMIT); #endif if (cm->width != width || cm->height != height) { constint new_mi_rows = CEIL_POWER_OF_TWO(height, MI_SIZE_LOG2); constint new_mi_cols = CEIL_POWER_OF_TWO(width, MI_SIZE_LOG2);
// Allocations in av1_alloc_context_buffers() depend on individual // dimensions as well as the overall size. if (new_mi_cols > cm->mi_params.mi_cols ||
new_mi_rows > cm->mi_params.mi_rows) { if (av1_alloc_context_buffers(cm, width, height, BLOCK_4X4)) { // The cm->mi_* values have been cleared and any existing context // buffers have been freed. Clear cm->width and cm->height to be // consistent and to force a realloc next time.
cm->width = 0;
cm->height = 0;
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, "Failed to allocate context buffers");
}
} else {
cm->mi_params.set_mb_mi(&cm->mi_params, width, height, BLOCK_4X4);
}
av1_init_mi_buffers(&cm->mi_params);
cm->width = width;
cm->height = height;
}
staticinlineint valid_ref_frame_img_fmt(aom_bit_depth_t ref_bit_depth, int ref_xss, int ref_yss,
aom_bit_depth_t this_bit_depth, int this_xss, int this_yss) { return ref_bit_depth == this_bit_depth && ref_xss == this_xss &&
ref_yss == this_yss;
}
staticinlinevoid setup_frame_size_with_refs(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) { int width, height; int found = 0; int has_valid_ref_frame = 0; for (int i = LAST_FRAME; i <= ALTREF_FRAME; ++i) { if (aom_rb_read_bit(rb)) { const RefCntBuffer *const ref_buf = get_ref_frame_buf(cm, i); // This will never be NULL in a normal stream, as streams are required to // have a shown keyframe before any inter frames, which would refresh all // the reference buffers. However, it might be null if we're starting in // the middle of a stream, and static analysis will error if we don't do // a null check here. if (ref_buf == NULL) {
aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, "Invalid condition: invalid reference buffer");
} else { const YV12_BUFFER_CONFIG *const buf = &ref_buf->buf;
width = buf->y_crop_width;
height = buf->y_crop_height;
cm->render_width = buf->render_width;
cm->render_height = buf->render_height;
setup_superres(cm, rb, &width, &height);
resize_context_buffers(cm, width, height);
found = 1; break;
}
}
}
const SequenceHeader *const seq_params = cm->seq_params; if (!found) { int num_bits_width = seq_params->num_bits_width; int num_bits_height = seq_params->num_bits_height;
// Check to make sure at least one of frames that this frame references // has valid dimensions. for (int i = LAST_FRAME; i <= ALTREF_FRAME; ++i) { const RefCntBuffer *const ref_frame = get_ref_frame_buf(cm, i);
has_valid_ref_frame |=
valid_ref_frame_size(ref_frame->buf.y_crop_width,
ref_frame->buf.y_crop_height, width, height);
} if (!has_valid_ref_frame)
aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, "Referenced frame has invalid size"); for (int i = LAST_FRAME; i <= ALTREF_FRAME; ++i) { const RefCntBuffer *const ref_frame = get_ref_frame_buf(cm, i); if (!valid_ref_frame_img_fmt(
ref_frame->buf.bit_depth, ref_frame->buf.subsampling_x,
ref_frame->buf.subsampling_y, seq_params->bit_depth,
seq_params->subsampling_x, seq_params->subsampling_y))
aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, "Referenced frame has incompatible color format");
}
setup_buffer_pool(cm);
}
// Same function as av1_read_uniform but reading from uncompresses header wb staticint rb_read_uniform(struct aom_read_bit_buffer *const rb, int n) { constint l = get_unsigned_bits(n); constint m = (1 << l) - n; constint v = aom_rb_read_literal(rb, l - 1);
assert(l != 0); if (v < m) return v; else return (v << 1) - m + aom_rb_read_bit(rb);
}
// This information is stored as a separate byte. int mod = rb->bit_offset % CHAR_BIT; if (mod > 0) aom_rb_read_literal(rb, CHAR_BIT - mod);
assert(rb->bit_offset % CHAR_BIT == 0);
if (cm->tiles.cols * cm->tiles.rows > 1) { // Read the number of bytes used to store tile size
pbi->tile_col_size_bytes = aom_rb_read_literal(rb, 2) + 1;
pbi->tile_size_bytes = aom_rb_read_literal(rb, 2) + 1;
}
} #endif// EXT_TILE_DEBUG
static size_t mem_get_varsize(const uint8_t *src, int sz) { switch (sz) { case 1: return src[0]; case 2: return mem_get_le16(src); case 3: return mem_get_le24(src); case 4: return mem_get_le32(src); default: assert(0 && "Invalid size"); return -1;
}
}
#if EXT_TILE_DEBUG // Reads the next tile returning its size and adjusting '*data' accordingly // based on 'is_last'. On return, '*data' is updated to point to the end of the // raw tile buffer in the bit stream. staticinlinevoid get_ls_tile_buffer( const uint8_t *const data_end, struct aom_internal_error_info *error_info, const uint8_t **data, TileBufferDec (*const tile_buffers)[MAX_TILE_COLS], int tile_size_bytes, int col, int row, int tile_copy_mode) {
size_t size;
if (!read_is_valid(*data, tile_size_bytes, data_end))
aom_internal_error(error_info, AOM_CODEC_CORRUPT_FRAME, "Truncated packet or corrupt tile length");
size = mem_get_varsize(*data, tile_size_bytes);
// If tile_copy_mode = 1, then the top bit of the tile header indicates copy // mode. if (tile_copy_mode && (size >> (tile_size_bytes * 8 - 1)) == 1) { // The remaining bits in the top byte signal the row offset int offset = (size >> (tile_size_bytes - 1) * 8) & 0x7f; if (offset > row) {
aom_internal_error(
error_info, AOM_CODEC_CORRUPT_FRAME, "Invalid row offset in tile copy mode: row=%d offset=%d", row,
offset);
}
// Currently, only use tiles in same column as reference tiles.
copy_data = tile_buffers[row - offset][col].data;
copy_size = tile_buffers[row - offset][col].size;
size = 0;
} else {
size += AV1_MIN_TILE_SIZE_BYTES;
}
*data += tile_size_bytes;
if (size > (size_t)(data_end - *data))
aom_internal_error(error_info, AOM_CODEC_CORRUPT_FRAME, "Truncated packet or corrupt tile size");
// Returns the end of the last tile buffer // (tile_buffers[cm->tiles.rows - 1][cm->tiles.cols - 1]). staticconst uint8_t *get_ls_tile_buffers(
AV1Decoder *pbi, const uint8_t *data, const uint8_t *data_end,
TileBufferDec (*const tile_buffers)[MAX_TILE_COLS]) {
AV1_COMMON *const cm = &pbi->common; constint tile_cols = cm->tiles.cols; constint tile_rows = cm->tiles.rows; constint have_tiles = tile_cols * tile_rows > 1; const uint8_t *raw_data_end; // The end of the last tile buffer
if (!have_tiles) { const size_t tile_size = data_end - data;
tile_buffers[0][0].data = data;
tile_buffers[0][0].size = tile_size;
raw_data_end = NULL;
} else { // We locate only the tile buffers that are required, which are the ones // specified by pbi->dec_tile_col and pbi->dec_tile_row. Also, we always // need the last (bottom right) tile buffer, as we need to know where the // end of the compressed frame buffer is for proper superframe decoding.
constint tile_col_size_bytes = pbi->tile_col_size_bytes; constint tile_size_bytes = pbi->tile_size_bytes; int tile_width, tile_height; if (!av1_get_uniform_tile_size(cm, &tile_width, &tile_height)) {
aom_internal_error(
&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Not all the tiles in the tile list have the same size.");
} constint tile_copy_mode =
((AOMMAX(tile_width, tile_height) << MI_SIZE_LOG2) <= 256) ? 1 : 0; // Read tile column sizes for all columns (we need the last tile buffer) for (int c = 0; c < tile_cols; ++c) { constint is_last = c == tile_cols - 1;
size_t tile_col_size;
if (!is_last) { if (tile_col_size_bytes > data_end - data) {
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Not enough data to read tile_col_size");
}
tile_col_size = mem_get_varsize(data, tile_col_size_bytes);
data += tile_col_size_bytes; if (tile_col_size > (size_t)(data_end - data)) {
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "tile_col_data_end[%d] is out of bound", c);
}
tile_col_data_end[c] = data + tile_col_size;
} else {
tile_col_size = data_end - data;
tile_col_data_end[c] = data_end;
}
data += tile_col_size;
}
data = data_start;
// Read the required tile sizes. for (int c = tile_cols_start; c < tile_cols_end; ++c) { constint is_last = c == tile_cols - 1;
if (c > 0) data = tile_col_data_end[c - 1];
if (!is_last) data += tile_col_size_bytes;
// Get the whole of the last column, otherwise stop at the required tile. for (int r = 0; r < (is_last ? tile_rows : tile_rows_end); ++r) {
get_ls_tile_buffer(tile_col_data_end[c], &pbi->error, &data,
tile_buffers, tile_size_bytes, c, r, tile_copy_mode);
}
}
// If we have not read the last column, then read it to get the last tile. if (tile_cols_end != tile_cols) { constint c = tile_cols - 1;
data = tile_col_data_end[c - 1];
for (int r = 0; r < tile_rows; ++r) {
get_ls_tile_buffer(tile_col_data_end[c], &pbi->error, &data,
tile_buffers, tile_size_bytes, c, r, tile_copy_mode);
}
}
raw_data_end = data;
} return raw_data_end;
} #endif// EXT_TILE_DEBUG
// Reads the next tile returning its size and adjusting '*data' accordingly // based on 'is_last'. staticinlinevoid get_tile_buffer(const uint8_t *const data_end, constint tile_size_bytes, int is_last, struct aom_internal_error_info *error_info, const uint8_t **data,
TileBufferDec *const buf) {
size_t size;
if (!is_last) { if (!read_is_valid(*data, tile_size_bytes, data_end))
aom_internal_error(error_info, AOM_CODEC_CORRUPT_FRAME, "Not enough data to read tile size");
// Set up nsync.
dec_row_mt_sync->sync_range = get_sync_range(cm->width);
}
// Deallocate decoder row synchronization related mutex and data void av1_dec_row_mt_dealloc(AV1DecRowMTSync *dec_row_mt_sync) { if (dec_row_mt_sync != NULL) { #if CONFIG_MULTITHREAD int i; if (dec_row_mt_sync->mutex_ != NULL) { for (i = 0; i < dec_row_mt_sync->allocated_sb_rows; ++i) {
pthread_mutex_destroy(&dec_row_mt_sync->mutex_[i]);
}
aom_free(dec_row_mt_sync->mutex_);
} if (dec_row_mt_sync->cond_ != NULL) { for (i = 0; i < dec_row_mt_sync->allocated_sb_rows; ++i) {
pthread_cond_destroy(&dec_row_mt_sync->cond_[i]);
}
aom_free(dec_row_mt_sync->cond_);
} #endif// CONFIG_MULTITHREAD
aom_free(dec_row_mt_sync->cur_sb_col);
// clear the structure as the source of this call may be a resize in which // case this call will be followed by an _alloc() which may fail.
av1_zero(*dec_row_mt_sync);
}
}
staticinlinevoid sync_read(AV1DecRowMTSync *const dec_row_mt_sync, int r, int c) { #if CONFIG_MULTITHREAD constint nsync = dec_row_mt_sync->sync_range;
staticinlinevoid sync_write(AV1DecRowMTSync *const dec_row_mt_sync, int r, int c, constint sb_cols) { #if CONFIG_MULTITHREAD constint nsync = dec_row_mt_sync->sync_range; int cur; int sig = 1;
if (c < sb_cols - 1) {
cur = c; if (c % nsync) sig = 0;
} else {
cur = sb_cols + nsync + dec_row_mt_sync->intrabc_extra_top_right_sb_delay;
}
if (sig) {
pthread_mutex_lock(&dec_row_mt_sync->mutex_[r]);
// aom_reader_tell() returns 1 for a newly initialized decoder, and the // return value only increases as values are decoded. So nb_bits > 0, and // thus p > p_begin. Therefore accessing p[-1] is safe.
uint8_t last_byte = p[-1];
uint8_t pattern = 128 >> ((nb_bits - 1) & 7); if ((last_byte & (2 * pattern - 1)) != pattern) return -1;
// Make sure that all padding bytes are zero as required by the spec. const uint8_t *p_end = aom_reader_find_end(r); while (p < p_end) { if (*p != 0) return -1;
p++;
} return 0;
}
// Initialise the tile context from the frame context
tile_data->tctx = *cm->fc;
td->dcb.xd.tile_ctx = &tile_data->tctx;
// decode tile
decode_tile(pbi, td, row, col);
aom_merge_corrupted_flag(&pbi->dcb.corrupted, td->dcb.corrupted); if (pbi->dcb.corrupted)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Failed to decode tile data");
}
}
if (tiles->large_scale) { if (n_tiles == 1) { // Find the end of the single tile buffer return aom_reader_find_end(&pbi->tile_data->bit_reader);
} // Return the end of the last tile buffer return raw_data_end;
}
TileDataDec *const tile_data = pbi->tile_data + end_tile;
// The jmp_buf is valid only for the duration of the function that calls // setjmp(). Therefore, this function must reset the 'setjmp' field to 0 // before it returns. if (setjmp(thread_data->error_info.jmp)) {
thread_data->error_info.setjmp = 0;
thread_data->td->dcb.corrupted = 1; return 0;
}
thread_data->error_info.setjmp = 1;
staticinlineint get_max_row_mt_workers_per_tile(AV1_COMMON *cm, const TileInfo *tile) { // NOTE: Currently value of max workers is calculated based // on the parse and decode time. As per the theoretical estimate // when percentage of parse time is equal to percentage of decode // time, number of workers needed to parse + decode a tile can not // exceed more than 2. // TODO(any): Modify this value if parsing is optimized in future. int sb_rows = av1_get_sb_rows_in_tile(cm, tile); int max_workers =
sb_rows == 1 ? AOM_MIN_THREADS_PER_TILE : AOM_MAX_THREADS_PER_TILE; return max_workers;
}
// The caller must hold pbi->row_mt_mutex_ when calling this function. // Returns 1 if either the next job is stored in *next_job_info or 1 is stored // in *end_of_frame. // NOTE: The caller waits on pbi->row_mt_cond_ if this function returns 0. // The return value of this function depends on the following variables: // - frame_row_mt_info->mi_rows_parse_done // - frame_row_mt_info->mi_rows_decode_started // - frame_row_mt_info->row_mt_exit // Therefore we may need to signal or broadcast pbi->row_mt_cond_ if any of // these variables is modified. staticint get_next_job_info(AV1Decoder *const pbi,
AV1DecRowMTJobInfo *next_job_info, int *end_of_frame) {
AV1_COMMON *cm = &pbi->common;
TileDataDec *tile_data;
AV1DecRowMTSync *dec_row_mt_sync;
AV1DecRowMTInfo *frame_row_mt_info = &pbi->frame_row_mt_info; constint tile_rows_start = frame_row_mt_info->tile_rows_start; constint tile_rows_end = frame_row_mt_info->tile_rows_end; constint tile_cols_start = frame_row_mt_info->tile_cols_start; constint tile_cols_end = frame_row_mt_info->tile_cols_end; constint start_tile = frame_row_mt_info->start_tile; constint end_tile = frame_row_mt_info->end_tile; constint sb_mi_size = mi_size_wide[cm->seq_params->sb_size]; int num_mis_to_decode, num_threads_working; int num_mis_waiting_for_decode; int min_threads_working = INT_MAX; int max_mis_to_decode = 0; int tile_row_idx, tile_col_idx; int tile_row = -1; int tile_col = -1;
memset(next_job_info, 0, sizeof(*next_job_info));
// Frame decode is completed or error is encountered.
*end_of_frame = (frame_row_mt_info->mi_rows_decode_started ==
frame_row_mt_info->mi_rows_to_decode) ||
(frame_row_mt_info->row_mt_exit == 1); if (*end_of_frame) { return 1;
}
// Decoding cannot start as bit-stream parsing is not complete.
assert(frame_row_mt_info->mi_rows_parse_done >=
frame_row_mt_info->mi_rows_decode_started); if (frame_row_mt_info->mi_rows_parse_done ==
frame_row_mt_info->mi_rows_decode_started) return 0;
// Choose the tile to decode. for (tile_row_idx = tile_rows_start; tile_row_idx < tile_rows_end;
++tile_row_idx) { for (tile_col_idx = tile_cols_start; tile_col_idx < tile_cols_end;
++tile_col_idx) { if (tile_row_idx * cm->tiles.cols + tile_col_idx < start_tile ||
tile_row_idx * cm->tiles.cols + tile_col_idx > end_tile) continue;
// Pick the tile which has minimum number of threads working on it. if (num_mis_waiting_for_decode > 0) { if (num_threads_working < min_threads_working) {
min_threads_working = num_threads_working;
max_mis_to_decode = 0;
} if (num_threads_working == min_threads_working &&
num_mis_to_decode > max_mis_to_decode &&
num_threads_working <
get_max_row_mt_workers_per_tile(cm, &tile_data->tile_info)) {
max_mis_to_decode = num_mis_to_decode;
tile_row = tile_row_idx;
tile_col = tile_col_idx;
}
}
}
} // No job found to process if (tile_row == -1 || tile_col == -1) return 0;
staticinlinevoid signal_parse_sb_row_done(AV1Decoder *const pbi,
TileDataDec *const tile_data, constint sb_mi_size) {
AV1DecRowMTInfo *frame_row_mt_info = &pbi->frame_row_mt_info; #if CONFIG_MULTITHREAD
pthread_mutex_lock(pbi->row_mt_mutex_); #endif
assert(frame_row_mt_info->mi_rows_parse_done >=
frame_row_mt_info->mi_rows_decode_started);
tile_data->dec_row_mt_sync.mi_rows_parse_done += sb_mi_size;
frame_row_mt_info->mi_rows_parse_done += sb_mi_size; #if CONFIG_MULTITHREAD // A new decode job is available. Wake up one worker thread to handle the // new decode job. // NOTE: This assumes we bump mi_rows_parse_done and mi_rows_decode_started // by the same increment (sb_mi_size).
pthread_cond_signal(pbi->row_mt_cond_);
pthread_mutex_unlock(pbi->row_mt_mutex_); #endif
}
// This function is very similar to decode_tile(). It would be good to figure // out how to share code. staticinlinevoid parse_tile_row_mt(AV1Decoder *pbi, ThreadData *const td,
TileDataDec *const tile_data) {
AV1_COMMON *const cm = &pbi->common; constint sb_mi_size = mi_size_wide[cm->seq_params->sb_size]; constint num_planes = av1_num_planes(cm); const TileInfo *const tile_info = &tile_data->tile_info; int tile_row = tile_info->tile_row;
DecoderCodingBlock *const dcb = &td->dcb;
MACROBLOCKD *const xd = &dcb->xd;
// The jmp_buf is valid only for the duration of the function that calls // setjmp(). Therefore, this function must reset the 'setjmp' field to 0 // before it returns. if (setjmp(thread_data->error_info.jmp)) {
thread_data->error_info.setjmp = 0;
thread_data->td->dcb.corrupted = 1; #if CONFIG_MULTITHREAD
pthread_mutex_lock(pbi->row_mt_mutex_); #endif
frame_row_mt_info->row_mt_exit = 1; #if CONFIG_MULTITHREAD
pthread_cond_broadcast(pbi->row_mt_cond_);
pthread_mutex_unlock(pbi->row_mt_mutex_); #endif // If any SB row (erroneous row) processed by a thread encounters an // internal error, there is a need to indicate other threads that decoding // of the erroneous row is complete. This ensures that other threads which // wait upon the completion of SB's present in erroneous row are not waiting // indefinitely.
signal_decoding_done_for_erroneous_row(pbi, &thread_data->td->dcb.xd); return 0;
}
thread_data->error_info.setjmp = 1;
staticinlinevoid enqueue_tile_jobs(AV1Decoder *pbi, AV1_COMMON *cm, int tile_rows_start, int tile_rows_end, int tile_cols_start, int tile_cols_end, int start_tile, int end_tile) {
AV1DecTileMT *tile_mt_info = &pbi->tile_mt_info;
TileJobsDec *tile_job_queue = tile_mt_info->job_queue;
tile_mt_info->jobs_enqueued = 0;
tile_mt_info->jobs_dequeued = 0;
for (int row = tile_rows_start; row < tile_rows_end; row++) { for (int col = tile_cols_start; col < tile_cols_end; col++) { if (row * cm->tiles.cols + col < start_tile ||
row * cm->tiles.cols + col > end_tile) continue;
tile_job_queue->tile_buffer = &pbi->tile_buffers[row][col];
tile_job_queue->tile_data = pbi->tile_data + row * cm->tiles.cols + col;
tile_job_queue++;
tile_mt_info->jobs_enqueued++;
}
}
}
staticinlinevoid alloc_dec_jobs(AV1DecTileMT *tile_mt_info, AV1_COMMON *cm, int tile_rows, int tile_cols) {
tile_mt_info->alloc_tile_rows = tile_rows;
tile_mt_info->alloc_tile_cols = tile_cols; int num_tiles = tile_rows * tile_cols; #if CONFIG_MULTITHREAD
{
CHECK_MEM_ERROR(cm, tile_mt_info->job_mutex,
aom_malloc(sizeof(*tile_mt_info->job_mutex) * num_tiles));
for (int i = 0; i < num_tiles; i++) {
pthread_mutex_init(&tile_mt_info->job_mutex[i], NULL);
}
} #endif
CHECK_MEM_ERROR(cm, tile_mt_info->job_queue,
aom_malloc(sizeof(*tile_mt_info->job_queue) * num_tiles));
}
void av1_free_mc_tmp_buf(ThreadData *thread_data) { int ref; for (ref = 0; ref < 2; ref++) { if (thread_data->mc_buf_use_highbd)
aom_free(CONVERT_TO_SHORTPTR(thread_data->mc_buf[ref])); else
aom_free(thread_data->mc_buf[ref]);
thread_data->mc_buf[ref] = NULL;
}
thread_data->mc_buf_size = 0;
thread_data->mc_buf_use_highbd = 0;
aom_free(thread_data->tmp_conv_dst);
thread_data->tmp_conv_dst = NULL;
aom_free(thread_data->seg_mask);
thread_data->seg_mask = NULL; for (int i = 0; i < 2; ++i) {
aom_free(thread_data->tmp_obmc_bufs[i]);
thread_data->tmp_obmc_bufs[i] = NULL;
}
}
staticinlinevoid allocate_mc_tmp_buf(AV1_COMMON *const cm,
ThreadData *thread_data, int buf_size, int use_highbd) { for (int ref = 0; ref < 2; ref++) { // The mc_buf/hbd_mc_buf must be zeroed to fix a intermittent valgrind error // 'Conditional jump or move depends on uninitialised value' from the loop // filter. Uninitialized reads in convolve function (e.g. horiz_4tap path in // av1_convolve_2d_sr_avx2()) from mc_buf/hbd_mc_buf are seen to be the // potential reason for this issue. if (use_highbd) {
uint16_t *hbd_mc_buf;
CHECK_MEM_ERROR(cm, hbd_mc_buf, (uint16_t *)aom_memalign(16, buf_size));
memset(hbd_mc_buf, 0, buf_size);
thread_data->mc_buf[ref] = CONVERT_TO_BYTEPTR(hbd_mc_buf);
} else {
CHECK_MEM_ERROR(cm, thread_data->mc_buf[ref],
(uint8_t *)aom_memalign(16, buf_size));
memset(thread_data->mc_buf[ref], 0, buf_size);
}
}
thread_data->mc_buf_size = buf_size;
thread_data->mc_buf_use_highbd = use_highbd;
if (worker_idx != 0) { // Allocate thread data.
CHECK_MEM_ERROR(cm, thread_data->td,
aom_memalign(32, sizeof(*thread_data->td)));
av1_zero(*thread_data->td);
} else { // Main thread acts as a worker and uses the thread data in pbi
thread_data->td = &pbi->td;
}
thread_data->error_info.error_code = AOM_CODEC_OK;
thread_data->error_info.setjmp = 0;
}
} constint use_highbd = cm->seq_params->use_highbitdepth; constint buf_size = MC_TEMP_BUF_PELS << use_highbd; for (worker_idx = 1; worker_idx < pbi->max_threads; ++worker_idx) {
DecWorkerData *const thread_data = pbi->thread_data + worker_idx; if (thread_data->td->mc_buf_size != buf_size) {
av1_free_mc_tmp_buf(thread_data->td);
allocate_mc_tmp_buf(cm, thread_data->td, buf_size, use_highbd);
}
}
}
staticinlinevoid tile_mt_queue(AV1Decoder *pbi, int tile_cols, int tile_rows, int tile_rows_start, int tile_rows_end, int tile_cols_start, int tile_cols_end, int start_tile, int end_tile) {
AV1_COMMON *const cm = &pbi->common; if (pbi->tile_mt_info.alloc_tile_cols != tile_cols ||
pbi->tile_mt_info.alloc_tile_rows != tile_rows) {
av1_dealloc_dec_jobs(&pbi->tile_mt_info);
alloc_dec_jobs(&pbi->tile_mt_info, cm, tile_rows, tile_cols);
}
enqueue_tile_jobs(pbi, cm, tile_rows_start, tile_rows_end, tile_cols_start,
tile_cols_end, start_tile, end_tile);
qsort(pbi->tile_mt_info.job_queue, pbi->tile_mt_info.jobs_enqueued, sizeof(pbi->tile_mt_info.job_queue[0]), compare_tile_buffers);
}
staticconst uint8_t *decode_tiles_mt(AV1Decoder *pbi, const uint8_t *data, const uint8_t *data_end, int start_tile, int end_tile) {
AV1_COMMON *const cm = &pbi->common;
CommonTileParams *const tiles = &cm->tiles; constint tile_cols = tiles->cols; constint tile_rows = tiles->rows; constint n_tiles = tile_cols * tile_rows;
TileBufferDec(*const tile_buffers)[MAX_TILE_COLS] = pbi->tile_buffers; constint dec_tile_row = AOMMIN(pbi->dec_tile_row, tile_rows); constint single_row = pbi->dec_tile_row >= 0; constint dec_tile_col = AOMMIN(pbi->dec_tile_col, tile_cols); constint single_col = pbi->dec_tile_col >= 0; int tile_rows_start; int tile_rows_end; int tile_cols_start; int tile_cols_end; int tile_count_tg; int num_workers; const uint8_t *raw_data_end = NULL;
if (pbi->dcb.corrupted)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Failed to decode tile data");
if (tiles->large_scale) { if (n_tiles == 1) { // Find the end of the single tile buffer return aom_reader_find_end(&pbi->tile_data->bit_reader);
} // Return the end of the last tile buffer return raw_data_end;
}
TileDataDec *const tile_data = pbi->tile_data + end_tile;
staticinlinevoid row_mt_frame_init(AV1Decoder *pbi, int tile_rows_start, int tile_rows_end, int tile_cols_start, int tile_cols_end, int start_tile, int end_tile, int max_sb_rows) {
AV1_COMMON *const cm = &pbi->common;
AV1DecRowMTInfo *frame_row_mt_info = &pbi->frame_row_mt_info;
if (pbi->dcb.corrupted)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Failed to decode tile data");
if (tiles->large_scale) { if (n_tiles == 1) { // Find the end of the single tile buffer return aom_reader_find_end(&pbi->tile_data->bit_reader);
} // Return the end of the last tile buffer return raw_data_end;
}
TileDataDec *const tile_data = pbi->tile_data + end_tile;
if (!pars->update_parameters) { // inherit parameters from a previous reference frame int film_grain_params_ref_idx = aom_rb_read_literal(rb, 3); // Section 6.8.20: It is a requirement of bitstream conformance that // film_grain_params_ref_idx is equal to ref_frame_idx[ j ] for some value // of j in the range 0 to REFS_PER_FRAME - 1. int found = 0; for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) { if (film_grain_params_ref_idx == cm->remapped_ref_idx[i]) {
found = 1; break;
}
} if (!found) {
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "Invalid film grain reference idx %d. ref_frame_idx = " "{%d, %d, %d, %d, %d, %d, %d}",
film_grain_params_ref_idx, cm->remapped_ref_idx[0],
cm->remapped_ref_idx[1], cm->remapped_ref_idx[2],
cm->remapped_ref_idx[3], cm->remapped_ref_idx[4],
cm->remapped_ref_idx[5], cm->remapped_ref_idx[6]);
}
RefCntBuffer *const buf = cm->ref_frame_map[film_grain_params_ref_idx]; if (buf == NULL) {
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "Invalid Film grain reference idx");
} if (!buf->film_grain_params_present) {
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "Film grain reference parameters not available");
}
uint16_t random_seed = pars->random_seed;
*pars = buf->film_grain_params; // inherit paramaters
pars->random_seed = random_seed; // with new random seed return;
}
// Scaling functions parameters
pars->num_y_points = aom_rb_read_literal(rb, 4); // max 14 if (pars->num_y_points > 14)
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "Number of points for film grain luma scaling function " "exceeds the maximum value."); for (int i = 0; i < pars->num_y_points; i++) {
pars->scaling_points_y[i][0] = aom_rb_read_literal(rb, 8); if (i && pars->scaling_points_y[i - 1][0] >= pars->scaling_points_y[i][0])
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "First coordinate of the scaling function points " "shall be increasing.");
pars->scaling_points_y[i][1] = aom_rb_read_literal(rb, 8);
}
if (!seq_params->monochrome)
pars->chroma_scaling_from_luma = aom_rb_read_bit(rb); else
pars->chroma_scaling_from_luma = 0;
if (seq_params->monochrome || pars->chroma_scaling_from_luma ||
((seq_params->subsampling_x == 1) && (seq_params->subsampling_y == 1) &&
(pars->num_y_points == 0))) {
pars->num_cb_points = 0;
pars->num_cr_points = 0;
} else {
pars->num_cb_points = aom_rb_read_literal(rb, 4); // max 10 if (pars->num_cb_points > 10)
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "Number of points for film grain cb scaling function " "exceeds the maximum value."); for (int i = 0; i < pars->num_cb_points; i++) {
pars->scaling_points_cb[i][0] = aom_rb_read_literal(rb, 8); if (i &&
pars->scaling_points_cb[i - 1][0] >= pars->scaling_points_cb[i][0])
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "First coordinate of the scaling function points " "shall be increasing.");
pars->scaling_points_cb[i][1] = aom_rb_read_literal(rb, 8);
}
pars->num_cr_points = aom_rb_read_literal(rb, 4); // max 10 if (pars->num_cr_points > 10)
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "Number of points for film grain cr scaling function " "exceeds the maximum value."); for (int i = 0; i < pars->num_cr_points; i++) {
pars->scaling_points_cr[i][0] = aom_rb_read_literal(rb, 8); if (i &&
pars->scaling_points_cr[i - 1][0] >= pars->scaling_points_cr[i][0])
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "First coordinate of the scaling function points " "shall be increasing.");
pars->scaling_points_cr[i][1] = aom_rb_read_literal(rb, 8);
}
if ((seq_params->subsampling_x == 1) && (seq_params->subsampling_y == 1) &&
(((pars->num_cb_points == 0) && (pars->num_cr_points != 0)) ||
((pars->num_cb_points != 0) && (pars->num_cr_points == 0))))
aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM, "In YCbCr 4:2:0, film grain shall be applied " "to both chroma components or neither.");
}
pars->scaling_shift = aom_rb_read_literal(rb, 2) + 8; // 8 + value
// AR coefficients // Only sent if the corresponsing scaling function has // more than 0 points
pars->ar_coeff_lag = aom_rb_read_literal(rb, 2);
int num_pos_luma = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1); int num_pos_chroma = num_pos_luma; if (pars->num_y_points > 0) ++num_pos_chroma;
if (pars->num_y_points) for (int i = 0; i < num_pos_luma; i++)
pars->ar_coeffs_y[i] = aom_rb_read_literal(rb, 8) - 128;
if (pars->num_cb_points || pars->chroma_scaling_from_luma) for (int i = 0; i < num_pos_chroma; i++)
pars->ar_coeffs_cb[i] = aom_rb_read_literal(rb, 8) - 128;
if (pars->num_cr_points || pars->chroma_scaling_from_luma) for (int i = 0; i < num_pos_chroma; i++)
pars->ar_coeffs_cr[i] = aom_rb_read_literal(rb, 8) - 128;
pars->ar_coeff_shift = aom_rb_read_literal(rb, 2) + 6; // 6 + value
if (seq_params->reduced_still_picture_hdr) {
seq_params->frame_id_numbers_present_flag = 0;
} else {
seq_params->frame_id_numbers_present_flag = aom_rb_read_bit(rb);
} if (seq_params->frame_id_numbers_present_flag) { // We must always have delta_frame_id_length < frame_id_length, // in order for a frame to be referenced with a unique delta. // Avoid wasting bits by using a coding that enforces this restriction.
seq_params->delta_frame_id_length = aom_rb_read_literal(rb, 4) + 2;
seq_params->frame_id_length =
aom_rb_read_literal(rb, 3) + seq_params->delta_frame_id_length + 1; if (seq_params->frame_id_length > 16)
aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, "Invalid frame_id_length");
}
// TODO(sarahparker, debargha): The logic in the commented out code below // does not work currently and causes mismatches when resize is on. Fix it // before turning the optimization back on. /* YV12_BUFFER_CONFIG *ref_buf = get_ref_frame(cm, frame); if (cm->width == ref_buf->y_crop_width && cm->height == ref_buf->y_crop_height) { read_global_motion_params(&cm->global_motion[frame], &cm->prev_frame->global_motion[frame], rb, cm->features.allow_high_precision_mv); } else { cm->global_motion[frame] = default_warp_params; }
*/ /* printf("Dec Ref %d [%d/%d]: %d %d %d %d\n", frame, cm->current_frame.frame_number, cm->show_frame, cm->global_motion[frame].wmmat[0], cm->global_motion[frame].wmmat[1], cm->global_motion[frame].wmmat[2], cm->global_motion[frame].wmmat[3]);
*/
}
memcpy(cm->cur_frame->global_motion, cm->global_motion,
REF_FRAMES * sizeof(WarpedMotionParams));
}
// Release the references to the frame buffers in cm->ref_frame_map and reset // all elements of cm->ref_frame_map to NULL. staticinlinevoid reset_ref_frame_map(AV1_COMMON *const cm) {
BufferPool *const pool = cm->buffer_pool;
for (int i = 0; i < REF_FRAMES; i++) {
decrease_ref_count(cm->ref_frame_map[i], pool);
cm->ref_frame_map[i] = NULL;
}
}
// If the refresh_frame_flags bitmask is set, update reference frame id values // and mark frames as valid for reference. staticinlinevoid update_ref_frame_id(AV1Decoder *const pbi) {
AV1_COMMON *const cm = &pbi->common; int refresh_frame_flags = cm->current_frame.refresh_frame_flags; for (int i = 0; i < REF_FRAMES; i++) { if ((refresh_frame_flags >> i) & 1) {
cm->ref_frame_id[i] = cm->current_frame_id;
pbi->valid_for_referencing[i] = 1;
}
}
}
staticinlinevoid show_existing_frame_reset(AV1Decoder *const pbi, int existing_frame_idx) {
AV1_COMMON *const cm = &pbi->common;
for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) {
cm->remapped_ref_idx[i] = INVALID_IDX;
}
if (pbi->need_resync) {
reset_ref_frame_map(cm);
pbi->need_resync = 0;
}
// Note that the displayed frame must be valid for referencing in order to // have been selected.
cm->current_frame_id = cm->ref_frame_id[existing_frame_idx];
update_ref_frame_id(pbi);
lock_buffer_pool(cm->buffer_pool);
reset_ref_frame_map(cm);
assert(cm->cur_frame->ref_count == 1); for (i = 0; i < cm->buffer_pool->num_frame_bufs; ++i) { // Reset all unreferenced frame buffers. We can also reset cm->cur_frame // because we are the sole owner of cm->cur_frame. if (frame_bufs[i].ref_count > 0 && &frame_bufs[i] != cm->cur_frame) { continue;
}
frame_bufs[i].order_hint = 0;
av1_zero(frame_bufs[i].ref_order_hints);
}
av1_zero_unused_internal_frame_buffers(&cm->buffer_pool->int_frame_buffers);
unlock_buffer_pool(cm->buffer_pool);
}
// On success, returns 0. On failure, calls aom_internal_error and does not // return. staticint read_uncompressed_header(AV1Decoder *pbi, struct aom_read_bit_buffer *rb) {
AV1_COMMON *const cm = &pbi->common; const SequenceHeader *const seq_params = cm->seq_params;
CurrentFrame *const current_frame = &cm->current_frame;
FeatureFlags *const features = &cm->features;
MACROBLOCKD *const xd = &pbi->dcb.xd;
BufferPool *const pool = cm->buffer_pool;
RefCntBuffer *const frame_bufs = pool->frame_bufs;
aom_s_frame_info *sframe_info = &pbi->sframe_info;
sframe_info->is_s_frame = 0;
sframe_info->is_s_frame_at_altref = 0;
if (!pbi->sequence_header_ready) {
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "No sequence header");
}
if (seq_params->reduced_still_picture_hdr) {
cm->show_existing_frame = 0;
cm->show_frame = 1;
current_frame->frame_type = KEY_FRAME; if (pbi->sequence_header_changed) { // This is the start of a new coded video sequence.
pbi->sequence_header_changed = 0;
pbi->decoding_first_frame = 1;
reset_frame_buffers(cm);
}
features->error_resilient_mode = 1;
} else {
cm->show_existing_frame = aom_rb_read_bit(rb);
pbi->reset_decoder_state = 0;
if (cm->show_existing_frame) { if (pbi->sequence_header_changed) {
aom_internal_error(
&pbi->error, AOM_CODEC_CORRUPT_FRAME, "New sequence header starts with a show_existing_frame.");
} // Show an existing frame directly. constint existing_frame_idx = aom_rb_read_literal(rb, 3);
RefCntBuffer *const frame_to_show = cm->ref_frame_map[existing_frame_idx]; if (frame_to_show == NULL) {
aom_internal_error(&pbi->error, AOM_CODEC_UNSUP_BITSTREAM, "Buffer does not contain a decoded frame");
} if (seq_params->decoder_model_info_present_flag &&
seq_params->timing_info.equal_picture_interval == 0) {
read_temporal_point_info(cm, rb);
} if (seq_params->frame_id_numbers_present_flag) { int frame_id_length = seq_params->frame_id_length; int display_frame_id = aom_rb_read_literal(rb, frame_id_length); /* Compare display_frame_id with ref_frame_id and check valid for
* referencing */ if (display_frame_id != cm->ref_frame_id[existing_frame_idx] ||
pbi->valid_for_referencing[existing_frame_idx] == 0)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Reference buffer frame ID mismatch");
}
lock_buffer_pool(pool);
assert(frame_to_show->ref_count > 0); // cm->cur_frame should be the buffer referenced by the return value // of the get_free_fb() call in assign_cur_frame_new_fb() (called by // av1_receive_compressed_data()), so the ref_count should be 1.
assert(cm->cur_frame->ref_count == 1); // assign_frame_buffer_p() decrements ref_count directly rather than // call decrease_ref_count(). If cm->cur_frame->raw_frame_buffer has // already been allocated, it will not be released by // assign_frame_buffer_p()!
assert(!cm->cur_frame->raw_frame_buffer.data);
assign_frame_buffer_p(&cm->cur_frame, frame_to_show);
pbi->reset_decoder_state = frame_to_show->frame_type == KEY_FRAME;
unlock_buffer_pool(pool);
// Section 6.8.2: It is a requirement of bitstream conformance that when // show_existing_frame is used to show a previous frame, that the value // of showable_frame for the previous frame was equal to 1. if (!frame_to_show->showable_frame) {
aom_internal_error(&pbi->error, AOM_CODEC_UNSUP_BITSTREAM, "Buffer does not contain a showable frame");
} // Section 6.8.2: It is a requirement of bitstream conformance that when // show_existing_frame is used to show a previous frame with // RefFrameType[ frame_to_show_map_idx ] equal to KEY_FRAME, that the // frame is output via the show_existing_frame mechanism at most once. if (pbi->reset_decoder_state) frame_to_show->showable_frame = 0;
current_frame->frame_type = (FRAME_TYPE)aom_rb_read_literal(rb, 2); if (pbi->sequence_header_changed) { if (current_frame->frame_type == KEY_FRAME) { // This is the start of a new coded video sequence.
pbi->sequence_header_changed = 0;
pbi->decoding_first_frame = 1;
reset_frame_buffers(cm);
} else {
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Sequence header has changed without a keyframe.");
}
}
cm->show_frame = aom_rb_read_bit(rb); if (cm->show_frame == 0) pbi->is_arf_frame_present = 1; if (cm->show_frame == 0 && cm->current_frame.frame_type == KEY_FRAME)
pbi->is_fwd_kf_present = 1; if (cm->current_frame.frame_type == S_FRAME) {
sframe_info->is_s_frame = 1;
sframe_info->is_s_frame_at_altref = cm->show_frame ? 0 : 1;
} if (seq_params->still_picture &&
(current_frame->frame_type != KEY_FRAME || !cm->show_frame)) {
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Still pictures must be coded as shown keyframes");
}
cm->showable_frame = current_frame->frame_type != KEY_FRAME; if (cm->show_frame) { if (seq_params->decoder_model_info_present_flag &&
seq_params->timing_info.equal_picture_interval == 0)
read_temporal_point_info(cm, rb);
} else { // See if this frame can be used as show_existing_frame in future
cm->showable_frame = aom_rb_read_bit(rb);
}
cm->cur_frame->showable_frame = cm->showable_frame;
features->error_resilient_mode =
frame_is_sframe(cm) ||
(current_frame->frame_type == KEY_FRAME && cm->show_frame)
? 1
: aom_rb_read_bit(rb);
}
if (current_frame->frame_type == KEY_FRAME && cm->show_frame) { /* All frames need to be marked as not valid for referencing */ for (int i = 0; i < REF_FRAMES; i++) {
pbi->valid_for_referencing[i] = 0;
}
}
features->disable_cdf_update = aom_rb_read_bit(rb); if (seq_params->force_screen_content_tools == 2) {
features->allow_screen_content_tools = aom_rb_read_bit(rb);
} else {
features->allow_screen_content_tools =
seq_params->force_screen_content_tools;
}
for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) {
cm->remapped_ref_idx[i] = INVALID_IDX;
} if (pbi->need_resync) {
reset_ref_frame_map(cm);
pbi->need_resync = 0;
}
} else { if (current_frame->frame_type == INTRA_ONLY_FRAME) {
current_frame->refresh_frame_flags = aom_rb_read_literal(rb, REF_FRAMES); if (current_frame->refresh_frame_flags == 0xFF) {
aom_internal_error(&pbi->error, AOM_CODEC_UNSUP_BITSTREAM, "Intra only frames cannot have refresh flags 0xFF");
} if (pbi->need_resync) {
reset_ref_frame_map(cm);
pbi->need_resync = 0;
}
} elseif (pbi->need_resync != 1) { /* Skip if need resync */
current_frame->refresh_frame_flags =
frame_is_sframe(cm) ? 0xFF : aom_rb_read_literal(rb, REF_FRAMES);
}
}
if (!frame_is_intra_only(cm) || current_frame->refresh_frame_flags != 0xFF) { // Read all ref frame order hints if error_resilient_mode == 1 if (features->error_resilient_mode &&
seq_params->order_hint_info.enable_order_hint) { for (int ref_idx = 0; ref_idx < REF_FRAMES; ref_idx++) { // Read order hint from bit stream unsignedint order_hint = aom_rb_read_literal(
rb, seq_params->order_hint_info.order_hint_bits_minus_1 + 1); // Get buffer
RefCntBuffer *buf = cm->ref_frame_map[ref_idx]; if (buf == NULL || order_hint != buf->order_hint) { if (buf != NULL) {
lock_buffer_pool(pool);
decrease_ref_count(buf, pool);
unlock_buffer_pool(pool);
cm->ref_frame_map[ref_idx] = NULL;
} // If no corresponding buffer exists, allocate a new buffer with all // pixels set to neutral grey. int buf_idx = get_free_fb(cm); if (buf_idx == INVALID_IDX) {
aom_internal_error(&pbi->error, AOM_CODEC_MEM_ERROR, "Unable to find free frame buffer");
}
buf = &frame_bufs[buf_idx];
lock_buffer_pool(pool); #if CONFIG_SIZE_LIMIT if (seq_params->max_frame_width > DECODE_WIDTH_LIMIT ||
seq_params->max_frame_height > DECODE_HEIGHT_LIMIT) {
decrease_ref_count(buf, pool);
unlock_buffer_pool(pool);
aom_internal_error(
cm->error, AOM_CODEC_CORRUPT_FRAME, "Dimensions of %dx%d beyond allowed size of %dx%d.",
seq_params->max_frame_width, seq_params->max_frame_height,
DECODE_WIDTH_LIMIT, DECODE_HEIGHT_LIMIT);
} #endif if (aom_realloc_frame_buffer(
&buf->buf, seq_params->max_frame_width,
seq_params->max_frame_height, seq_params->subsampling_x,
seq_params->subsampling_y, seq_params->use_highbitdepth,
AOM_BORDER_IN_PIXELS, features->byte_alignment,
&buf->raw_frame_buffer, pool->get_fb_cb, pool->cb_priv, false,
0)) {
decrease_ref_count(buf, pool);
unlock_buffer_pool(pool);
aom_internal_error(&pbi->error, AOM_CODEC_MEM_ERROR, "Failed to allocate frame buffer");
}
unlock_buffer_pool(pool); // According to the specification, valid bitstreams are required to // never use missing reference frames so the filling process for // missing frames is not normatively defined and RefValid for missing // frames is set to 0.
// To make libaom more robust when the bitstream has been corrupted // by the loss of some frames of data, this code adds a neutral grey // buffer in place of missing frames, i.e. //
set_planes_to_neutral_grey(seq_params, &buf->buf, 0); // // and allows the frames to be used for referencing, i.e. //
pbi->valid_for_referencing[ref_idx] = 1; // // Please note such behavior is not normative and other decoders may // use a different approach.
cm->ref_frame_map[ref_idx] = buf;
buf->order_hint = order_hint;
}
}
}
}
if (current_frame->frame_type == KEY_FRAME) {
setup_frame_size(cm, frame_size_override_flag, rb);
if (current_frame->frame_type == INTRA_ONLY_FRAME) {
cm->cur_frame->film_grain_params_present =
seq_params->film_grain_params_present;
setup_frame_size(cm, frame_size_override_flag, rb); if (features->allow_screen_content_tools && !av1_superres_scaled(cm))
features->allow_intrabc = aom_rb_read_bit(rb);
} elseif (pbi->need_resync != 1) { /* Skip if need resync */ int frame_refs_short_signaling = 0; // Frame refs short signaling is off when error resilient mode is on. if (seq_params->order_hint_info.enable_order_hint)
frame_refs_short_signaling = aom_rb_read_bit(rb);
// Most of the time, streams start with a keyframe. In that case, // ref_frame_map will have been filled in at that point and will not // contain any NULLs. However, streams are explicitly allowed to start // with an intra-only frame, so long as they don't then signal a // reference to a slot that hasn't been set yet. That's what we are // checking here. if (lst_buf == NULL)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Inter frame requests nonexistent reference"); if (gld_buf == NULL)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Inter frame requests nonexistent reference");
for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) { int ref = 0; if (!frame_refs_short_signaling) {
ref = aom_rb_read_literal(rb, REF_FRAMES_LOG2);
// Most of the time, streams start with a keyframe. In that case, // ref_frame_map will have been filled in at that point and will not // contain any NULLs. However, streams are explicitly allowed to start // with an intra-only frame, so long as they don't then signal a // reference to a slot that hasn't been set yet. That's what we are // checking here. if (cm->ref_frame_map[ref] == NULL)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Inter frame requests nonexistent reference");
cm->remapped_ref_idx[i] = ref;
} else {
ref = cm->remapped_ref_idx[i];
} // Check valid for referencing if (pbi->valid_for_referencing[ref] == 0)
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Reference frame not valid for referencing");
cm->ref_frame_sign_bias[LAST_FRAME + i] = 0;
if (seq_params->frame_id_numbers_present_flag) { int frame_id_length = seq_params->frame_id_length; int diff_len = seq_params->delta_frame_id_length; int delta_frame_id_minus_1 = aom_rb_read_literal(rb, diff_len); int ref_frame_id =
((cm->current_frame_id - (delta_frame_id_minus_1 + 1) +
(1 << frame_id_length)) %
(1 << frame_id_length)); // Compare values derived from delta_frame_id_minus_1 and // refresh_frame_flags. if (ref_frame_id != cm->ref_frame_id[ref])
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Reference buffer frame ID mismatch");
}
}
#if CONFIG_BITSTREAM_DEBUG if (cm->seq_params->order_hint_info.enable_order_hint) {
aom_bitstream_queue_set_frame_read(cm->current_frame.order_hint * 2 +
cm->show_frame);
} else { // This is currently used in RTC encoding. cm->show_frame is always 1.
assert(cm->show_frame);
aom_bitstream_queue_set_frame_read(cm->current_frame.frame_number);
} #endif #if CONFIG_MISMATCH_DEBUG
mismatch_move_frame_idx_r(); #endif
for (int i = LAST_FRAME; i <= ALTREF_FRAME; ++i) {
cm->global_motion[i] = default_warp_params;
cm->cur_frame->global_motion[i] = default_warp_params;
}
xd->global_motion = cm->global_motion;
read_uncompressed_header(pbi, rb);
if (trailing_bits_present) av1_check_trailing_bits(pbi, rb);
// If the bit stream is monochrome, set the U and V buffers to a constant. if (num_planes < 3) {
set_planes_to_neutral_grey(cm->seq_params, xd->cur_buf, 1);
}
if (!cm->features.allow_intrabc && !tiles->single_tile_decoding) { if (cm->lf.filter_level[0] || cm->lf.filter_level[1]) {
av1_loop_filter_frame_mt(&cm->cur_frame->buf, cm, &pbi->dcb.xd, 0,
num_planes, 0, pbi->tile_workers,
pbi->num_workers, &pbi->lf_row_sync, 0);
}
constint do_cdef =
!pbi->skip_loop_filter && !cm->features.coded_lossless &&
(cm->cdef_info.cdef_bits || cm->cdef_info.cdef_strengths[0] ||
cm->cdef_info.cdef_uv_strengths[0]); constint do_superres = av1_superres_scaled(cm); constint optimized_loop_restoration = !do_cdef && !do_superres; constint do_loop_restoration =
cm->rst_info[0].frame_restoration_type != RESTORE_NONE ||
cm->rst_info[1].frame_restoration_type != RESTORE_NONE ||
cm->rst_info[2].frame_restoration_type != RESTORE_NONE; // Frame border extension is not required in the decoder // as it happens in extend_mc_border(). int do_extend_border_mt = 0; if (!optimized_loop_restoration) { if (do_loop_restoration)
av1_loop_restoration_save_boundary_lines(&pbi->common.cur_frame->buf,
cm, 0);
if (do_cdef) { if (pbi->num_workers > 1) {
av1_cdef_frame_mt(cm, &pbi->dcb.xd, pbi->cdef_worker,
pbi->tile_workers, &pbi->cdef_sync,
pbi->num_workers, av1_cdef_init_fb_row_mt,
do_extend_border_mt);
} else {
av1_cdef_frame(&pbi->common.cur_frame->buf, cm, &pbi->dcb.xd,
av1_cdef_init_fb_row);
}
}
superres_post_decode(pbi);
if (do_loop_restoration) {
av1_loop_restoration_save_boundary_lines(&pbi->common.cur_frame->buf,
cm, 1); if (pbi->num_workers > 1) {
av1_loop_restoration_filter_frame_mt(
(YV12_BUFFER_CONFIG *)xd->cur_buf, cm, optimized_loop_restoration,
pbi->tile_workers, pbi->num_workers, &pbi->lr_row_sync,
&pbi->lr_ctxt, do_extend_border_mt);
} else {
av1_loop_restoration_filter_frame((YV12_BUFFER_CONFIG *)xd->cur_buf,
cm, optimized_loop_restoration,
&pbi->lr_ctxt);
}
}
} else { // In no cdef and no superres case. Provide an optimized version of // loop_restoration_filter. if (do_loop_restoration) { if (pbi->num_workers > 1) {
av1_loop_restoration_filter_frame_mt(
(YV12_BUFFER_CONFIG *)xd->cur_buf, cm, optimized_loop_restoration,
pbi->tile_workers, pbi->num_workers, &pbi->lr_row_sync,
&pbi->lr_ctxt, do_extend_border_mt);
} else {
av1_loop_restoration_filter_frame((YV12_BUFFER_CONFIG *)xd->cur_buf,
cm, optimized_loop_restoration,
&pbi->lr_ctxt);
}
}
}
}
if (!pbi->dcb.corrupted) { if (cm->features.refresh_frame_context == REFRESH_FRAME_CONTEXT_BACKWARD) {
assert(pbi->context_update_tile_id < pbi->allocated_tiles);
*cm->fc = pbi->tile_data[pbi->context_update_tile_id].tctx;
av1_reset_cdf_symbol_counters(cm->fc);
}
} else {
aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "Decode failed. Frame data is corrupted.");
}
// Non frame parallel update frame context here. if (!tiles->large_scale) {
cm->cur_frame->frame_context = *cm->fc;
}
if (cm->show_frame && !cm->seq_params->order_hint_info.enable_order_hint) {
++cm->current_frame.frame_number;
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.106Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.