/* * VP7/VP8 compatible video decoder * * Copyright (C) 2010 David Conrad * Copyright (C) 2010 Ronald S. Bultje * Copyright (C) 2010 Fiona Glaser * Copyright (C) 2012 Daniel Kang * Copyright (C) 2014 Peter Ross * * 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
*/
static av_always_inline int update_dimensions(VP8Context *s, int width, int height, int is_vp7)
{
AVCodecContext *avctx = s->avctx; int i, ret, dim_reset = 0;
if (s->segmentation.update_feature_data) {
s->segmentation.absolute_vals = vp89_rac_get(c);
for (i = 0; i < 4; i++)
s->segmentation.base_quant[i] = vp8_rac_get_sint(c, 7);
for (i = 0; i < 4; i++)
s->segmentation.filter_level[i] = vp8_rac_get_sint(c, 6);
} if (s->segmentation.update_map) for (i = 0; i < 3; i++)
s->prob->segmentid[i] = vp89_rac_get(c) ? vp89_rac_get_uint(c, 8) : 255;
}
staticvoid update_lf_deltas(VP8Context *s)
{
VPXRangeCoder *c = &s->c; int i;
for (i = 0; i < 4; i++) { if (vp89_rac_get(c)) {
s->lf_delta.ref[i] = vp89_rac_get_uint(c, 6);
if (vp89_rac_get(c))
s->lf_delta.ref[i] = -s->lf_delta.ref[i];
}
}
for (i = MODE_I4x4; i <= VP8_MVMODE_SPLIT; i++) { if (vp89_rac_get(c)) {
s->lf_delta.mode[i] = vp89_rac_get_uint(c, 6);
if (vp89_rac_get(c))
s->lf_delta.mode[i] = -s->lf_delta.mode[i];
}
}
}
staticint setup_partitions(VP8Context *s, const uint8_t *buf, int buf_size)
{ const uint8_t *sizes = buf; int i; int ret;
for (i = 0; i < 4; i++) { if (s->segmentation.enabled) {
base_qi = s->segmentation.base_quant[i]; if (!s->segmentation.absolute_vals)
base_qi += s->quant.yac_qi;
} else
base_qi = s->quant.yac_qi;
/** * Determine which buffers golden and altref should be updated with after this frame. * The spec isn't clear here, so I'm going by my understanding of what libvpx does * * Intra frames update all 3 references * Inter frames update VP8_FRAME_PREVIOUS if the update_last flag is set * If the update (golden|altref) flag is set, it's updated with the current frame * if update_last is set, and VP8_FRAME_PREVIOUS otherwise. * If the flag is not set, the number read means: * 0: no update * 1: VP8_FRAME_PREVIOUS * 2: update golden with altref, or update altref with golden
*/ static VP8FrameType ref_to_update(VP8Context *s, int update, VP8FrameType ref)
{
VPXRangeCoder *c = &s->c;
staticvoid vp78_reset_probability_tables(VP8Context *s)
{ int i, j; for (i = 0; i < 4; i++) for (j = 0; j < 16; j++)
memcpy(s->prob->token[i][j], vp8_token_default_probs[i][vp8_coeff_band[j]], sizeof(s->prob->token[i][j]));
}
staticvoid vp78_update_probability_tables(VP8Context *s)
{
VPXRangeCoder *c = &s->c; int i, j, k, l, m;
for (i = 0; i < 4; i++) for (j = 0; j < 8; j++) for (k = 0; k < 3; k++) for (l = 0; l < NUM_DCT_TOKENS-1; l++) if (vpx_rac_get_prob_branchy(c, ff_vp8_token_update_probs[i][j][k][l])) { int prob = vp89_rac_get_uint(c, 8); for (m = 0; vp8_coeff_band_indexes[j][m] >= 0; m++)
s->prob->token[i][vp8_coeff_band_indexes[j][m]][k][l] = prob;
}
}
#define VP7_MVC_SIZE 17 #define VP8_MVC_SIZE 19
staticvoid vp78_update_pred16x16_pred8x8_mvc_probabilities(VP8Context *s, int mvc_size)
{
VPXRangeCoder *c = &s->c; int i, j;
if (vp89_rac_get(c)) for (i = 0; i < 4; i++)
s->prob->pred16x16[i] = vp89_rac_get_uint(c, 8); if (vp89_rac_get(c)) for (i = 0; i < 3; i++)
s->prob->pred8x8c[i] = vp89_rac_get_uint(c, 8);
// 17.2 MV probability update for (i = 0; i < 2; i++) for (j = 0; j < mvc_size; j++) if (vpx_rac_get_prob_branchy(c, vp8_mv_update_prob[i][j]))
s->prob->mvc[i][j] = vp8_rac_get_nn(c);
}
staticvoid copy_chroma(AVFrame *dst, const AVFrame *src, int width, int height)
{ int i, j;
for (j = 1; j < 3; j++) { for (i = 0; i < height / 2; i++)
memcpy(dst->data[j] + i * dst->linesize[j],
src->data[j] + i * src->linesize[j], width / 2);
}
}
staticvoid fade(uint8_t *dst, ptrdiff_t dst_linesize, const uint8_t *src, ptrdiff_t src_linesize, int width, int height, int alpha, int beta)
{ int i, j; for (j = 0; j < height; j++) { const uint8_t *src2 = src + j * src_linesize;
uint8_t *dst2 = dst + j * dst_linesize; for (i = 0; i < width; i++) {
uint8_t y = src2[i];
dst2[i] = av_clip_uint8(y + ((y * beta) >> 8) + alpha);
}
}
}
staticint vp7_fade_frame(VP8Context *s, int alpha, int beta)
{ int ret;
if (!s->keyframe && (alpha || beta)) { int width = s->mb_width * 16; int height = s->mb_height * 16; const AVFrame *src;
AVFrame *dst;
if (!s->framep[VP8_FRAME_PREVIOUS] ||
!s->framep[VP8_FRAME_GOLDEN]) {
av_log(s->avctx, AV_LOG_WARNING, "Discarding interframe without a prior keyframe!\n"); return AVERROR_INVALIDDATA;
}
src =
dst = s->framep[VP8_FRAME_PREVIOUS]->tf.f;
/* preserve the golden frame, write a new previous frame */ if (s->framep[VP8_FRAME_GOLDEN] == s->framep[VP8_FRAME_PREVIOUS]) {
s->framep[VP8_FRAME_PREVIOUS] = vp8_find_free_buffer(s); if ((ret = vp8_alloc_frame(s, s->framep[VP8_FRAME_PREVIOUS], 1)) < 0) return ret;
staticint vp7_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_size)
{
VPXRangeCoder *c = &s->c; int part1_size, hscale, vscale, i, j, ret; int width = s->avctx->width; int height = s->avctx->height; int alpha = 0; int beta = 0; int fade_present = 1;
if (s->keyframe || s->profile > 0)
memset(s->inter_dc_pred, 0 , sizeof(s->inter_dc_pred));
/* B. Decoding information for all four macroblock-level features */ for (i = 0; i < 4; i++) {
s->feature_enabled[i] = vp89_rac_get(c); if (s->feature_enabled[i]) {
s->feature_present_prob[i] = vp89_rac_get_uint(c, 8);
/* C. Dequantization indices */
vp7_get_quants(s);
/* D. Golden frame update flag (a Flag) for interframes only */ if (!s->keyframe) {
s->update_golden = vp89_rac_get(c) ? VP8_FRAME_CURRENT : VP8_FRAME_NONE;
s->sign_bias[VP8_FRAME_GOLDEN] = 0;
}
s->update_last = 1;
s->update_probabilities = 1;
if (s->profile > 0) {
s->update_probabilities = vp89_rac_get(c); if (!s->update_probabilities)
s->prob[1] = s->prob[0];
if (!s->keyframe)
fade_present = vp89_rac_get(c);
}
if (vpx_rac_is_end(c)) return AVERROR_INVALIDDATA; /* E. Fading information for previous frame */ if (fade_present && vp89_rac_get(c)) {
alpha = (int8_t) vp89_rac_get_uint(c, 8);
beta = (int8_t) vp89_rac_get_uint(c, 8);
}
/* F. Loop filter type */ if (!s->profile)
s->filter.simple = vp89_rac_get(c);
/* G. DCT coefficient ordering specification */ if (vp89_rac_get(c)) for (i = 1; i < 16; i++)
s->prob[0].scan[i] = ff_zigzag_scan[vp89_rac_get_uint(c, 4)];
/* I. DCT coefficient probability update; 13.3 Token Probability Updates */
vp78_update_probability_tables(s);
s->mbskip_enabled = 0;
/* J. The remaining frame header data occurs ONLY FOR INTERFRAMES */ if (!s->keyframe) {
s->prob->intra = vp89_rac_get_uint(c, 8);
s->prob->last = vp89_rac_get_uint(c, 8);
vp78_update_pred16x16_pred8x8_mvc_probabilities(s, VP7_MVC_SIZE);
}
if (vpx_rac_is_end(c)) return AVERROR_INVALIDDATA;
if ((ret = vp7_fade_frame(s, alpha, beta)) < 0) return ret;
return 0;
}
staticint vp8_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_size)
{
VPXRangeCoder *c = &s->c; int header_size, hscale, vscale, ret; int width = s->avctx->width; int height = s->avctx->height;
if (buf_size < 3) {
av_log(s->avctx, AV_LOG_ERROR, "Insufficent data (%d) for header\n", buf_size); return AVERROR_INVALIDDATA;
}
if (s->profile > 3)
av_log(s->avctx, AV_LOG_WARNING, "Unknown profile %d\n", s->profile);
if (!s->profile)
memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_epel_pixels_tab, sizeof(s->put_pixels_tab)); else// profile 1-3 use bilinear, 4+ aren't defined so whatever
memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_bilinear_pixels_tab, sizeof(s->put_pixels_tab));
if (header_size > buf_size - 7 * s->keyframe) {
av_log(s->avctx, AV_LOG_ERROR, "Header size larger than data provided\n"); return AVERROR_INVALIDDATA;
}
ret = ff_vpx_init_range_decoder(c, buf, header_size); if (ret < 0) return ret;
buf += header_size;
buf_size -= header_size;
if (s->keyframe) {
s->colorspace = vp89_rac_get(c); if (s->colorspace)
av_log(s->avctx, AV_LOG_WARNING, "Unspecified colorspace\n");
s->fullrange = vp89_rac_get(c);
}
if ((s->segmentation.enabled = vp89_rac_get(c)))
parse_segment_info(s); else
s->segmentation.update_map = 0; // FIXME: move this to some init function?
// if we aren't saving this frame's probabilities for future frames, // make a copy of the current probabilities if (!(s->update_probabilities = vp89_rac_get(c)))
s->prob[1] = s->prob[0];
s->update_last = s->keyframe || vp89_rac_get(c);
vp78_update_probability_tables(s);
if ((s->mbskip_enabled = vp89_rac_get(c)))
s->prob->mbskip = vp89_rac_get_uint(c, 8);
// Record the entropy coder state here so that hwaccels can use it.
s->c.code_word = vpx_rac_renorm(&s->c);
s->coder_state_at_header_end.input = s->c.buffer - (-s->c.bits / 8);
s->coder_state_at_header_end.range = s->c.high;
s->coder_state_at_header_end.value = s->c.code_word >> 16;
s->coder_state_at_header_end.bit_count = -s->c.bits % 8;
/** * The vp7 reference decoder uses a padding macroblock column (added to right * edge of the frame) to guard against illegal macroblock offsets. The * algorithm has bugs that permit offsets to straddle the padding column. * This function replicates those bugs. * * @param[out] edge_x macroblock x address * @param[out] edge_y macroblock y address * * @return macroblock offset legal (boolean)
*/ staticint vp7_calculate_mb_offset(int mb_x, int mb_y, int mb_width, int xoffset, int yoffset, int boundary, int *edge_x, int *edge_y)
{ int vwidth = mb_width + 1; intnew = (mb_y + yoffset) * vwidth + mb_x + xoffset; if (new < boundary || new % vwidth == vwidth - 1) return 0;
*edge_y = new / vwidth;
*edge_x = new % vwidth; return 1;
}
mb->partitioning = VP8_SPLITMVMODE_NONE; if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_ZERO]][0])) {
mb->mode = VP8_MVMODE_MV;
/* If we have three distinct MVs, merge first and last if they're the same */ if (cnt[CNT_SPLITMV] &&
AV_RN32A(&near_mv[1 + VP8_EDGE_TOP]) == AV_RN32A(&near_mv[1 + VP8_EDGE_TOPLEFT]))
cnt[CNT_NEAREST] += 1;
/* Swap near and nearest if necessary */ if (cnt[CNT_NEAR] > cnt[CNT_NEAREST]) {
FFSWAP(uint8_t, cnt[CNT_NEAREST], cnt[CNT_NEAR]);
FFSWAP(VP8mv, near_mv[CNT_NEAREST], near_mv[CNT_NEAR]);
}
if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_NEAREST]][1])) { if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_NEAR]][2])) { /* Choose the best mv out of 0,0 and the nearest mv */
clamp_mv(mv_bounds, &mb->mv, &near_mv[CNT_ZERO + (cnt[CNT_NEAREST] >= cnt[CNT_ZERO])]);
cnt[CNT_SPLITMV] = ((mb_edge[VP8_EDGE_LEFT]->mode == VP8_MVMODE_SPLIT) +
(mb_edge[VP8_EDGE_TOP]->mode == VP8_MVMODE_SPLIT)) * 2 +
(mb_edge[VP8_EDGE_TOPLEFT]->mode == VP8_MVMODE_SPLIT);
/** * @param r arithmetic bitstream reader context * @param block destination for block coefficients * @param probs probabilities to use when reading trees from the bitstream * @param i initial coeff index, 0 unless a separate DC block is coded * @param qmul array holding the dc/ac dequant factor at position 0/1 * * @return 0 if no coeffs were decoded * otherwise, the index of the last coeff decoded plus one
*/ static av_always_inline int decode_block_coeffs_internal(VPXRangeCoder *r, int16_t block[16],
uint8_t probs[16][3][NUM_DCT_TOKENS - 1], int i, const uint8_t *token_prob, const int16_t qmul[2], const uint8_t scan[16], int vp7)
{
VPXRangeCoder c = *r; goto skip_eob; do { int coeff;
restart: if (!vpx_rac_get_prob_branchy(&c, token_prob[0])) // DCT_EOB break;
skip_eob: if (!vpx_rac_get_prob_branchy(&c, token_prob[1])) { // DCT_0 if (++i == 16) break; // invalid input; blocks should end with EOB
token_prob = probs[i][0]; if (vp7) goto restart; goto skip_eob;
}
if (!vpx_rac_get_prob_branchy(&c, token_prob[2])) { // DCT_1
coeff = 1;
token_prob = probs[i + 1][1];
} else { if (!vpx_rac_get_prob_branchy(&c, token_prob[3])) { // DCT 2,3,4
coeff = vpx_rac_get_prob_branchy(&c, token_prob[4]); if (coeff)
coeff += vpx_rac_get_prob(&c, token_prob[5]);
coeff += 2;
} else { // DCT_CAT* if (!vpx_rac_get_prob_branchy(&c, token_prob[6])) { if (!vpx_rac_get_prob_branchy(&c, token_prob[7])) { // DCT_CAT1
coeff = 5 + vpx_rac_get_prob(&c, vp8_dct_cat1_prob[0]);
} else { // DCT_CAT2
coeff = 7;
coeff += vpx_rac_get_prob(&c, vp8_dct_cat2_prob[0]) << 1;
coeff += vpx_rac_get_prob(&c, vp8_dct_cat2_prob[1]);
}
} else { // DCT_CAT3 and up int a = vpx_rac_get_prob(&c, token_prob[8]); int b = vpx_rac_get_prob(&c, token_prob[9 + a]); int cat = (a << 1) + b;
coeff = 3 + (8 << cat);
coeff += vp8_rac_get_coeff(&c, ff_vp8_dct_cat_prob[cat]);
}
}
token_prob = probs[i + 1][2];
}
block[scan[i]] = (vp89_rac_get(&c) ? -coeff : coeff) * qmul[!!i];
} while (++i < 16);
*r = c; return i;
}
static av_always_inline int inter_predict_dc(int16_t block[16], int16_t pred[2])
{
int16_t dc = block[0]; int ret = 0;
/** * @param c arithmetic bitstream reader context * @param block destination for block coefficients * @param probs probabilities to use when reading trees from the bitstream * @param i initial coeff index, 0 unless a separate DC block is coded * @param zero_nhood the initial prediction context for number of surrounding * all-zero blocks (only left/top, so 0-2) * @param qmul array holding the dc/ac dequant factor at position 0/1 * @param scan scan pattern (VP7 only) * * @return 0 if no coeffs were decoded * otherwise, the index of the last coeff decoded plus one
*/ static av_always_inline int decode_block_coeffs(VPXRangeCoder *c, int16_t block[16],
uint8_t probs[16][3][NUM_DCT_TOKENS - 1], int i, int zero_nhood, const int16_t qmul[2], const uint8_t scan[16], int vp7)
{ const uint8_t *token_prob = probs[i][zero_nhood]; if (!vpx_rac_get_prob_branchy(c, token_prob[0])) // DCT_EOB return 0; return vp7 ? vp7_decode_block_coeffs_internal(c, block, probs, i,
token_prob, qmul, scan)
: vp8_decode_block_coeffs_internal(c, block, probs, i,
token_prob, qmul);
}
static av_always_inline void decode_mb_coeffs(VP8Context *s, VP8ThreadData *td, VPXRangeCoder *c,
VP8Macroblock *mb, uint8_t t_nnz[9], uint8_t l_nnz[9], int is_vp7)
{ int i, x, y, luma_start = 0, luma_ctx = 3; int nnz_pred, nnz, nnz_total = 0; int segment = mb->segment; int block_dc = 0;
// luma blocks for (y = 0; y < 4; y++) for (x = 0; x < 4; x++) {
nnz_pred = l_nnz[y] + t_nnz[x];
nnz = decode_block_coeffs(c, td->block[y][x],
s->prob->token[luma_ctx],
luma_start, nnz_pred,
s->qmat[segment].luma_qmul,
s->prob[0].scan, is_vp7); /* nnz+block_dc may be one more than the actual last index,
* but we don't care */
td->non_zero_count_cache[y][x] = nnz + block_dc;
t_nnz[x] = l_nnz[y] = !!nnz;
nnz_total += nnz;
}
// chroma blocks // TODO: what to do about dimensions? 2nd dim for luma is x, // but for chroma it's (y<<1)|x for (i = 4; i < 6; i++) for (y = 0; y < 2; y++) for (x = 0; x < 2; x++) {
nnz_pred = l_nnz[i + 2 * y] + t_nnz[i + 2 * x];
nnz = decode_block_coeffs(c, td->block[i][(y << 1) + x],
s->prob->token[2], 0, nnz_pred,
s->qmat[segment].chroma_qmul,
s->prob[0].scan, is_vp7);
td->non_zero_count_cache[i][(y << 1) + x] = nnz;
t_nnz[i + 2 * x] = l_nnz[i + 2 * y] = !!nnz;
nnz_total += nnz;
}
// if there were no coded coeffs despite the macroblock not being marked skip, // we MUST not do the inner loop filter and should not do IDCT // Since skip isn't used for bitstream prediction, just manually set it. if (!nnz_total)
mb->skip = 1;
}
// only copy chroma for normal loop filter // or to initialize the top row to 127 if (!simple || !mb_y) {
XCHG(top_border_m1 + 16, src_cb - 8, xchg);
XCHG(top_border_m1 + 24, src_cr - 8, xchg);
XCHG(top_border + 16, src_cb, 1);
XCHG(top_border + 24, src_cr, 1);
}
}
static av_always_inline int check_dc_pred8x8_mode(int mode, int mb_x, int mb_y)
{ if (!mb_x) return mb_y ? TOP_DC_PRED8x8 : DC_128_PRED8x8; else return mb_y ? mode : LEFT_DC_PRED8x8;
}
static av_always_inline int check_tm_pred8x8_mode(int mode, int mb_x, int mb_y, int vp7)
{ if (!mb_x) return mb_y ? VERT_PRED8x8 : (vp7 ? DC_128_PRED8x8 : DC_129_PRED8x8); else return mb_y ? mode : HOR_PRED8x8;
}
static av_always_inline int check_intra_pred8x8_mode_emuedge(int mode, int mb_x, int mb_y, int vp7)
{ switch (mode) { case DC_PRED8x8: return check_dc_pred8x8_mode(mode, mb_x, mb_y); case VERT_PRED8x8: return !mb_y ? (vp7 ? DC_128_PRED8x8 : DC_127_PRED8x8) : mode; case HOR_PRED8x8: return !mb_x ? (vp7 ? DC_128_PRED8x8 : DC_129_PRED8x8) : mode; case PLANE_PRED8x8: /* TM */ return check_tm_pred8x8_mode(mode, mb_x, mb_y, vp7);
} return mode;
}
static av_always_inline int check_tm_pred4x4_mode(int mode, int mb_x, int mb_y, int vp7)
{ if (!mb_x) { return mb_y ? VERT_VP8_PRED : (vp7 ? DC_128_PRED : DC_129_PRED);
} else { return mb_y ? mode : HOR_VP8_PRED;
}
}
static av_always_inline int check_intra_pred4x4_mode_emuedge(int mode, int mb_x, int mb_y, int *copy_buf, int vp7)
{ switch (mode) { case VERT_PRED: if (!mb_x && mb_y) {
*copy_buf = 1; return mode;
} /* fall-through */ case DIAG_DOWN_LEFT_PRED: case VERT_LEFT_PRED: return !mb_y ? (vp7 ? DC_128_PRED : DC_127_PRED) : mode; case HOR_PRED: if (!mb_y) {
*copy_buf = 1; return mode;
} /* fall-through */ case HOR_UP_PRED: return !mb_x ? (vp7 ? DC_128_PRED : DC_129_PRED) : mode; case TM_VP8_PRED: return check_tm_pred4x4_mode(mode, mb_x, mb_y, vp7); case DC_PRED: /* 4x4 DC doesn't use the same "H.264-style" exceptions
* as 16x16/8x8 DC */ case DIAG_DOWN_RIGHT_PRED: case VERT_RIGHT_PRED: case HOR_DOWN_PRED: if (!mb_y || !mb_x)
*copy_buf = 1; return mode;
} return mode;
}
static av_always_inline void intra_predict(VP8Context *s, VP8ThreadData *td, uint8_t *const dst[3],
VP8Macroblock *mb, int mb_x, int mb_y, int is_vp7)
{ int x, y, mode, nnz;
uint32_t tr;
/* for the first row, we need to run xchg_mb_border to init the top edge
* to 127 otherwise, skip it if we aren't going to deblock */ if (mb_y && (s->deblock_filter || !mb_y) && td->thread_nr == 0)
xchg_mb_border(s->top_border[mb_x + 1], dst[0], dst[1], dst[2],
s->linesize, s->uvlinesize, mb_x, mb_y, s->mb_width,
s->filter.simple, 1);
// all blocks on the right edge of the macroblock use bottom edge // the top macroblock for their topright edge const uint8_t *tr_right = ptr - s->linesize + 16;
// if we're on the right edge of the frame, said edge is extended // from the top macroblock if (mb_y && mb_x == s->mb_width - 1) {
tr = tr_right[-1] * 0x01010101u;
tr_right = (uint8_t *) &tr;
}
if (mb->skip)
AV_ZERO128(td->non_zero_count_cache);
for (y = 0; y < 4; y++) { const uint8_t *topright = ptr + 4 - s->linesize; for (x = 0; x < 4; x++) { int copy = 0;
ptrdiff_t linesize = s->linesize;
uint8_t *dst = ptr + 4 * x;
LOCAL_ALIGNED(4, uint8_t, copy_dst, [5 * 8]);
staticconst uint8_t subpel_idx[3][8] = {
{ 0, 1, 2, 1, 2, 1, 2, 1 }, // nr. of left extra pixels, // also function pointer index
{ 0, 3, 5, 3, 5, 3, 5, 3 }, // nr. of extra pixels required
{ 0, 2, 3, 2, 3, 2, 3, 2 }, // nr. of right extra pixels
};
/** * luma MC function * * @param s VP8 decoding context * @param dst target buffer for block data at block position * @param ref reference picture buffer at origin (0, 0) * @param mv motion vector (relative to block position) to get pixel data from * @param x_off horizontal position of block from origin (0, 0) * @param y_off vertical position of block from origin (0, 0) * @param block_w width of block (16, 8 or 4) * @param block_h height of block (always same as block_w) * @param width width of src/dst plane data * @param height height of src/dst plane data * @param linesize size of a single line of plane data, including padding * @param mc_func motion compensation function pointers (bilinear or sixtap MC)
*/ static av_always_inline void vp8_mc_luma(VP8Context *s, VP8ThreadData *td, uint8_t *dst, const ProgressFrame *ref, const VP8mv *mv, int x_off, int y_off, int block_w, int block_h, int width, int height, ptrdiff_t linesize,
vp8_mc_func mc_func[3][3])
{ const uint8_t *src = ref->f->data[0];
if (AV_RN32A(mv)) {
ptrdiff_t src_linesize = linesize;
int mx = (mv->x * 2) & 7, mx_idx = subpel_idx[0][mx]; int my = (mv->y * 2) & 7, my_idx = subpel_idx[0][my];
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.