1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-01-03 05:10:03 +02:00
FFmpeg/libavcodec/vc1_pred.c
Andreas Rheinhardt fe6037fd04 avcodec/mpegpicture: Split MPVPicture into WorkPicture and ordinary Pic
There are two types of MPVPictures: Three (cur_pic, last_pic, next_pic)
that are directly part of MpegEncContext and an array of MPVPictures
that are separately allocated and are mostly accessed via pointers
(cur|last|next)_pic_ptr; they are also used to store AVFrames in the
encoder (necessary due to B-frames). As the name implies, each of the
former is directly associated with one of the _ptr pointers:
They actually share the same underlying buffers, but the ones
that are part of the context can have their data pointers offset
and their linesize doubled for field pictures.

Up until now, each of these had their own references; in particular,
there was an underlying av_frame_ref() to sync cur_pic and cur_pic_ptr
etc. This is wasteful.

This commit changes this relationship: cur_pic, last_pic and next_pic
now become MPVWorkPictures; this structure does not have an AVFrame
at all any more, but only the cached values of data and linesize.
It also contains a pointer to the corresponding MPVPicture, establishing
a more natural relationsship between the two.
This already means that creating the context-pictures from the pointers
can no longer fail.

What has not been changed is the fact that the MPVPicture* pointers
are not ownership pointers and that the MPVPictures are part of an
array of MPVPictures that is owned by a single AVCodecContext.
Doing so will be done in a latter commit.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2024-06-12 11:38:13 +02:00

946 lines
37 KiB
C

