Commit 80188d15 authored by Thomas Davies's avatar Thomas Davies Committed by Yaowu Xu

Encode and decode multiple tile groups

This is a manual adaptation of the following commit from aom/master:
ce12003d

The original commit message:

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 18ee02b0
......@@ -391,11 +391,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;
......
......@@ -56,6 +56,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;
......@@ -2473,6 +2481,17 @@ static void read_tile_info(AV1Decoder *const pbi,
pbi->tile_size_bytes = aom_rb_read_literal(rb, 2) + 1;
}
#endif // CONFIG_EXT_TILE
#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
}
static int mem_get_varsize(const uint8_t *src, const int sz) {
......@@ -2670,6 +2689,43 @@ static void get_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;
#if CONFIG_TILE_GROUPS
int r, c;
const int tile_cols = cm->tile_cols;
const int tile_rows = cm->tile_rows;
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);
TileBufferDec *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->tile_size_bytes, is_last,
&pbi->common.error, &data, pbi->decrypt_cb,
pbi->decrypt_state, buf);
}
}
#else
int r, c;
const int tile_cols = cm->tile_cols;
const int tile_rows = cm->tile_rows;
......@@ -2683,6 +2739,7 @@ static void get_tile_buffers(
&data, pbi->decrypt_cb, pbi->decrypt_state, buf);
}
}
#endif
}
#endif // CONFIG_EXT_TILE
......@@ -3519,7 +3576,6 @@ static size_t read_uncompressed_header(AV1Decoder *pbi,
if (sz == 0)
aom_internal_error(&cm->error, AOM_CODEC_CORRUPT_FRAME,
"Invalid header size");
return sz;
}
......@@ -3945,7 +4001,56 @@ BITSTREAM_PROFILE av1_read_profile(struct aom_read_bit_buffer *rb) {
if (profile > 2) profile += aom_rb_read_bit(rb);
return (BITSTREAM_PROFILE)profile;
}
#if CONFIG_TILE_GROUPS
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 CONFIG_GLOBAL_MOTION
xd->global_motion = cm->global_motion;
#endif // CONFIG_GLOBAL_MOTION
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;
}
#endif
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;
......
......@@ -107,7 +107,13 @@ typedef struct AV1Decoder {
int acct_enabled;
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;
int av1_receive_compressed_data(struct AV1Decoder *pbi, size_t size,
......
This diff is collapsed.
......@@ -141,7 +141,13 @@ static void build_nmv_component_cost_table(int *mvcost,
static void update_mv(aom_writer *w, const unsigned int ct[2], aom_prob *cur_p,
aom_prob upd_p) {
(void)upd_p;
av1_cond_prob_diff_update(w, cur_p, ct);
#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, 1);
#endif
}
static void write_mv_update(const aom_tree_index *tree,
......
......@@ -4563,6 +4563,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],
......@@ -71,8 +72,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);
}
}
......@@ -294,6 +296,11 @@ void av1_choose_segmap_coding_method(AV1_COMMON *cm, MACROBLOCKD *xd) {
int t_pred_cost = INT_MAX;
int i, tile_col, tile_row, mi_row, mi_col;
#if CONFIG_TILE_GROUPS
const int probwt = cm->num_tg;
#else
const int probwt = 1;
#endif
unsigned(*temporal_predictor_count)[2] = cm->counts.seg.pred;
unsigned *no_pred_segcounts = cm->counts.seg.tree_total;
......@@ -333,14 +340,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.
......@@ -349,9 +357,9 @@ void av1_choose_segmap_coding_method(AV1_COMMON *cm, MACROBLOCKD *xd) {
const int count1 = temporal_predictor_count[i][1];
t_nopred_prob[i] = get_binary_prob(count0, count1);
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);
// Add in the predictor signaling cost
t_pred_cost += count0 * av1_cost_zero(t_nopred_prob[i]) +
......
......@@ -116,7 +116,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;
......@@ -126,7 +127,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;
......@@ -139,7 +140,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;
int newp;
const int step_sign = *bestp > oldp[PIVOT_NODE] ? -1 : 1;
......@@ -164,7 +165,7 @@ int av1_prob_diff_update_savings_search_model(const unsigned int *ct,
new_b += cost_branch256(ct + 2 * i, newplist[i]);
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;
......@@ -253,11 +254,11 @@ int av1_prob_update_search_model_subframe(unsigned int ct[ENTROPY_NODES]
#endif // CONFIG_ENTROPY
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);
......@@ -268,12 +269,12 @@ 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,22 @@ 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 av1_cond_prob_diff_update_savings(aom_prob *oldp, const unsigned int ct[2]);
int stepsize, int probwt);
int av1_cond_prob_diff_update_savings(aom_prob *oldp, const unsigned int ct[2],
int probwt);
#if CONFIG_ENTROPY
int av1_prob_update_search_subframe(unsigned int ct[][2], aom_prob oldp,
aom_prob *bestp, aom_prob upd, int n);
......
......@@ -290,6 +290,7 @@ EXPERIMENT_LIST="
adapt_scan
filter_7bit
parallel_deblocking
tile_groups
"
CONFIG_LIST="
dependency_tracking
......@@ -406,6 +407,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