Commit ce12003d authored by Thomas Davies's avatar Thomas Davies

Encode and decode multiple tile groups.

A tile group is a set of tiles in scan order.

Each tile group has a version of uncompressed and compressed headers,
identical apart from tile group parameters.
Encoding probability updates takes account of the number of
headers to control overheads.

The decoder supports arbitrary numbers of tile groups with
arbitrary number of tiles. The number of tiles in a TG is
signalled in the uncompressed header for that TG.

The encoder currently only supports a fixed number
of TGs (3, when error resilient mode is on) of equal size
(except possibly for the last one).

The average BDR performnce with 3 tile groups versus
anchor with error resilient mode and up to 16 tiles is:

NR YCbCr:      3.02%      3.04%      3.05%
PSNRHVS:      3.09%
SSIM:      3.06%
MSSSIM:      3.05%
CIEDE2000:      3.04%

Change-Id: I9b97c5ed733103b9160a3a5d4370de5322c00c0b
parent 75112626
......@@ -345,11 +345,15 @@ typedef struct AV1Common {
#if CONFIG_DERING
int dering_level;
#endif
#if CONFIG_DELTA_Q
int delta_q_present_flag;
// Resolution of delta quant
int delta_q_res;
#endif
#if CONFIG_TILE_GROUPS
int num_tg;
#endif
} AV1_COMMON;
// TODO(hkuang): Don't need to lock the whole pool after implementing atomic
......
......@@ -18,6 +18,10 @@ extern "C" {
struct AV1Common;
#if CONFIG_TILE_GROUPS
#define MAX_NUM_TG 3
#endif
typedef struct TileInfo {
int mi_row_start, mi_row_end;
int mi_col_start, mi_col_end;
......
......@@ -59,6 +59,14 @@
#define MAX_AV1_HEADER_SIZE 80
#define ACCT_STR __func__
static struct aom_read_bit_buffer *init_read_bit_buffer(
AV1Decoder *pbi, struct aom_read_bit_buffer *rb, const uint8_t *data,
const uint8_t *data_end, uint8_t clear_data[MAX_AV1_HEADER_SIZE]);
static int read_compressed_header(AV1Decoder *pbi, const uint8_t *data,
size_t partition_size);
static size_t read_uncompressed_header(AV1Decoder *pbi,
struct aom_read_bit_buffer *rb);
static int is_compound_reference_allowed(const AV1_COMMON *cm) {
int i;
if (frame_is_intra_only(cm)) return 0;
......@@ -1107,7 +1115,8 @@ static void setup_frame_size_with_refs(AV1_COMMON *cm,
pool->frame_bufs[cm->new_fb_idx].buf.render_height = cm->render_height;
}
static void setup_tile_info(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
static void setup_tile_info(AV1Decoder *pbi, struct aom_read_bit_buffer *rb) {
AV1_COMMON *cm = &pbi->common;
int min_log2_tile_cols, max_log2_tile_cols, max_ones;
av1_get_tile_n_bits(cm->mi_cols, &min_log2_tile_cols, &max_log2_tile_cols);
......@@ -1132,6 +1141,17 @@ static void setup_tile_info(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
#else
cm->tile_sz_mag = 3;
#endif
#if CONFIG_TILE_GROUPS
// Store an index to the location of the tile group information
pbi->tg_size_bit_offset = rb->bit_offset;
pbi->tg_size = 1 << (cm->log2_tile_rows + cm->log2_tile_cols);
if (cm->log2_tile_rows + cm->log2_tile_cols > 0) {
pbi->tg_start =
aom_rb_read_literal(rb, cm->log2_tile_rows + cm->log2_tile_cols);
pbi->tg_size =
1 + aom_rb_read_literal(rb, cm->log2_tile_rows + cm->log2_tile_cols);
}
#endif
}
typedef struct TileBuffer {
......@@ -1193,6 +1213,41 @@ static void get_tile_buffers(AV1Decoder *pbi, const uint8_t *data,
const uint8_t *data_end, int tile_cols,
int tile_rows,
TileBuffer (*tile_buffers)[1 << 6]) {
#if CONFIG_TILE_GROUPS
int r, c;
int tc = 0;
int first_tile_in_tg = 0;
int hdr_offset;
struct aom_read_bit_buffer rb_tg_hdr;
uint8_t clear_data[MAX_AV1_HEADER_SIZE];
const int num_tiles = tile_rows * tile_cols;
const int num_bits = OD_ILOG(num_tiles) - 1;
const int hdr_size = pbi->uncomp_hdr_size + pbi->first_partition_size;
const int tg_size_bit_offset = pbi->tg_size_bit_offset;
for (r = 0; r < tile_rows; ++r) {
for (c = 0; c < tile_cols; ++c, ++tc) {
const int is_last = (r == tile_rows - 1) && (c == tile_cols - 1);
TileBuffer *const buf = &tile_buffers[r][c];
hdr_offset = (tc && tc == first_tile_in_tg) ? hdr_size : 0;
buf->col = c;
if (hdr_offset) {
init_read_bit_buffer(pbi, &rb_tg_hdr, data, data_end, clear_data);
rb_tg_hdr.bit_offset = tg_size_bit_offset;
if (num_tiles) {
pbi->tg_start = aom_rb_read_literal(&rb_tg_hdr, num_bits);
pbi->tg_size = 1 + aom_rb_read_literal(&rb_tg_hdr, num_bits);
}
}
first_tile_in_tg += tc == first_tile_in_tg ? pbi->tg_size : 0;
data += hdr_offset;
get_tile_buffer(data_end, pbi->common.tile_sz_mag, is_last,
&pbi->common.error, &data, pbi->decrypt_cb,
pbi->decrypt_state, buf);
}
}
#else
int r, c;
for (r = 0; r < tile_rows; ++r) {
......@@ -1205,6 +1260,7 @@ static void get_tile_buffers(AV1Decoder *pbi, const uint8_t *data,
pbi->decrypt_state, buf);
}
}
#endif
}
static const uint8_t *decode_tiles(AV1Decoder *pbi, const uint8_t *data,
......@@ -1972,13 +2028,12 @@ static size_t read_uncompressed_header(AV1Decoder *pbi,
cm->reference_mode = read_frame_reference_mode(cm, rb);
#endif
setup_tile_info(cm, rb);
setup_tile_info(pbi, rb);
sz = aom_rb_read_literal(rb, 16);
if (sz == 0)
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Invalid header size");
return sz;
}
......@@ -2242,6 +2297,52 @@ BITSTREAM_PROFILE av1_read_profile(struct aom_read_bit_buffer *rb) {
return (BITSTREAM_PROFILE)profile;
}
static int read_all_headers(AV1Decoder *pbi, struct aom_read_bit_buffer *rb,
const uint8_t **p_data,
const uint8_t **p_data_end) {
AV1_COMMON *const cm = &pbi->common;
MACROBLOCKD *const xd = &pbi->mb;
YV12_BUFFER_CONFIG *fb = (YV12_BUFFER_CONFIG *)xd->cur_buf;
pbi->first_partition_size = read_uncompressed_header(pbi, rb);
pbi->uncomp_hdr_size = aom_rb_bytes_read(rb);
if (!pbi->first_partition_size) {
// showing a frame directly
#if CONFIG_EXT_REFS
if (cm->show_existing_frame)
*p_data_end = *p_data + pbi->uncomp_hdr_size;
else
#endif // CONFIG_EXT_REFS
*p_data_end = *p_data + (cm->profile <= PROFILE_2 ? 1 : 2);
return 1;
}
*p_data += pbi->uncomp_hdr_size;
if (!read_is_valid(*p_data, pbi->first_partition_size, *p_data_end))
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Truncated packet or corrupt header length");
*cm->fc = cm->frame_contexts[cm->frame_context_idx];
if (!cm->fc->initialized)
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Uninitialized entropy context.");
av1_zero(cm->counts);
xd->corrupted = 0;
fb->corrupted =
read_compressed_header(pbi, *p_data, pbi->first_partition_size);
if (fb->corrupted)
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Decode failed. Frame data header is corrupted.");
*p_data += pbi->first_partition_size;
return 0;
}
void av1_decode_frame(AV1Decoder *pbi, const uint8_t *data,
const uint8_t *data_end, const uint8_t **p_data_end) {
AV1_COMMON *const cm = &pbi->common;
......@@ -2249,9 +2350,9 @@ void av1_decode_frame(AV1Decoder *pbi, const uint8_t *data,
struct aom_read_bit_buffer rb;
int context_updated = 0;
uint8_t clear_data[MAX_AV1_HEADER_SIZE];
size_t first_partition_size;
const int tile_rows = 1 << cm->log2_tile_rows;
const int tile_cols = 1 << cm->log2_tile_cols;
int early_terminate;
YV12_BUFFER_CONFIG *const new_fb = get_frame_new_buffer(cm);
xd->cur_buf = new_fb;
......@@ -2259,24 +2360,11 @@ void av1_decode_frame(AV1Decoder *pbi, const uint8_t *data,
bitstream_queue_set_frame_read(cm->current_video_frame * 2 + cm->show_frame);
#endif
first_partition_size = read_uncompressed_header(
pbi, init_read_bit_buffer(pbi, &rb, data, data_end, clear_data));
early_terminate = read_all_headers(
pbi, init_read_bit_buffer(pbi, &rb, data, data_end, clear_data), &data,
&data_end);
if (!first_partition_size) {
// showing a frame directly
#if CONFIG_EXT_REFS
if (cm->show_existing_frame)
*p_data_end = data + aom_rb_bytes_read(&rb);
else
#endif // CONFIG_EXT_REFS
*p_data_end = data + (cm->profile <= PROFILE_2 ? 1 : 2);
return;
}
data += aom_rb_bytes_read(&rb);
if (!read_is_valid(data, first_partition_size, data_end))
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Truncated packet or corrupt header length");
if (early_terminate) return;
cm->use_prev_frame_mvs =
!cm->error_resilient_mode && cm->width == cm->last_width &&
......@@ -2302,19 +2390,6 @@ void av1_decode_frame(AV1Decoder *pbi, const uint8_t *data,
av1_setup_block_planes(xd, cm->subsampling_x, cm->subsampling_y);
*cm->fc = cm->frame_contexts[cm->frame_context_idx];
if (!cm->fc->initialized)
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Uninitialized entropy context.");
av1_zero(cm->counts);
xd->corrupted = 0;
new_fb->corrupted = read_compressed_header(pbi, data, first_partition_size);
if (new_fb->corrupted)
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Decode failed. Frame data header is corrupted.");
if (cm->lf.filter_level && !cm->skip_loop_filter) {
av1_loop_filter_frame_init(cm, cm->lf.filter_level);
}
......@@ -2340,7 +2415,7 @@ void av1_decode_frame(AV1Decoder *pbi, const uint8_t *data,
if (pbi->max_threads > 1 && tile_rows == 1 && tile_cols > 1) {
// Multi-threaded tile decoder
*p_data_end = decode_tiles_mt(pbi, data + first_partition_size, data_end);
*p_data_end = decode_tiles_mt(pbi, data, data_end);
if (!xd->corrupted) {
if (!cm->skip_loop_filter) {
// If multiple threads are used to decode tiles, then we use those
......@@ -2354,7 +2429,7 @@ void av1_decode_frame(AV1Decoder *pbi, const uint8_t *data,
"Decode failed. Frame data is corrupted.");
}
} else {
*p_data_end = decode_tiles(pbi, data + first_partition_size, data_end);
*p_data_end = decode_tiles(pbi, data, data_end);
}
#if CONFIG_CLPF
......
......@@ -89,6 +89,13 @@ typedef struct AV1Decoder {
int hold_ref_buf; // hold the reference buffer.
#if CONFIG_ACCOUNTING
Accounting accounting;
#endif
size_t uncomp_hdr_size; // Size of the uncompressed header
size_t first_partition_size; // Size of the compressed header
#if CONFIG_TILE_GROUPS
int tg_size; // Number of tiles in the current tilegroup
int tg_start; // First tile in the current tilegroup
int tg_size_bit_offset;
#endif
} AV1Decoder;
......
This diff is collapsed.
......@@ -142,7 +142,13 @@ static void update_mv(aom_writer *w, const unsigned int ct[2], aom_prob *cur_p,
aom_prob upd_p) {
#if CONFIG_MISC_FIXES
(void)upd_p;
#if CONFIG_TILE_GROUPS
// Just use the maximum number of tile groups to avoid passing in the actual
// number
av1_cond_prob_diff_update(w, cur_p, ct, MAX_NUM_TG);
#else
av1_cond_prob_diff_update(w, cur_p, ct);
#endif
#else
const aom_prob new_p = get_binary_prob(ct[0], ct[1]) | 1;
const int update = cost_branch256(ct, *cur_p) + av1_cost_zero(upd_p) >
......
......@@ -3720,6 +3720,12 @@ static void encode_frame_to_data_rate(AV1_COMP *cpi, size_t *size,
cm->reset_frame_context = RESET_FRAME_CONTEXT_CURRENT;
}
}
#if CONFIG_TILE_GROUPS
if (cm->error_resilient_mode)
cm->num_tg = MAX_NUM_TG;
else
cm->num_tg = 1;
#endif
// For 1 pass CBR, check if we are dropping this frame.
// Never drop on key frame.
......
......@@ -51,7 +51,8 @@ void av1_clear_segdata(struct segmentation *seg, int segment_id,
// Based on set of segment counts calculate a probability tree
static void calc_segtree_probs(unsigned *segcounts,
aom_prob *segment_tree_probs,
const aom_prob *cur_tree_probs) {
const aom_prob *cur_tree_probs,
const int probwt) {
// Work out probabilities of each segment
const unsigned cc[4] = { segcounts[0] + segcounts[1],
segcounts[2] + segcounts[3],
......@@ -60,6 +61,8 @@ static void calc_segtree_probs(unsigned *segcounts,
const unsigned ccc[2] = { cc[0] + cc[1], cc[2] + cc[3] };
#if CONFIG_MISC_FIXES
int i;
#else
(void)probwt;
#endif
segment_tree_probs[0] = get_binary_prob(ccc[0], ccc[1]);
......@@ -74,8 +77,9 @@ static void calc_segtree_probs(unsigned *segcounts,
for (i = 0; i < 7; i++) {
const unsigned *ct =
i == 0 ? ccc : i < 3 ? cc + (i & 2) : segcounts + (i - 3) * 2;
av1_prob_diff_update_savings_search(
ct, cur_tree_probs[i], &segment_tree_probs[i], DIFF_UPDATE_PROB);
av1_prob_diff_update_savings_search(ct, cur_tree_probs[i],
&segment_tree_probs[i],
DIFF_UPDATE_PROB, probwt);
}
#else
(void)cur_tree_probs;
......@@ -216,6 +220,11 @@ void av1_choose_segmap_coding_method(AV1_COMMON *cm, MACROBLOCKD *xd) {
int t_pred_cost = INT_MAX;
int i, tile_col, mi_row, mi_col;
#if CONFIG_TILE_GROUPS
const int probwt = cm->num_tg;
#else
const int probwt = 1;
#endif
#if CONFIG_MISC_FIXES
unsigned(*temporal_predictor_count)[2] = cm->counts.seg.pred;
......@@ -262,14 +271,15 @@ void av1_choose_segmap_coding_method(AV1_COMMON *cm, MACROBLOCKD *xd) {
// Work out probability tree for coding segments without prediction
// and the cost.
calc_segtree_probs(no_pred_segcounts, no_pred_tree, segp->tree_probs);
calc_segtree_probs(no_pred_segcounts, no_pred_tree, segp->tree_probs, probwt);
no_pred_cost = cost_segmap(no_pred_segcounts, no_pred_tree);
// Key frames cannot use temporal prediction
if (!frame_is_intra_only(cm) && !cm->error_resilient_mode) {
// Work out probability tree for coding those segments not
// predicted using the temporal method and the cost.
calc_segtree_probs(t_unpred_seg_counts, t_pred_tree, segp->tree_probs);
calc_segtree_probs(t_unpred_seg_counts, t_pred_tree, segp->tree_probs,
probwt);
t_pred_cost = cost_segmap(t_unpred_seg_counts, t_pred_tree);
// Add in the cost of the signaling for each prediction context.
......@@ -279,9 +289,9 @@ void av1_choose_segmap_coding_method(AV1_COMMON *cm, MACROBLOCKD *xd) {
t_nopred_prob[i] = get_binary_prob(count0, count1);
#if CONFIG_MISC_FIXES
av1_prob_diff_update_savings_search(temporal_predictor_count[i],
segp->pred_probs[i],
&t_nopred_prob[i], DIFF_UPDATE_PROB);
av1_prob_diff_update_savings_search(
temporal_predictor_count[i], segp->pred_probs[i], &t_nopred_prob[i],
DIFF_UPDATE_PROB, probwt);
#endif
// Add in the predictor signaling cost
......
......@@ -122,7 +122,8 @@ void av1_write_prob_diff_update(aom_writer *w, aom_prob newp, aom_prob oldp) {
}
int av1_prob_diff_update_savings_search(const unsigned int *ct, aom_prob oldp,
aom_prob *bestp, aom_prob upd) {
aom_prob *bestp, aom_prob upd,
int probwt) {
const uint32_t old_b = cost_branch256(ct, oldp);
int bestsavings = 0;
aom_prob newp, bestnewp = oldp;
......@@ -132,7 +133,7 @@ int av1_prob_diff_update_savings_search(const unsigned int *ct, aom_prob oldp,
const uint32_t new_b = cost_branch256(ct, newp);
const uint32_t update_b =
prob_diff_update_cost(newp, oldp) + av1_cost_upd256;
const int savings = (int)((int64_t)old_b - new_b - update_b);
const int savings = (int)((int64_t)old_b - new_b - update_b * probwt);
if (savings > bestsavings) {
bestsavings = savings;
bestnewp = newp;
......@@ -145,7 +146,7 @@ int av1_prob_diff_update_savings_search(const unsigned int *ct, aom_prob oldp,
int av1_prob_diff_update_savings_search_model(const unsigned int *ct,
const aom_prob *oldp,
aom_prob *bestp, aom_prob upd,
int stepsize) {
int stepsize, int probwt) {
int i, old_b, new_b, update_b, savings, bestsavings, step;
int newp;
aom_prob bestnewp, newplist[ENTROPY_NODES], oldplist[ENTROPY_NODES];
......@@ -169,7 +170,7 @@ int av1_prob_diff_update_savings_search_model(const unsigned int *ct,
new_b += cost_branch256(ct + 2 * PIVOT_NODE, newplist[PIVOT_NODE]);
update_b =
prob_diff_update_cost(newp, oldp[PIVOT_NODE]) + av1_cost_upd256;
savings = old_b - new_b - update_b;
savings = old_b - new_b - update_b * probwt;
if (savings > bestsavings) {
bestsavings = savings;
bestnewp = newp;
......@@ -186,7 +187,7 @@ int av1_prob_diff_update_savings_search_model(const unsigned int *ct,
new_b += cost_branch256(ct + 2 * PIVOT_NODE, newplist[PIVOT_NODE]);
update_b =
prob_diff_update_cost(newp, oldp[PIVOT_NODE]) + av1_cost_upd256;
savings = old_b - new_b - update_b;
savings = old_b - new_b - probwt * update_b;
if (savings > bestsavings) {
bestsavings = savings;
bestnewp = newp;
......@@ -199,11 +200,11 @@ int av1_prob_diff_update_savings_search_model(const unsigned int *ct,
}
void av1_cond_prob_diff_update(aom_writer *w, aom_prob *oldp,
const unsigned int ct[2]) {
const unsigned int ct[2], int probwt) {
const aom_prob upd = DIFF_UPDATE_PROB;
aom_prob newp = get_binary_prob(ct[0], ct[1]);
const int savings =
av1_prob_diff_update_savings_search(ct, *oldp, &newp, upd);
av1_prob_diff_update_savings_search(ct, *oldp, &newp, upd, probwt);
assert(newp >= 1);
if (savings > 0) {
aom_write(w, 1, upd);
......@@ -214,11 +215,11 @@ void av1_cond_prob_diff_update(aom_writer *w, aom_prob *oldp,
}
}
int av1_cond_prob_diff_update_savings(aom_prob *oldp,
const unsigned int ct[2]) {
int av1_cond_prob_diff_update_savings(aom_prob *oldp, const unsigned int ct[2],
int probwt) {
const aom_prob upd = DIFF_UPDATE_PROB;
aom_prob newp = get_binary_prob(ct[0], ct[1]);
const int savings =
av1_prob_diff_update_savings_search(ct, *oldp, &newp, upd);
av1_prob_diff_update_savings_search(ct, *oldp, &newp, upd, probwt);
return savings;
}
......@@ -19,20 +19,23 @@ extern "C" {
#include "aom_dsp/bitwriter.h"
#include "aom_dsp/prob.h"
void av1_write_prob_diff_update(aom_writer *w, aom_prob newp, aom_prob oldp);
void av1_write_prob_diff_update(aom_writer *w, aom_prob newp, aom_prob oldpm);
void av1_cond_prob_diff_update(aom_writer *w, aom_prob *oldp,
const unsigned int ct[2]);
const unsigned int ct[2], int probwt);
int av1_prob_diff_update_savings_search(const unsigned int *ct, aom_prob oldp,
aom_prob *bestp, aom_prob upd);
aom_prob *bestp, aom_prob upd,
int probwt);
int av1_prob_diff_update_savings_search_model(const unsigned int *ct,
const aom_prob *oldp,
aom_prob *bestp, aom_prob upd,
int stepsize);
int stepsize, int probwt);
int av1_cond_prob_diff_update_savings(aom_prob *oldp, const unsigned int ct[2],
int probwt);
int av1_cond_prob_diff_update_savings(aom_prob *oldp, const unsigned int ct[2]);
#ifdef __cplusplus
} // extern "C"
#endif
......
......@@ -273,6 +273,7 @@ EXPERIMENT_LIST="
delta_q
adapt_scan
bitstream_debug
tile_groups
"
CONFIG_LIST="
dependency_tracking
......@@ -381,6 +382,7 @@ CMDLINE_SELECT="
aom_highbitdepth
experimental
aom_qm
tile_groups
"
process_cmdline() {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment