/* * VP9 compatible video decoder * * Copyright (C) 2013 Ronald S. Bultje <rsbultje gmail com> * Copyright (C) 2013 Clément Bœsch <u pkh me> * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
if (s->s.h.segmentation.enabled && s->s.h.segmentation.feat[b->seg_id].ref_enabled) {
av_assert2(s->s.h.segmentation.feat[b->seg_id].ref_val != 0);
b->comp = 0;
b->ref[0] = s->s.h.segmentation.feat[b->seg_id].ref_val - 1;
} else { // read comp_pred flag if (s->s.h.comppredmode != PRED_SWITCHABLE) {
b->comp = s->s.h.comppredmode == PRED_COMPREF;
} else { int c;
// FIXME add intra as ref=0xff (or -1) to make these easier? if (have_a) { if (have_l) { if (s->above_comp_ctx[col] && td->left_comp_ctx[row7]) {
c = 4;
} elseif (s->above_comp_ctx[col]) {
c = 2 + (td->left_intra_ctx[row7] ||
td->left_ref_ctx[row7] == s->s.h.fixcompref);
} elseif (td->left_comp_ctx[row7]) {
c = 2 + (s->above_intra_ctx[col] ||
s->above_ref_ctx[col] == s->s.h.fixcompref);
} else {
c = (!s->above_intra_ctx[col] &&
s->above_ref_ctx[col] == s->s.h.fixcompref) ^
(!td->left_intra_ctx[row7] &&
td->left_ref_ctx[row & 7] == s->s.h.fixcompref);
}
} else {
c = s->above_comp_ctx[col] ? 3 :
(!s->above_intra_ctx[col] && s->above_ref_ctx[col] == s->s.h.fixcompref);
}
} elseif (have_l) {
c = td->left_comp_ctx[row7] ? 3 :
(!td->left_intra_ctx[row7] && td->left_ref_ctx[row7] == s->s.h.fixcompref);
} else {
c = 1;
}
b->comp = vpx_rac_get_prob(td->c, s->prob.p.comp[c]);
td->counts.comp[c][b->comp]++;
}
// read actual references // FIXME probably cache a few variables here to prevent repetitive // memory accesses below if (b->comp) { /* two references */ int fix_idx = s->s.h.signbias[s->s.h.fixcompref], var_idx = !fix_idx, c, bit;
b->ref[fix_idx] = s->s.h.fixcompref; // FIXME can this codeblob be replaced by some sort of LUT? if (have_a) { if (have_l) { if (s->above_intra_ctx[col]) { if (td->left_intra_ctx[row7]) {
c = 2;
} else {
c = 1 + 2 * (td->left_ref_ctx[row7] != s->s.h.varcompref[1]);
}
} elseif (td->left_intra_ctx[row7]) {
c = 1 + 2 * (s->above_ref_ctx[col] != s->s.h.varcompref[1]);
} else { int refl = td->left_ref_ctx[row7], refa = s->above_ref_ctx[col];
if (refl == refa && refa == s->s.h.varcompref[1]) {
c = 0;
} elseif (!td->left_comp_ctx[row7] && !s->above_comp_ctx[col]) { if ((refa == s->s.h.fixcompref && refl == s->s.h.varcompref[0]) ||
(refl == s->s.h.fixcompref && refa == s->s.h.varcompref[0])) {
c = 4;
} else {
c = (refa == refl) ? 3 : 1;
}
} elseif (!td->left_comp_ctx[row7]) { if (refa == s->s.h.varcompref[1] && refl != s->s.h.varcompref[1]) {
c = 1;
} else {
c = (refl == s->s.h.varcompref[1] &&
refa != s->s.h.varcompref[1]) ? 2 : 4;
}
} elseif (!s->above_comp_ctx[col]) { if (refl == s->s.h.varcompref[1] && refa != s->s.h.varcompref[1]) {
c = 1;
} else {
c = (refa == s->s.h.varcompref[1] &&
refl != s->s.h.varcompref[1]) ? 2 : 4;
}
} else {
c = (refl == refa) ? 4 : 2;
}
}
} else { if (s->above_intra_ctx[col]) {
c = 2;
} elseif (s->above_comp_ctx[col]) {
c = 4 * (s->above_ref_ctx[col] != s->s.h.varcompref[1]);
} else {
c = 3 * (s->above_ref_ctx[col] != s->s.h.varcompref[1]);
}
}
} elseif (have_l) { if (td->left_intra_ctx[row7]) {
c = 2;
} elseif (td->left_comp_ctx[row7]) {
c = 4 * (td->left_ref_ctx[row7] != s->s.h.varcompref[1]);
} else {
c = 3 * (td->left_ref_ctx[row7] != s->s.h.varcompref[1]);
}
} else {
c = 2;
}
bit = vpx_rac_get_prob(td->c, s->prob.p.comp_ref[c]);
b->ref[var_idx] = s->s.h.varcompref[bit];
td->counts.comp_ref[c][bit]++;
} else/* single reference */ { int bit, c;
if (have_a && !s->above_intra_ctx[col]) { if (have_l && !td->left_intra_ctx[row7]) { if (td->left_comp_ctx[row7]) { if (s->above_comp_ctx[col]) {
c = 1 + (!s->s.h.fixcompref || !td->left_ref_ctx[row7] ||
!s->above_ref_ctx[col]);
} else {
c = (3 * !s->above_ref_ctx[col]) +
(!s->s.h.fixcompref || !td->left_ref_ctx[row7]);
}
} elseif (s->above_comp_ctx[col]) {
c = (3 * !td->left_ref_ctx[row7]) +
(!s->s.h.fixcompref || !s->above_ref_ctx[col]);
} else {
c = 2 * !td->left_ref_ctx[row7] + 2 * !s->above_ref_ctx[col];
}
} elseif (s->above_intra_ctx[col]) {
c = 2;
} elseif (s->above_comp_ctx[col]) {
c = 1 + (!s->s.h.fixcompref || !s->above_ref_ctx[col]);
} else {
c = 4 * (!s->above_ref_ctx[col]);
}
} elseif (have_l && !td->left_intra_ctx[row7]) { if (td->left_intra_ctx[row7]) {
c = 2;
} elseif (td->left_comp_ctx[row7]) {
c = 1 + (!s->s.h.fixcompref || !td->left_ref_ctx[row7]);
} else {
c = 4 * (!td->left_ref_ctx[row7]);
}
} else {
c = 2;
}
bit = vpx_rac_get_prob(td->c, s->prob.p.single_ref[c][0]);
td->counts.single_ref[c][0][bit]++; if (!bit) {
b->ref[0] = 0;
} else { // FIXME can this codeblob be replaced by some sort of LUT? if (have_a) { if (have_l) { if (td->left_intra_ctx[row7]) { if (s->above_intra_ctx[col]) {
c = 2;
} elseif (s->above_comp_ctx[col]) {
c = 1 + 2 * (s->s.h.fixcompref == 1 ||
s->above_ref_ctx[col] == 1);
} elseif (!s->above_ref_ctx[col]) {
c = 3;
} else {
c = 4 * (s->above_ref_ctx[col] == 1);
}
} elseif (s->above_intra_ctx[col]) { if (td->left_intra_ctx[row7]) {
c = 2;
} elseif (td->left_comp_ctx[row7]) {
c = 1 + 2 * (s->s.h.fixcompref == 1 ||
td->left_ref_ctx[row7] == 1);
} elseif (!td->left_ref_ctx[row7]) {
c = 3;
} else {
c = 4 * (td->left_ref_ctx[row7] == 1);
}
} elseif (s->above_comp_ctx[col]) { if (td->left_comp_ctx[row7]) { if (td->left_ref_ctx[row7] == s->above_ref_ctx[col]) {
c = 3 * (s->s.h.fixcompref == 1 ||
td->left_ref_ctx[row7] == 1);
} else {
c = 2;
}
} elseif (!td->left_ref_ctx[row7]) {
c = 1 + 2 * (s->s.h.fixcompref == 1 ||
s->above_ref_ctx[col] == 1);
} else {
c = 3 * (td->left_ref_ctx[row7] == 1) +
(s->s.h.fixcompref == 1 || s->above_ref_ctx[col] == 1);
}
} elseif (td->left_comp_ctx[row7]) { if (!s->above_ref_ctx[col]) {
c = 1 + 2 * (s->s.h.fixcompref == 1 ||
td->left_ref_ctx[row7] == 1);
} else {
c = 3 * (s->above_ref_ctx[col] == 1) +
(s->s.h.fixcompref == 1 || td->left_ref_ctx[row7] == 1);
}
} elseif (!s->above_ref_ctx[col]) { if (!td->left_ref_ctx[row7]) {
c = 3;
} else {
c = 4 * (td->left_ref_ctx[row7] == 1);
}
} elseif (!td->left_ref_ctx[row7]) {
c = 4 * (s->above_ref_ctx[col] == 1);
} else {
c = 2 * (td->left_ref_ctx[row7] == 1) +
2 * (s->above_ref_ctx[col] == 1);
}
} else { if (s->above_intra_ctx[col] ||
(!s->above_comp_ctx[col] && !s->above_ref_ctx[col])) {
c = 2;
} elseif (s->above_comp_ctx[col]) {
c = 3 * (s->s.h.fixcompref == 1 || s->above_ref_ctx[col] == 1);
} else {
c = 4 * (s->above_ref_ctx[col] == 1);
}
}
} elseif (have_l) { if (td->left_intra_ctx[row7] ||
(!td->left_comp_ctx[row7] && !td->left_ref_ctx[row7])) {
c = 2;
} elseif (td->left_comp_ctx[row7]) {
c = 3 * (s->s.h.fixcompref == 1 || td->left_ref_ctx[row7] == 1);
} else {
c = 4 * (td->left_ref_ctx[row7] == 1);
}
} else {
c = 2;
}
bit = vpx_rac_get_prob(td->c, s->prob.p.single_ref[c][1]);
td->counts.single_ref[c][1][bit]++;
b->ref[0] = 1 + bit;
}
}
}
// FIXME this needs to use the LUT tables from find_ref_mvs // because not all are -1,0/0,-1 int c = inter_mode_ctx_lut[s->above_mode_ctx[col + off[b->bs]]]
[td->left_mode_ctx[row7 + off[b->bs]]];
static av_always_inline void mask_edges(uint8_t (*mask)[8][4], int ss_h, int ss_v, int row_and_7, int col_and_7, int w, int h, int col_end, int row_end, enum TxfmMode tx, int skip_inter)
{ staticconstunsigned wide_filter_col_mask[2] = { 0x11, 0x01 }; staticconstunsigned wide_filter_row_mask[2] = { 0x03, 0x07 };
// FIXME I'm pretty sure all loops can be replaced by a single LUT if // we make VP9Filter.mask uint64_t (i.e. row/col all single variable) // and make the LUT 5-indexed (bl, bp, is_uv, tx and row/col), and then // use row_and_7/col_and_7 as shifts (1*col_and_7+8*row_and_7)
// the intended behaviour of the vp9 loopfilter is to work on 8-pixel // edges. This means that for UV, we work on two subsampled blocks at // a time, and we only use the topleft block's mode information to set // things like block strength. Thus, for any block size smaller than // 16x16, ignore the odd portion of the block. if (tx == TX_4X4 && (ss_v | ss_h)) { if (h == ss_v) { if (row_and_7 & 1) return; if (!row_end)
h += 1;
} if (w == ss_h) { if (col_and_7 & 1) return; if (!col_end)
w += 1;
}
}
if (tx == TX_4X4 && !skip_inter) { int t = 1 << col_and_7, m_col = (t << w) - t, y; // on 32-px edges, use the 8-px wide loopfilter; else, use 4-px wide int m_row_8 = m_col & wide_filter_col_mask[ss_h], m_row_4 = m_col - m_row_8;
for (y = row_and_7; y < h + row_and_7; y++) { int col_mask_id = 2 - !(y & wide_filter_row_mask[ss_v]);
mask[0][y][1] |= m_row_8;
mask[0][y][2] |= m_row_4; // for odd lines, if the odd col is not being filtered, // skip odd row also: // .---. <-- a // | | // |___| <-- b // ^ ^ // c d // // if a/c are even row/col and b/d are odd, and d is skipped, // e.g. right edge of size-66x66.webm, then skip b also (bug) if ((ss_h & ss_v) && (col_end & 1) && (y & 1)) {
mask[1][y][col_mask_id] |= (t << (w - 1)) - t;
} else {
mask[1][y][col_mask_id] |= m_col;
} if (!ss_h)
mask[0][y][3] |= m_col; if (!ss_v) { if (ss_h && (col_end & 1))
mask[1][y][3] |= (t << (w - 1)) - t; else
mask[1][y][3] |= m_col;
}
}
} else { int y, t = 1 << col_and_7, m_col = (t << w) - t;
if (!skip_inter) { int mask_id = (tx == TX_8X8); int l2 = tx + ss_h - 1, step1d; staticconstunsigned masks[4] = { 0xff, 0x55, 0x11, 0x01 }; int m_row = m_col & masks[l2];
// at odd UV col/row edges tx16/tx32 loopfilter edges, force // 8wd loopfilter to prevent going off the visible edge. if (ss_h && tx > TX_8X8 && (w ^ (w - 1)) == 1) { int m_row_16 = ((t << (w - 1)) - t) & masks[l2]; int m_row_8 = m_row - m_row_16;
for (y = row_and_7; y < h + row_and_7; y++) {
mask[0][y][0] |= m_row_16;
mask[0][y][1] |= m_row_8;
}
} else { for (y = row_and_7; y < h + row_and_7; y++)
mask[0][y][mask_id] |= m_row;
}
l2 = tx + ss_v - 1;
step1d = 1 << l2; if (ss_v && tx > TX_8X8 && (h ^ (h - 1)) == 1) { for (y = row_and_7; y < h + row_and_7 - 1; y += step1d)
mask[1][y][0] |= m_col; if (y - row_and_7 == h - 1)
mask[1][y][1] |= m_col;
} else { for (y = row_and_7; y < h + row_and_7; y += step1d)
mask[1][y][mask_id] |= m_col;
}
} elseif (tx != TX_4X4) { int mask_id;
mask_id = (tx == TX_8X8) || (h == ss_v);
mask[1][row_and_7][mask_id] |= m_col;
mask_id = (tx == TX_8X8) || (w == ss_h); for (y = row_and_7; y < h + row_and_7; y++)
mask[0][y][mask_id] |= t;
} else { int t8 = t & wide_filter_col_mask[ss_h], t4 = t - t8;
for (y = row_and_7; y < h + row_and_7; y++) {
mask[0][y][2] |= t4;
mask[0][y][1] |= t8;
}
mask[1][row_and_7][2 - !(row_and_7 & wide_filter_row_mask[ss_v])] |= m_col;
}
}
}
// emulated overhangs if the stride of the target buffer can't hold. This // makes it possible to support emu-edge and so on even if we have large block // overhangs
emu[0] = (col + w4) * 8 * bytesperpixel > f->linesize[0] ||
(row + h4) > s->rows;
emu[1] = ((col + w4) * 8 >> s->ss_h) * bytesperpixel > f->linesize[1] ||
(row + h4) > s->rows; if (emu[0]) {
td->dst[0] = td->tmp_y;
td->y_stride = 128;
} else {
td->dst[0] = f->data[0] + yoff;
td->y_stride = f->linesize[0];
} if (emu[1]) {
td->dst[1] = td->tmp_uv[0];
td->dst[2] = td->tmp_uv[1];
td->uv_stride = 128;
} else {
td->dst[1] = f->data[1] + uvoff;
td->dst[2] = f->data[2] + uvoff;
td->uv_stride = f->linesize[1];
} if (b->intra) { if (s->s.h.bpp > 8) {
ff_vp9_intra_recon_16bpp(td, yoff, uvoff);
} else {
ff_vp9_intra_recon_8bpp(td, yoff, uvoff);
}
} else { if (s->s.h.bpp > 8) {
ff_vp9_inter_recon_16bpp(td);
} else {
ff_vp9_inter_recon_8bpp(td);
}
} if (emu[0]) { int w = FFMIN(s->cols - col, w4) * 8, h = FFMIN(s->rows - row, h4) * 8, n, o = 0;
for (n = 0; o < w; n++) { int bw = 64 >> n;
av_assert2(n <= 4); if (w & bw) {
s->dsp.mc[n][0][0][0][0](f->data[0] + yoff + o * bytesperpixel, f->linesize[0],
td->tmp_y + o * bytesperpixel, 128, h, 0, 0);
o += bw;
}
}
} if (emu[1]) { int w = FFMIN(s->cols - col, w4) * 8 >> s->ss_h; int h = FFMIN(s->rows - row, h4) * 8 >> s->ss_v, n, o = 0;
for (n = s->ss_h; o < w; n++) { int bw = 64 >> n;
av_assert2(n <= 4); if (w & bw) {
s->dsp.mc[n][0][0][0][0](f->data[1] + uvoff + o * bytesperpixel, f->linesize[1],
td->tmp_uv[0] + o * bytesperpixel, 128, h, 0, 0);
s->dsp.mc[n][0][0][0][0](f->data[2] + uvoff + o * bytesperpixel, f->linesize[2],
td->tmp_uv[1] + o * bytesperpixel, 128, h, 0, 0);
o += bw;
}
}
}
// pick filter level and find edges to apply filter to if (s->s.h.filter.level &&
(lvl = s->s.h.segmentation.feat[b->seg_id].lflvl[b->intra ? 0 : b->ref[0] + 1]
[b->mode[3] != ZEROMV]) > 0) { int x_end = FFMIN(s->cols - col, w4), y_end = FFMIN(s->rows - row, h4); int skip_inter = !b->intra && b->skip, col7 = td->col7, row7 = td->row7;
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.