/*
* VC-1 and WMV3 decoder
* Copyright (c) 2011 Mashiat Sarker Shakkhar
* Copyright (c) 2006-2007 Konstantin Shishkov
* Partly based on vc9.c (c) 2005 Anonymous, Alex Beregszaszi, Michael Niedermayer
*
* 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
*/
/**
* @file
* VC-1 and WMV3 block decoding routines
*/
#include "mathops.h"
#include "mpegutils.h"
#include "mpegvideo.h"
#include "vc1.h"
#include "vc1_pred.h"
#include "vc1data.h"
static av_always_inline int scaleforsame_x(const VC1Context *v, int n /* MV */, int dir)
{
int scaledvalue, refdist;
int scalesame1, scalesame2;
int scalezone1_x, zone1offset_x;
int table_index = dir ^ v->second_field;
if (v->s.pict_type != AV_PICTURE_TYPE_B)
refdist = v->refdist;
else
refdist = dir ? v->brfd : v->frfd;
if (refdist > 3)
refdist = 3;
scalesame1 = ff_vc1_field_mvpred_scales[table_index][1][refdist];
scalesame2 = ff_vc1_field_mvpred_scales[table_index][2][refdist];
scalezone1_x = ff_vc1_field_mvpred_scales[table_index][3][refdist];
zone1offset_x = ff_vc1_field_mvpred_scales[table_index][5][refdist];
if (FFABS(n) > 255)
scaledvalue = n;
else {
if (FFABS(n) < scalezone1_x)
scaledvalue = (n * scalesame1) >> 8;
else {
if (n < 0)
scaledvalue = ((n * scalesame2) >> 8) - zone1offset_x;
else
scaledvalue = ((n * scalesame2) >> 8) + zone1offset_x;
}
}
return av_clip(scaledvalue, -v->range_x, v->range_x - 1);
}
static av_always_inline int scaleforsame_y(const VC1Context *v, int n /* MV */, int dir)
{
int scaledvalue, refdist;
int scalesame1, scalesame2;
int scalezone1_y, zone1offset_y;
int table_index = dir ^ v->second_field;
if (v->s.pict_type != AV_PICTURE_TYPE_B)
refdist = v->refdist;
else
refdist = dir ? v->brfd : v->frfd;
if (refdist > 3)
refdist = 3;
scalesame1 = ff_vc1_field_mvpred_scales[table_index][1][refdist];
scalesame2 = ff_vc1_field_mvpred_scales[table_index][2][refdist];
scalezone1_y = ff_vc1_field_mvpred_scales[table_index][4][refdist];
zone1offset_y = ff_vc1_field_mvpred_scales[table_index][6][refdist];
if (FFABS(n) > 63)
scaledvalue = n;
else {
if (FFABS(n) < scalezone1_y)
scaledvalue = (n * scalesame1) >> 8;
else {
if (n < 0)
scaledvalue = ((n * scalesame2) >> 8) - zone1offset_y;
else
scaledvalue = ((n * scalesame2) >> 8) + zone1offset_y;
}
}
if (v->cur_field_type && !v->ref_field_type[dir])
return av_clip(scaledvalue, -v->range_y / 2 + 1, v->range_y / 2);
else
return av_clip(scaledvalue, -v->range_y / 2, v->range_y / 2 - 1);
}
static av_always_inline int scaleforopp_x(const VC1Context *v, int n /* MV */)
{
int scalezone1_x, zone1offset_x;
int scaleopp1, scaleopp2, brfd;
int scaledvalue;
brfd = FFMIN(v->brfd, 3);
scalezone1_x = ff_vc1_b_field_mvpred_scales[3][brfd];
zone1offset_x = ff_vc1_b_field_mvpred_scales[5][brfd];
scaleopp1 = ff_vc1_b_field_mvpred_scales[1][brfd];
scaleopp2 = ff_vc1_b_field_mvpred_scales[2][brfd];
if (FFABS(n) > 255)
scaledvalue = n;
else {
if (FFABS(n) < scalezone1_x)
scaledvalue = (n * scaleopp1) >> 8;
else {
if (n < 0)
scaledvalue = ((n * scaleopp2) >> 8) - zone1offset_x;
else
scaledvalue = ((n * scaleopp2) >> 8) + zone1offset_x;
}
}
return av_clip(scaledvalue, -v->range_x, v->range_x - 1);
}
static av_always_inline int scaleforopp_y(const VC1Context *v, int n /* MV */, int dir)
{
int scalezone1_y, zone1offset_y;
int scaleopp1, scaleopp2, brfd;
int scaledvalue;
brfd = FFMIN(v->brfd, 3);
scalezone1_y = ff_vc1_b_field_mvpred_scales[4][brfd];
zone1offset_y = ff_vc1_b_field_mvpred_scales[6][brfd];
scaleopp1 = ff_vc1_b_field_mvpred_scales[1][brfd];
scaleopp2 = ff_vc1_b_field_mvpred_scales[2][brfd];
if (FFABS(n) > 63)
scaledvalue = n;
else {
if (FFABS(n) < scalezone1_y)
scaledvalue = (n * scaleopp1) >> 8;
else {
if (n < 0)
scaledvalue = ((n * scaleopp2) >> 8) - zone1offset_y;
else
scaledvalue = ((n * scaleopp2) >> 8) + zone1offset_y;
}
}
if (v->cur_field_type && !v->ref_field_type[dir]) {
return av_clip(scaledvalue, -v->range_y / 2 + 1, v->range_y / 2);
} else {
return av_clip(scaledvalue, -v->range_y / 2, v->range_y / 2 - 1);
}
}
static av_always_inline int scaleforsame(const VC1Context *v, int n /* MV */,
int dim, int dir)
{
int brfd, scalesame;
int hpel = 1 - v->s.quarter_sample;
n >>= hpel;
if (v->s.pict_type != AV_PICTURE_TYPE_B || v->second_field || !dir) {
if (dim)
n = scaleforsame_y(v, n, dir) * (1 << hpel);
else
n = scaleforsame_x(v, n, dir) * (1 << hpel);
return n;
}
brfd = FFMIN(v->brfd, 3);
scalesame = ff_vc1_b_field_mvpred_scales[0][brfd];
n = (n * scalesame >> 8) * (1 << hpel);
return n;
}
static av_always_inline int scaleforopp(const VC1Context *v, int n /* MV */,
int dim, int dir)
{
int refdist, scaleopp;
int hpel = 1 - v->s.quarter_sample;
n >>= hpel;
if (v->s.pict_type == AV_PICTURE_TYPE_B && !v->second_field && dir == 1) {
if (dim)
n = scaleforopp_y(v, n, dir) * (1 << hpel);
else
n = scaleforopp_x(v, n) * (1 << hpel);
return n;
}
if (v->s.pict_type != AV_PICTURE_TYPE_B)
refdist = v->refdist;
else
refdist = dir ? v->brfd : v->frfd;
refdist = FFMIN(refdist, 3);
scaleopp = ff_vc1_field_mvpred_scales[dir ^ v->second_field][0][refdist];
n = (n * scaleopp >> 8) * (1 << hpel);
return n;
}
/** Predict and set motion vector
*/
void ff_vc1_pred_mv(VC1Context *v, int n, int dmv_x, int dmv_y,
int mv1, int r_x, int r_y, uint8_t* is_intra,
int pred_flag, int dir)
{
MpegEncContext *s = &v->s;
int xy, wrap, off = 0;
int px, py;
int sum;
int mixedmv_pic, num_samefield = 0, num_oppfield = 0;
int opposite, a_f, b_f, c_f;
int16_t field_predA[2];
int16_t field_predB[2];
int16_t field_predC[2];
int a_valid, b_valid, c_valid;
int hybridmv_thresh, y_bias = 0;
if (v->mv_mode == MV_PMODE_MIXED_MV ||
((v->mv_mode == MV_PMODE_INTENSITY_COMP) && (v->mv_mode2 == MV_PMODE_MIXED_MV)))
mixedmv_pic = 1;
else
mixedmv_pic = 0;
/* scale MV difference to be quad-pel */
if (!s->quarter_sample) {
dmv_x *= 2;
dmv_y *= 2;
}
wrap = s->b8_stride;
xy = s->block_index[n];
if (s->mb_intra) {
s->mv[0][n][0] = s->cur_pic.motion_val[0][xy + v->blocks_off][0] = 0;
s->mv[0][n][1] = s->cur_pic.motion_val[0][xy + v->blocks_off][1] = 0;
s->cur_pic.motion_val[1][xy + v->blocks_off][0] = 0;
s->cur_pic.motion_val[1][xy + v->blocks_off][1] = 0;
if (mv1) { /* duplicate motion data for 1-MV block */
s->cur_pic.motion_val[0][xy + 1 + v->blocks_off][0] = 0;
s->cur_pic.motion_val[0][xy + 1 + v->blocks_off][1] = 0;
s->cur_pic.motion_val[0][xy + wrap + v->blocks_off][0] = 0;
s->cur_pic.motion_val[0][xy + wrap + v->blocks_off][1] = 0;
s->cur_pic.motion_val[0][xy + wrap + 1 + v->blocks_off][0] = 0;
s->cur_pic.motion_val[0][xy + wrap + 1 + v->blocks_off][1] = 0;
v->luma_mv[s->mb_x][0] = v->luma_mv[s->mb_x][1] = 0;
s->cur_pic.motion_val[1][xy + 1 + v->blocks_off][0] = 0;
s->cur_pic.motion_val[1][xy + 1 + v->blocks_off][1] = 0;
s->cur_pic.motion_val[1][xy + wrap + v->blocks_off][0] = 0;
s->cur_pic.motion_val[1][xy + wrap + v->blocks_off][1] = 0;
s->cur_pic.motion_val[1][xy + wrap + 1 + v->blocks_off][0] = 0;
s->cur_pic.motion_val[1][xy + wrap + 1 + v->blocks_off][1] = 0;
}
return;
}
a_valid = !s->first_slice_line || (n == 2 || n == 3);
b_valid = a_valid;
c_valid = s->mb_x || (n == 1 || n == 3);
if (mv1) {
if (v->field_mode && mixedmv_pic)
off = (s->mb_x == (s->mb_width - 1)) ? -2 : 2;
else
off = (s->mb_x == (s->mb_width - 1)) ? -1 : 2;
b_valid = b_valid && s->mb_width > 1;
} else {
//in 4-MV mode different blocks have different B predictor position
switch (n) {
case 0:
if (v->res_rtm_flag)
off = s->mb_x ? -1 : 1;
else
off = s->mb_x ? -1 : 2 * s->mb_width - wrap - 1;
break;
case 1:
off = (s->mb_x == (s->mb_width - 1)) ? -1 : 1;
break;
case 2:
off = 1;
break;
case 3:
off = -1;
}
if (v->field_mode && s->mb_width == 1)
b_valid = b_valid && c_valid;
}
if (v->field_mode) {
a_valid = a_valid && !is_intra[xy - wrap];
b_valid = b_valid && !is_intra[xy - wrap + off];
c_valid = c_valid && !is_intra[xy - 1];
}
if (a_valid) {
const int16_t *A = s->cur_pic.motion_val[dir][xy - wrap + v->blocks_off];
a_f = v->mv_f[dir][xy - wrap + v->blocks_off];
num_oppfield += a_f;
num_samefield += 1 - a_f;
field_predA[0] = A[0];
field_predA[1] = A[1];
} else {
field_predA[0] = field_predA[1] = 0;
a_f = 0;
}
if (b_valid) {
const int16_t *B = s->cur_pic.motion_val[dir][xy - wrap + off + v->blocks_off];
b_f = v->mv_f[dir][xy - wrap + off + v->blocks_off];
num_oppfield += b_f;
num_samefield += 1 - b_f;
field_predB[0] = B[0];
field_predB[1] = B[1];
} else {
field_predB[0] = field_predB[1] = 0;
b_f = 0;
}
if (c_valid) {
const int16_t *C = s->cur_pic.motion_val[dir][xy - 1 + v->blocks_off];
c_f = v->mv_f[dir][xy - 1 + v->blocks_off];
num_oppfield += c_f;
num_samefield += 1 - c_f;
field_predC[0] = C[0];
field_predC[1] = C[1];
} else {
field_predC[0] = field_predC[1] = 0;
c_f = 0;
}
if (v->field_mode) {
if (!v->numref)
// REFFIELD determines if the last field or the second-last field is
// to be used as reference
opposite = 1 - v->reffield;
else {
if (num_samefield <= num_oppfield)
opposite = 1 - pred_flag;
else
opposite = pred_flag;
}
} else
opposite = 0;
if (opposite) {
v->mv_f[dir][xy + v->blocks_off] = 1;
v->ref_field_type[dir] = !v->cur_field_type;
if (a_valid && !a_f) {
field_predA[0] = scaleforopp(v, field_predA[0], 0, dir);
field_predA[1] = scaleforopp(v, field_predA[1], 1, dir);
}
if (b_valid && !b_f) {
field_predB[0] = scaleforopp(v, field_predB[0], 0, dir);
field_predB[1] = scaleforopp(v, field_predB[1], 1, dir);
}
if (c_valid && !c_f) {
field_predC[0] = scaleforopp(v, field_predC[0], 0, dir);
field_predC[1] = scaleforopp(v, field_predC[1], 1, dir);
}
} else {
v->mv_f[dir][xy + v->blocks_off] = 0;
v->ref_field_type[dir] = v->cur_field_type;
if (a_valid && a_f) {
field_predA[0] = scaleforsame(v, field_predA[0], 0, dir);
field_predA[1] = scaleforsame(v, field_predA[1], 1, dir);
}
if (b_valid && b_f) {
field_predB[0] = scaleforsame(v, field_predB[0], 0, dir);
field_predB[1] = scaleforsame(v, field_predB[1], 1, dir);
}
if (c_valid && c_f) {
field_predC[0] = scaleforsame(v, field_predC[0], 0, dir);
field_predC[1] = scaleforsame(v, field_predC[1], 1, dir);
}
}
if (a_valid) {
px = field_predA[0];
py = field_predA[1];
} else if (c_valid) {
px = field_predC[0];
py = field_predC[1];
} else if (b_valid) {
px = field_predB[0];
py = field_predB[1];
} else {
px = 0;
py = 0;
}
if (num_samefield + num_oppfield > 1) {
px = mid_pred(field_predA[0], field_predB[0], field_predC[0]);
py = mid_pred(field_predA[1], field_predB[1], field_predC[1]);
}
/* Pullback MV as specified in 8.3.5.3.4 */
if (!v->field_mode) {
int qx, qy, X, Y;
int MV = mv1 ? -60 : -28;
qx = (s->mb_x << 6) + ((n == 1 || n == 3) ? 32 : 0);
qy = (s->mb_y << 6) + ((n == 2 || n == 3) ? 32 : 0);
X = (s->mb_width << 6) - 4;
Y = (s->mb_height << 6) - 4;
if (qx + px < MV) px = MV - qx;
if (qy + py < MV) py = MV - qy;
if (qx + px > X) px = X - qx;
if (qy + py > Y) py = Y - qy;
}
if (!v->field_mode || s->pict_type != AV_PICTURE_TYPE_B) {
/* Calculate hybrid prediction as specified in 8.3.5.3.5 (also 10.3.5.4.3.5) */
hybridmv_thresh = 32;
if (a_valid && c_valid) {
if (is_intra[xy - wrap])
sum = FFABS(px) + FFABS(py);
else
sum = FFABS(px - field_predA[0]) + FFABS(py - field_predA[1]);
if (sum > hybridmv_thresh) {
if (get_bits1(&s->gb)) { // read HYBRIDPRED bit
px = field_predA[0];
py = field_predA[1];
} else {
px = field_predC[0];
py = field_predC[1];
}
} else {
if (is_intra[xy - 1])
sum = FFABS(px) + FFABS(py);
else
sum = FFABS(px - field_predC[0]) + FFABS(py - field_predC[1]);
if (sum > hybridmv_thresh) {
if (get_bits1(&s->gb)) {
px = field_predA[0];
py = field_predA[1];
} else {
px = field_predC[0];
py = field_predC[1];
}
}
}
}
}
if (v->field_mode && v->numref)
r_y >>= 1;
if (v->field_mode && v->cur_field_type && v->ref_field_type[dir] == 0)
y_bias = 1;
/* store MV using signed modulus of MV range defined in 4.11 */
s->mv[dir][n][0] = s->cur_pic.motion_val[dir][xy + v->blocks_off][0] = ((px + dmv_x + r_x) & ((r_x << 1) - 1)) - r_x;
s->mv[dir][n][1] = s->cur_pic.motion_val[dir][xy + v->blocks_off][1] = ((py + dmv_y + r_y - y_bias) & ((r_y << 1) - 1)) - r_y + y_bias;
if (mv1) { /* duplicate motion data for 1-MV block */
s->cur_pic.motion_val[dir][xy + 1 + v->blocks_off][0] = s->cur_pic.motion_val[dir][xy + v->blocks_off][0];
s->cur_pic.motion_val[dir][xy + 1 + v->blocks_off][1] = s->cur_pic.motion_val[dir][xy + v->blocks_off][1];
s->cur_pic.motion_val[dir][xy + wrap + v->blocks_off][0] = s->cur_pic.motion_val[dir][xy + v->blocks_off][0];
s->cur_pic.motion_val[dir][xy + wrap + v->blocks_off][1] = s->cur_pic.motion_val[dir][xy + v->blocks_off][1];
s->cur_pic.motion_val[dir][xy + wrap + 1 + v->blocks_off][0] = s->cur_pic.motion_val[dir][xy + v->blocks_off][0];
s->cur_pic.motion_val[dir][xy + wrap + 1 + v->blocks_off][1] = s->cur_pic.motion_val[dir][xy + v->blocks_off][1];
v->mv_f[dir][xy + 1 + v->blocks_off] = v->mv_f[dir][xy + v->blocks_off];
v->mv_f[dir][xy + wrap + v->blocks_off] = v->mv_f[dir][xy + wrap + 1 + v->blocks_off] = v->mv_f[dir][xy + v->blocks_off];
}
}
/** Predict and set motion vector for interlaced frame picture MBs
*/
void ff_vc1_pred_mv_intfr(VC1Context *v, int n, int dmv_x, int dmv_y,
int mvn, int r_x, int r_y, int dir)
{
MpegEncContext *s = &v->s;
int xy, wrap, off = 0;
int A[2], B[2], C[2];
int px = 0, py = 0;
int a_valid = 0, b_valid = 0, c_valid = 0;
int field_a, field_b, field_c; // 0: same, 1: opposite
int total_valid, num_samefield, num_oppfield;
int pos_c, pos_b, n_adj;
wrap = s->b8_stride;
xy = s->block_index[n];
if (s->mb_intra) {
s->mv[0][n][0] = s->cur_pic.motion_val[0][xy][0] = 0;
s->mv[0][n][1] = s->cur_pic.motion_val[0][xy][1] = 0;
s->cur_pic.motion_val[1][xy][0] = 0;
s->cur_pic.motion_val[1][xy][1] = 0;
if (mvn == 1) { /* duplicate motion data for 1-MV block */
s->cur_pic.motion_val[0][xy + 1][0] = 0;
s->cur_pic.motion_val[0][xy + 1][1] = 0;
s->cur_pic.motion_val[0][xy + wrap][0] = 0;
s->cur_pic.motion_val[0][xy + wrap][1] = 0;
s->cur_pic.motion_val[0][xy + wrap + 1][0] = 0;
s->cur_pic.motion_val[0][xy + wrap + 1][1] = 0;
v->luma_mv[s->mb_x][0] = v->luma_mv[s->mb_x][1] = 0;
s->cur_pic.motion_val[1][xy + 1][0] = 0;
s->cur_pic.motion_val[1][xy + 1][1] = 0;
s->cur_pic.motion_val[1][xy + wrap][0] = 0;
s->cur_pic.motion_val[1][xy + wrap][1] = 0;
s->cur_pic.motion_val[1][xy + wrap + 1][0] = 0;
s->cur_pic.motion_val[1][xy + wrap + 1][1] = 0;
}
return;
}
off = ((n == 0) || (n == 1)) ? 1 : -1;
/* predict A */
if (s->mb_x || (n == 1) || (n == 3)) {
if ((v->blk_mv_type[xy]) // current block (MB) has a field MV
|| (!v->blk_mv_type[xy] && !v->blk_mv_type[xy - 1])) { // or both have frame MV
A[0] = s->cur_pic.motion_val[dir][xy - 1][0];
A[1] = s->cur_pic.motion_val[dir][xy - 1][1];
a_valid = 1;
} else { // current block has frame mv and cand. has field MV (so average)
A[0] = (s->cur_pic.motion_val[dir][xy - 1][0]
+ s->cur_pic.motion_val[dir][xy - 1 + off * wrap][0] + 1) >> 1;
A[1] = (s->cur_pic.motion_val[dir][xy - 1][1]
+ s->cur_pic.motion_val[dir][xy - 1 + off * wrap][1] + 1) >> 1;
a_valid = 1;
}
if (!(n & 1) && v->is_intra[s->mb_x - 1]) {
a_valid = 0;
A[0] = A[1] = 0;
}
} else
A[0] = A[1] = 0;
/* Predict B and C */
B[0] = B[1] = C[0] = C[1] = 0;
if (n == 0 || n == 1 || v->blk_mv_type[xy]) {
if (!s->first_slice_line) {
if (!v->is_intra[s->mb_x - s->mb_stride]) {
b_valid = 1;
n_adj = n | 2;
pos_b = s->block_index[n_adj] - 2 * wrap;
if (v->blk_mv_type[pos_b] && v->blk_mv_type[xy]) {
n_adj = (n & 2) | (n & 1);
}
B[0] = s->cur_pic.motion_val[dir][s->block_index[n_adj] - 2 * wrap][0];
B[1] = s->cur_pic.motion_val[dir][s->block_index[n_adj] - 2 * wrap][1];
if (v->blk_mv_type[pos_b] && !v->blk_mv_type[xy]) {
B[0] = (B[0] + s->cur_pic.motion_val[dir][s->block_index[n_adj ^ 2] - 2 * wrap][0] + 1) >> 1;
B[1] = (B[1] + s->cur_pic.motion_val[dir][s->block_index[n_adj ^ 2] - 2 * wrap][1] + 1) >> 1;
}
}
if (s->mb_width > 1) {
if (!v->is_intra[s->mb_x - s->mb_stride + 1]) {
c_valid = 1;
n_adj = 2;
pos_c = s->block_index[2] - 2 * wrap + 2;
if (v->blk_mv_type[pos_c] && v->blk_mv_type[xy]) {
n_adj = n & 2;
}
C[0] = s->cur_pic.motion_val[dir][s->block_index[n_adj] - 2 * wrap + 2][0];
C[1] = s->cur_pic.motion_val[dir][s->block_index[n_adj] - 2 * wrap + 2][1];
if (v->blk_mv_type[pos_c] && !v->blk_mv_type[xy]) {
C[0] = (1 + C[0] + (s->cur_pic.motion_val[dir][s->block_index[n_adj ^ 2] - 2 * wrap + 2][0])) >> 1;
C[1] = (1 + C[1] + (s->cur_pic.motion_val[dir][s->block_index[n_adj ^ 2] - 2 * wrap + 2][1])) >> 1;
}
if (s->mb_x == s->mb_width - 1) {
if (!v->is_intra[s->mb_x - s->mb_stride - 1]) {
c_valid = 1;
n_adj = 3;
pos_c = s->block_index[3] - 2 * wrap - 2;
if (v->blk_mv_type[pos_c] && v->blk_mv_type[xy]) {
n_adj = n | 1;
}
C[0] = s->cur_pic.motion_val[dir][s->block_index[n_adj] - 2 * wrap - 2][0];
C[1] = s->cur_pic.motion_val[dir][s->block_index[n_adj] - 2 * wrap - 2][1];
if (v->blk_mv_type[pos_c] && !v->blk_mv_type[xy]) {
C[0] = (1 + C[0] + s->cur_pic.motion_val[dir][s->block_index[1] - 2 * wrap - 2][0]) >> 1;
C[1] = (1 + C[1] + s->cur_pic.motion_val[dir][s->block_index[1] - 2 * wrap - 2][1]) >> 1;
}
} else
c_valid = 0;
}
}
}
}
} else {
pos_b = s->block_index[1];
b_valid = 1;
B[0] = s->cur_pic.motion_val[dir][pos_b][0];
B[1] = s->cur_pic.motion_val[dir][pos_b][1];
pos_c = s->block_index[0];
c_valid = 1;
C[0] = s->cur_pic.motion_val[dir][pos_c][0];
C[1] = s->cur_pic.motion_val[dir][pos_c][1];
}
total_valid = a_valid + b_valid + c_valid;
// check if predictor A is out of bounds
if (!s->mb_x && !(n == 1 || n == 3)) {
A[0] = A[1] = 0;
}
// check if predictor B is out of bounds
if ((s->first_slice_line && v->blk_mv_type[xy]) || (s->first_slice_line && !(n & 2))) {
B[0] = B[1] = C[0] = C[1] = 0;
}
if (!v->blk_mv_type[xy]) {
if (s->mb_width == 1) {
px = B[0];
py = B[1];
} else {
if (total_valid >= 2) {
px = mid_pred(A[0], B[0], C[0]);
py = mid_pred(A[1], B[1], C[1]);
} else if (total_valid) {
if (a_valid) { px = A[0]; py = A[1]; }
else if (b_valid) { px = B[0]; py = B[1]; }
else { px = C[0]; py = C[1]; }
}
}
} else {
if (a_valid)
field_a = (A[1] & 4) ? 1 : 0;
else
field_a = 0;
if (b_valid)
field_b = (B[1] & 4) ? 1 : 0;
else
field_b = 0;
if (c_valid)
field_c = (C[1] & 4) ? 1 : 0;
else
field_c = 0;
num_oppfield = field_a + field_b + field_c;
num_samefield = total_valid - num_oppfield;
if (total_valid == 3) {
if ((num_samefield == 3) || (num_oppfield == 3)) {
px = mid_pred(A[0], B[0], C[0]);
py = mid_pred(A[1], B[1], C[1]);
} else if (num_samefield >= num_oppfield) {
/* take one MV from same field set depending on priority
the check for B may not be necessary */
px = !field_a ? A[0] : B[0];
py = !field_a ? A[1] : B[1];
} else {
px = field_a ? A[0] : B[0];
py = field_a ? A[1] : B[1];
}
} else if (total_valid == 2) {
if (num_samefield >= num_oppfield) {
if (!field_a && a_valid) {
px = A[0];
py = A[1];
} else if (!field_b && b_valid) {
px = B[0];
py = B[1];
} else /*if (c_valid)*/ {
av_assert1(c_valid);
px = C[0];
py = C[1];
}
} else {
if (field_a && a_valid) {
px = A[0];
py = A[1];
} else /*if (field_b && b_valid)*/ {
av_assert1(field_b && b_valid);
px = B[0];
py = B[1];
}
}
} else if (total_valid == 1) {
px = (a_valid) ? A[0] : ((b_valid) ? B[0] : C[0]);
py = (a_valid) ? A[1] : ((b_valid) ? B[1] : C[1]);
}
}
/* store MV using signed modulus of MV range defined in 4.11 */
s->mv[dir][n][0] = s->cur_pic.motion_val[dir][xy][0] = ((px + dmv_x + r_x) & ((r_x << 1) - 1)) - r_x;
s->mv[dir][n][1] = s->cur_pic.motion_val[dir][xy][1] = ((py + dmv_y + r_y) & ((r_y << 1) - 1)) - r_y;
if (mvn == 1) { /* duplicate motion data for 1-MV block */
s->cur_pic.motion_val[dir][xy + 1 ][0] = s->cur_pic.motion_val[dir][xy][0];
s->cur_pic.motion_val[dir][xy + 1 ][1] = s->cur_pic.motion_val[dir][xy][1];
s->cur_pic.motion_val[dir][xy + wrap ][0] = s->cur_pic.motion_val[dir][xy][0];
s->cur_pic.motion_val[dir][xy + wrap ][1] = s->cur_pic.motion_val[dir][xy][1];
s->cur_pic.motion_val[dir][xy + wrap + 1][0] = s->cur_pic.motion_val[dir][xy][0];
s->cur_pic.motion_val[dir][xy + wrap + 1][1] = s->cur_pic.motion_val[dir][xy][1];
} else if (mvn == 2) { /* duplicate motion data for 2-Field MV block */
s->cur_pic.motion_val[dir][xy + 1][0] = s->cur_pic.motion_val[dir][xy][0];
s->cur_pic.motion_val[dir][xy + 1][1] = s->cur_pic.motion_val[dir][xy][1];
s->mv[dir][n + 1][0] = s->mv[dir][n][0];
s->mv[dir][n + 1][1] = s->mv[dir][n][1];
}
}
void ff_vc1_pred_b_mv(VC1Context *v, int dmv_x[2], int dmv_y[2],
int direct, int mvtype)
{
MpegEncContext *s = &v->s;
int xy, wrap;
int px, py;
int sum;
int r_x, r_y;
const uint8_t *is_intra = v->mb_type[0];
av_assert0(!v->field_mode);
r_x = v->range_x;
r_y = v->range_y;
/* scale MV difference to be quad-pel */
if (!s->quarter_sample) {
dmv_x[0] *= 2;
dmv_y[0] *= 2;
dmv_x[1] *= 2;
dmv_y[1] *= 2;
}
wrap = s->b8_stride;
xy = s->block_index[0];
if (s->mb_intra) {
s->cur_pic.motion_val[0][xy][0] =
s->cur_pic.motion_val[0][xy][1] =
s->cur_pic.motion_val[1][xy][0] =
s->cur_pic.motion_val[1][xy][1] = 0;
return;
}
if (direct && s->next_pic.ptr->field_picture)
av_log(s->avctx, AV_LOG_WARNING, "Mixed frame/field direct mode not supported\n");
s->mv[0][0][0] = scale_mv(s->next_pic.motion_val[1][xy][0], v->bfraction, 0, s->quarter_sample);
s->mv[0][0][1] = scale_mv(s->next_pic.motion_val[1][xy][1], v->bfraction, 0, s->quarter_sample);
s->mv[1][0][0] = scale_mv(s->next_pic.motion_val[1][xy][0], v->bfraction, 1, s->quarter_sample);
s->mv[1][0][1] = scale_mv(s->next_pic.motion_val[1][xy][1], v->bfraction, 1, s->quarter_sample);
/* Pullback predicted motion vectors as specified in 8.4.5.4 */
s->mv[0][0][0] = av_clip(s->mv[0][0][0], -60 - (s->mb_x << 6), (s->mb_width << 6) - 4 - (s->mb_x << 6));
s->mv[0][0][1] = av_clip(s->mv[0][0][1], -60 - (s->mb_y << 6), (s->mb_height << 6) - 4 - (s->mb_y << 6));
s->mv[1][0][0] = av_clip(s->mv[1][0][0], -60 - (s->mb_x << 6), (s->mb_width << 6) - 4 - (s->mb_x << 6));
s->mv[1][0][1] = av_clip(s->mv[1][0][1], -60 - (s->mb_y << 6), (s->mb_height << 6) - 4 - (s->mb_y << 6));
if (direct) {
s->cur_pic.motion_val[0][xy][0] = s->mv[0][0][0];
s->cur_pic.motion_val[0][xy][1] = s->mv[0][0][1];
s->cur_pic.motion_val[1][xy][0] = s->mv[1][0][0];
s->cur_pic.motion_val[1][xy][1] = s->mv[1][0][1];
return;
}
if ((mvtype == BMV_TYPE_FORWARD) || (mvtype == BMV_TYPE_INTERPOLATED)) {
int16_t *C = s->cur_pic.motion_val[0][xy - 2];
const int16_t *A = s->cur_pic.motion_val[0][xy - wrap * 2];
int off = (s->mb_x == (s->mb_width - 1)) ? -2 : 2;
const int16_t *B = s->cur_pic.motion_val[0][xy - wrap * 2 + off];
if (!s->mb_x) C[0] = C[1] = 0;
if (!s->first_slice_line) { // predictor A is not out of bounds
if (s->mb_width == 1) {
px = A[0];
py = A[1];
} else {
px = mid_pred(A[0], B[0], C[0]);
py = mid_pred(A[1], B[1], C[1]);
}
} else if (s->mb_x) { // predictor C is not out of bounds
px = C[0];
py = C[1];
} else {
px = py = 0;
}
/* Pullback MV as specified in 8.3.5.3.4 */
{
int qx, qy, X, Y;
int sh = v->profile < PROFILE_ADVANCED ? 5 : 6;
int MV = 4 - (1 << sh);
qx = (s->mb_x << sh);
qy = (s->mb_y << sh);
X = (s->mb_width << sh) - 4;
Y = (s->mb_height << sh) - 4;
if (qx + px < MV) px = MV - qx;
if (qy + py < MV) py = MV - qy;
if (qx + px > X) px = X - qx;
if (qy + py > Y) py = Y - qy;
}
/* Calculate hybrid prediction as specified in 8.3.5.3.5 */
if (0 && !s->first_slice_line && s->mb_x) {
if (is_intra[xy - wrap])
sum = FFABS(px) + FFABS(py);
else
sum = FFABS(px - A[0]) + FFABS(py - A[1]);
if (sum > 32) {
if (get_bits1(&s->gb)) {
px = A[0];
py = A[1];
} else {
px = C[0];
py = C[1];
}
} else {
if (is_intra[xy - 2])
sum = FFABS(px) + FFABS(py);
else
sum = FFABS(px - C[0]) + FFABS(py - C[1]);
if (sum > 32) {
if (get_bits1(&s->gb)) {
px = A[0];
py = A[1];
} else {
px = C[0];
py = C[1];
}
}
}
}
/* store MV using signed modulus of MV range defined in 4.11 */
s->mv[0][0][0] = ((px + dmv_x[0] + r_x) & ((r_x << 1) - 1)) - r_x;
s->mv[0][0][1] = ((py + dmv_y[0] + r_y) & ((r_y << 1) - 1)) - r_y;
}
if ((mvtype == BMV_TYPE_BACKWARD) || (mvtype == BMV_TYPE_INTERPOLATED)) {
int16_t *C = s->cur_pic.motion_val[1][xy - 2];
const int16_t *A = s->cur_pic.motion_val[1][xy - wrap * 2];
int off = (s->mb_x == (s->mb_width - 1)) ? -2 : 2;
const int16_t *B = s->cur_pic.motion_val[1][xy - wrap * 2 + off];
if (!s->mb_x)
C[0] = C[1] = 0;
if (!s->first_slice_line) { // predictor A is not out of bounds
if (s->mb_width == 1) {
px = A[0];
py = A[1];
} else {
px = mid_pred(A[0], B[0], C[0]);
py = mid_pred(A[1], B[1], C[1]);
}
} else if (s->mb_x) { // predictor C is not out of bounds
px = C[0];
py = C[1];
} else {
px = py = 0;
}
/* Pullback MV as specified in 8.3.5.3.4 */
{
int qx, qy, X, Y;
int sh = v->profile < PROFILE_ADVANCED ? 5 : 6;
int MV = 4 - (1 << sh);
qx = (s->mb_x << sh);
qy = (s->mb_y << sh);
X = (s->mb_width << sh) - 4;
Y = (s->mb_height << sh) - 4;
if (qx + px < MV) px = MV - qx;
if (qy + py < MV) py = MV - qy;
if (qx + px > X) px = X - qx;
if (qy + py > Y) py = Y - qy;
}
/* Calculate hybrid prediction as specified in 8.3.5.3.5 */
if (0 && !s->first_slice_line && s->mb_x) {
if (is_intra[xy - wrap])
sum = FFABS(px) + FFABS(py);
else
sum = FFABS(px - A[0]) + FFABS(py - A[1]);
if (sum > 32) {
if (get_bits1(&s->gb)) {
px = A[0];
py = A[1];
} else {
px = C[0];
py = C[1];
}
} else {
if (is_intra[xy - 2])
sum = FFABS(px) + FFABS(py);
else
sum = FFABS(px - C[0]) + FFABS(py - C[1]);
if (sum > 32) {
if (get_bits1(&s->gb)) {
px = A[0];
py = A[1];
} else {
px = C[0];
py = C[1];
}
}
}
}
/* store MV using signed modulus of MV range defined in 4.11 */
s->mv[1][0][0] = ((px + dmv_x[1] + r_x) & ((r_x << 1) - 1)) - r_x;
s->mv[1][0][1] = ((py + dmv_y[1] + r_y) & ((r_y << 1) - 1)) - r_y;
}
s->cur_pic.motion_val[0][xy][0] = s->mv[0][0][0];
s->cur_pic.motion_val[0][xy][1] = s->mv[0][0][1];
s->cur_pic.motion_val[1][xy][0] = s->mv[1][0][0];
s->cur_pic.motion_val[1][xy][1] = s->mv[1][0][1];
}
void ff_vc1_pred_b_mv_intfi(VC1Context *v, int n, int *dmv_x, int *dmv_y,
int mv1, int *pred_flag)
{
int dir = (v->bmvtype == BMV_TYPE_BACKWARD) ? 1 : 0;
MpegEncContext *s = &v->s;
int mb_pos = s->mb_x + s->mb_y * s->mb_stride;
if (v->bmvtype == BMV_TYPE_DIRECT) {
int total_opp, k, f;
if (s->next_pic.mb_type[mb_pos + v->mb_off] != MB_TYPE_INTRA) {
s->mv[0][0][0] = scale_mv(s->next_pic.motion_val[1][s->block_index[0] + v->blocks_off][0],
v->bfraction, 0, s->quarter_sample);
s->mv[0][0][1] = scale_mv(s->next_pic.motion_val[1][s->block_index[0] + v->blocks_off][1],
v->bfraction, 0, s->quarter_sample);
s->mv[1][0][0] = scale_mv(s->next_pic.motion_val[1][s->block_index[0] + v->blocks_off][0],
v->bfraction, 1, s->quarter_sample);
s->mv[1][0][1] = scale_mv(s->next_pic.motion_val[1][s->block_index[0] + v->blocks_off][1],
v->bfraction, 1, s->quarter_sample);
total_opp = v->mv_f_next[0][s->block_index[0] + v->blocks_off]
+ v->mv_f_next[0][s->block_index[1] + v->blocks_off]
+ v->mv_f_next[0][s->block_index[2] + v->blocks_off]
+ v->mv_f_next[0][s->block_index[3] + v->blocks_off];
f = (total_opp > 2) ? 1 : 0;
} else {
s->mv[0][0][0] = s->mv[0][0][1] = 0;
s->mv[1][0][0] = s->mv[1][0][1] = 0;
f = 0;
}
v->ref_field_type[0] = v->ref_field_type[1] = v->cur_field_type ^ f;
for (k = 0; k < 4; k++) {
s->cur_pic.motion_val[0][s->block_index[k] + v->blocks_off][0] = s->mv[0][0][0];
s->cur_pic.motion_val[0][s->block_index[k] + v->blocks_off][1] = s->mv[0][0][1];
s->cur_pic.motion_val[1][s->block_index[k] + v->blocks_off][0] = s->mv[1][0][0];
s->cur_pic.motion_val[1][s->block_index[k] + v->blocks_off][1] = s->mv[1][0][1];
v->mv_f[0][s->block_index[k] + v->blocks_off] = f;
v->mv_f[1][s->block_index[k] + v->blocks_off] = f;
}
return;
}
if (v->bmvtype == BMV_TYPE_INTERPOLATED) {
ff_vc1_pred_mv(v, 0, dmv_x[0], dmv_y[0], 1, v->range_x, v->range_y, v->mb_type[0], pred_flag[0], 0);
ff_vc1_pred_mv(v, 0, dmv_x[1], dmv_y[1], 1, v->range_x, v->range_y, v->mb_type[0], pred_flag[1], 1);
return;
}
if (dir) { // backward
ff_vc1_pred_mv(v, n, dmv_x[1], dmv_y[1], mv1, v->range_x, v->range_y, v->mb_type[0], pred_flag[1], 1);
if (n == 3 || mv1) {
ff_vc1_pred_mv(v, 0, dmv_x[0], dmv_y[0], 1, v->range_x, v->range_y, v->mb_type[0], 0, 0);
}
} else { // forward
ff_vc1_pred_mv(v, n, dmv_x[0], dmv_y[0], mv1, v->range_x, v->range_y, v->mb_type[0], pred_flag[0], 0);
if (n == 3 || mv1) {
ff_vc1_pred_mv(v, 0, dmv_x[1], dmv_y[1], 1, v->range_x, v->range_y, v->mb_type[0], 0, 1);
}
}
}