This source file includes following definitions.
- mc_luma
- mc_chroma
- generate_inter_prediction_samples
- logmvcand
- get_PartMode
- get_mv_info
- get_PartMode
- get_mv_info
- derive_spatial_merging_candidates
- derive_zero_motion_vector_candidates
- scale_mv
- derive_collocated_motion_vectors
- derive_temporal_luma_vector_prediction
- derive_combined_bipredictive_merging_candidates
- get_merge_candidate_list_without_step_9
- get_merge_candidate_list
- get_merge_candidate_list_from_tree
- derive_luma_motion_merge_mode
- derive_spatial_luma_vector_prediction
- fill_luma_motion_vector_predictors
- luma_motion_vector_prediction
- logMV
- motion_vectors_and_ref_indices
- decode_prediction_unit
#include "motion.h"
#include "decctx.h"
#include "util.h"
#include "dpb.h"
#include "encoder/encoder-context.h"
#include <assert.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <malloc.h>
#elif defined(HAVE_ALLOCA_H)
# include <alloca.h>
#endif
#define MAX_CU_SIZE 64
static int extra_before[4] = { 0,3,3,2 };
static int extra_after [4] = { 0,3,4,4 };
template <class pixel_t>
void mc_luma(const base_context* ctx,
const seq_parameter_set* sps, int mv_x, int mv_y,
int xP,int yP,
int16_t* out, int out_stride,
const pixel_t* ref, int ref_stride,
int nPbW, int nPbH, int bitDepth_L)
{
int xFracL = mv_x & 3;
int yFracL = mv_y & 3;
int xIntOffsL = xP + (mv_x>>2);
int yIntOffsL = yP + (mv_y>>2);
const int shift3 = 14 - sps->BitDepth_Y;
int w = sps->pic_width_in_luma_samples;
int h = sps->pic_height_in_luma_samples;
ALIGNED_16(int16_t) mcbuffer[MAX_CU_SIZE * (MAX_CU_SIZE+7)];
if (xFracL==0 && yFracL==0) {
if (xIntOffsL >= 0 && yIntOffsL >= 0 &&
nPbW+xIntOffsL <= w && nPbH+yIntOffsL <= h) {
ctx->acceleration.put_hevc_qpel(out, out_stride,
&ref[yIntOffsL*ref_stride + xIntOffsL],
ref_stride ,
nPbW,nPbH, mcbuffer, 0,0, bitDepth_L);
}
else {
for (int y=0;y<nPbH;y++)
for (int x=0;x<nPbW;x++) {
int xA = Clip3(0,w-1,x + xIntOffsL);
int yA = Clip3(0,h-1,y + yIntOffsL);
out[y*out_stride+x] = ref[ xA + yA*ref_stride ] << shift3;
}
}
#ifdef DE265_LOG_TRACE
logtrace(LogMotion,"---MC luma %d %d = direct---\n",xFracL,yFracL);
for (int y=0;y<nPbH;y++) {
for (int x=0;x<nPbW;x++) {
int xA = Clip3(0,w-1,x + xIntOffsL);
int yA = Clip3(0,h-1,y + yIntOffsL);
logtrace(LogMotion,"%02x ", ref[ xA + yA*ref_stride ]);
}
logtrace(LogMotion,"\n");
}
logtrace(LogMotion," -> \n");
for (int y=0;y<nPbH;y++) {
for (int x=0;x<nPbW;x++) {
logtrace(LogMotion,"%02x ",out[y*out_stride+x] >> 6);
}
logtrace(LogMotion,"\n");
}
#endif
}
else {
int extra_left = extra_before[xFracL];
int extra_right = extra_after [xFracL];
int extra_top = extra_before[yFracL];
int extra_bottom = extra_after [yFracL];
pixel_t padbuf[(MAX_CU_SIZE+16)*(MAX_CU_SIZE+7)];
const pixel_t* src_ptr;
int src_stride;
if (-extra_left + xIntOffsL >= 0 &&
-extra_top + yIntOffsL >= 0 &&
nPbW+extra_right + xIntOffsL < w &&
nPbH+extra_bottom + yIntOffsL < h) {
src_ptr = &ref[xIntOffsL + yIntOffsL*ref_stride];
src_stride = ref_stride;
}
else {
for (int y=-extra_top;y<nPbH+extra_bottom;y++) {
for (int x=-extra_left;x<nPbW+extra_right;x++) {
int xA = Clip3(0,w-1,x + xIntOffsL);
int yA = Clip3(0,h-1,y + yIntOffsL);
padbuf[x+extra_left + (y+extra_top)*(MAX_CU_SIZE+16)] = ref[ xA + yA*ref_stride ];
}
}
src_ptr = &padbuf[extra_top*(MAX_CU_SIZE+16) + extra_left];
src_stride = MAX_CU_SIZE+16;
}
ctx->acceleration.put_hevc_qpel(out, out_stride,
src_ptr, src_stride ,
nPbW,nPbH, mcbuffer, xFracL,yFracL, bitDepth_L);
logtrace(LogMotion,"---V---\n");
for (int y=0;y<nPbH;y++) {
for (int x=0;x<nPbW;x++) {
logtrace(LogMotion,"%04x ",out[x+y*out_stride]);
}
logtrace(LogMotion,"\n");
}
}
}
template <class pixel_t>
void mc_chroma(const base_context* ctx,
const seq_parameter_set* sps,
int mv_x, int mv_y,
int xP,int yP,
int16_t* out, int out_stride,
const pixel_t* ref, int ref_stride,
int nPbWC, int nPbHC, int bit_depth_C)
{
const int shift3 = 14 - sps->BitDepth_C;
int wC = sps->pic_width_in_luma_samples /sps->SubWidthC;
int hC = sps->pic_height_in_luma_samples/sps->SubHeightC;
mv_x *= 2 / sps->SubWidthC;
mv_y *= 2 / sps->SubHeightC;
int xFracC = mv_x & 7;
int yFracC = mv_y & 7;
int xIntOffsC = xP/sps->SubWidthC + (mv_x>>3);
int yIntOffsC = yP/sps->SubHeightC + (mv_y>>3);
ALIGNED_32(int16_t mcbuffer[MAX_CU_SIZE*(MAX_CU_SIZE+7)]);
if (xFracC == 0 && yFracC == 0) {
if (xIntOffsC>=0 && nPbWC+xIntOffsC<=wC &&
yIntOffsC>=0 && nPbHC+yIntOffsC<=hC) {
ctx->acceleration.put_hevc_epel(out, out_stride,
&ref[xIntOffsC + yIntOffsC*ref_stride], ref_stride,
nPbWC,nPbHC, 0,0, NULL, bit_depth_C);
}
else
{
for (int y=0;y<nPbHC;y++)
for (int x=0;x<nPbWC;x++) {
int xB = Clip3(0,wC-1,x + xIntOffsC);
int yB = Clip3(0,hC-1,y + yIntOffsC);
out[y*out_stride+x] = ref[ xB + yB*ref_stride ] << shift3;
}
}
}
else {
pixel_t padbuf[(MAX_CU_SIZE+16)*(MAX_CU_SIZE+3)];
const pixel_t* src_ptr;
int src_stride;
int extra_top = 1;
int extra_left = 1;
int extra_right = 2;
int extra_bottom = 2;
if (xIntOffsC>=1 && nPbWC+xIntOffsC<=wC-2 &&
yIntOffsC>=1 && nPbHC+yIntOffsC<=hC-2) {
src_ptr = &ref[xIntOffsC + yIntOffsC*ref_stride];
src_stride = ref_stride;
}
else {
for (int y=-extra_top;y<nPbHC+extra_bottom;y++) {
for (int x=-extra_left;x<nPbWC+extra_right;x++) {
int xA = Clip3(0,wC-1,x + xIntOffsC);
int yA = Clip3(0,hC-1,y + yIntOffsC);
padbuf[x+extra_left + (y+extra_top)*(MAX_CU_SIZE+16)] = ref[ xA + yA*ref_stride ];
}
}
src_ptr = &padbuf[extra_left + extra_top*(MAX_CU_SIZE+16)];
src_stride = MAX_CU_SIZE+16;
}
if (xFracC && yFracC) {
ctx->acceleration.put_hevc_epel_hv(out, out_stride,
src_ptr, src_stride,
nPbWC,nPbHC, xFracC,yFracC, mcbuffer, bit_depth_C);
}
else if (xFracC) {
ctx->acceleration.put_hevc_epel_h(out, out_stride,
src_ptr, src_stride,
nPbWC,nPbHC, xFracC,yFracC, mcbuffer, bit_depth_C);
}
else if (yFracC) {
ctx->acceleration.put_hevc_epel_v(out, out_stride,
src_ptr, src_stride,
nPbWC,nPbHC, xFracC,yFracC, mcbuffer, bit_depth_C);
}
else {
assert(false);
}
}
}
void generate_inter_prediction_samples(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
int xC,int yC,
int xB,int yB,
int nCS, int nPbW,int nPbH,
const PBMotion* vi)
{
int xP = xC+xB;
int yP = yC+yB;
void* pixels[3];
int stride[3];
const pic_parameter_set* pps = shdr->pps;
const seq_parameter_set* sps = pps->sps;
const int SubWidthC = sps->SubWidthC;
const int SubHeightC = sps->SubHeightC;
pixels[0] = img->get_image_plane_at_pos_any_depth(0,xP,yP);
stride[0] = img->get_image_stride(0);
pixels[1] = img->get_image_plane_at_pos_any_depth(1,xP/SubWidthC,yP/SubHeightC);
stride[1] = img->get_image_stride(1);
pixels[2] = img->get_image_plane_at_pos_any_depth(2,xP/SubWidthC,yP/SubHeightC);
stride[2] = img->get_image_stride(2);
ALIGNED_16(int16_t) predSamplesL [2 ][MAX_CU_SIZE* MAX_CU_SIZE];
ALIGNED_16(int16_t) predSamplesC[2 ][2 ][MAX_CU_SIZE* MAX_CU_SIZE];
int predFlag[2];
predFlag[0] = vi->predFlag[0];
predFlag[1] = vi->predFlag[1];
const int bit_depth_L = sps->BitDepth_Y;
const int bit_depth_C = sps->BitDepth_C;
if (pps->weighted_pred_flag==0) {
if (predFlag[0] && predFlag[1]) {
if (vi->mv[0].x == vi->mv[1].x &&
vi->mv[0].y == vi->mv[1].y &&
shdr->RefPicList[0][vi->refIdx[0]] ==
shdr->RefPicList[1][vi->refIdx[1]]) {
predFlag[1] = 0;
}
}
}
for (int l=0;l<2;l++) {
if (predFlag[l]) {
if (vi->refIdx[l] >= MAX_NUM_REF_PICS) {
img->integrity = INTEGRITY_DECODING_ERRORS;
ctx->add_warning(DE265_WARNING_NONEXISTING_REFERENCE_PICTURE_ACCESSED, false);
return;
}
const de265_image* refPic = ctx->get_image(shdr->RefPicList[l][vi->refIdx[l]]);
logtrace(LogMotion, "refIdx: %d -> dpb[%d]\n", vi->refIdx[l], shdr->RefPicList[l][vi->refIdx[l]]);
if (refPic->PicState == UnusedForReference) {
img->integrity = INTEGRITY_DECODING_ERRORS;
ctx->add_warning(DE265_WARNING_NONEXISTING_REFERENCE_PICTURE_ACCESSED, false);
}
else {
logtrace(LogMotion,"do MC: L%d,MV=%d;%d RefPOC=%d\n",
l,vi->mv[l].x,vi->mv[l].y,refPic->PicOrderCntVal);
if (img->high_bit_depth(0)) {
mc_luma(ctx, sps, vi->mv[l].x, vi->mv[l].y, xP,yP,
predSamplesL[l],nCS,
(const uint16_t*)refPic->get_image_plane(0),
refPic->get_luma_stride(), nPbW,nPbH, bit_depth_L);
}
else {
mc_luma(ctx, sps, vi->mv[l].x, vi->mv[l].y, xP,yP,
predSamplesL[l],nCS,
(const uint8_t*)refPic->get_image_plane(0),
refPic->get_luma_stride(), nPbW,nPbH, bit_depth_L);
}
if (img->high_bit_depth(0)) {
mc_chroma(ctx, sps, vi->mv[l].x, vi->mv[l].y, xP,yP,
predSamplesC[0][l],nCS, (const uint16_t*)refPic->get_image_plane(1),
refPic->get_chroma_stride(), nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
mc_chroma(ctx, sps, vi->mv[l].x, vi->mv[l].y, xP,yP,
predSamplesC[1][l],nCS, (const uint16_t*)refPic->get_image_plane(2),
refPic->get_chroma_stride(), nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
}
else {
mc_chroma(ctx, sps, vi->mv[l].x, vi->mv[l].y, xP,yP,
predSamplesC[0][l],nCS, (const uint8_t*)refPic->get_image_plane(1),
refPic->get_chroma_stride(), nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
mc_chroma(ctx, sps, vi->mv[l].x, vi->mv[l].y, xP,yP,
predSamplesC[1][l],nCS, (const uint8_t*)refPic->get_image_plane(2),
refPic->get_chroma_stride(), nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
}
}
}
}
const int shift1_L = libde265_max(2,14-sps->BitDepth_Y);
const int offset_shift1_L = img->get_sps().WpOffsetBdShiftY;
const int shift1_C = libde265_max(2,14-sps->BitDepth_C);
const int offset_shift1_C = img->get_sps().WpOffsetBdShiftC;
logtrace(LogMotion,"predFlags (modified): %d %d\n", predFlag[0], predFlag[1]);
if (shdr->slice_type == SLICE_TYPE_P) {
if (pps->weighted_pred_flag==0) {
if (predFlag[0]==1 && predFlag[1]==0) {
ctx->acceleration.put_unweighted_pred(pixels[0], stride[0],
predSamplesL[0],nCS, nPbW,nPbH, bit_depth_L);
ctx->acceleration.put_unweighted_pred(pixels[1], stride[1],
predSamplesC[0][0],nCS,
nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
ctx->acceleration.put_unweighted_pred(pixels[2], stride[2],
predSamplesC[1][0],nCS,
nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
}
else {
ctx->add_warning(DE265_WARNING_BOTH_PREDFLAGS_ZERO, false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
}
else {
if (predFlag[0]==1 && predFlag[1]==0) {
int refIdx0 = vi->refIdx[0];
int luma_log2WD = shdr->luma_log2_weight_denom + shift1_L;
int chroma_log2WD = shdr->ChromaLog2WeightDenom + shift1_C;
int luma_w0 = shdr->LumaWeight[0][refIdx0];
int luma_o0 = shdr->luma_offset[0][refIdx0] * (1<<(offset_shift1_L));
int chroma0_w0 = shdr->ChromaWeight[0][refIdx0][0];
int chroma0_o0 = shdr->ChromaOffset[0][refIdx0][0] * (1<<(offset_shift1_C));
int chroma1_w0 = shdr->ChromaWeight[0][refIdx0][1];
int chroma1_o0 = shdr->ChromaOffset[0][refIdx0][1] * (1<<(offset_shift1_C));
logtrace(LogMotion,"weighted-0 [%d] %d %d %d %dx%d\n", refIdx0, luma_log2WD-6,luma_w0,luma_o0,nPbW,nPbH);
ctx->acceleration.put_weighted_pred(pixels[0], stride[0],
predSamplesL[0],nCS, nPbW,nPbH,
luma_w0, luma_o0, luma_log2WD, bit_depth_L);
ctx->acceleration.put_weighted_pred(pixels[1], stride[1],
predSamplesC[0][0],nCS, nPbW/SubWidthC,nPbH/SubHeightC,
chroma0_w0, chroma0_o0, chroma_log2WD, bit_depth_C);
ctx->acceleration.put_weighted_pred(pixels[2], stride[2],
predSamplesC[1][0],nCS, nPbW/SubWidthC,nPbH/SubHeightC,
chroma1_w0, chroma1_o0, chroma_log2WD, bit_depth_C);
}
else {
ctx->add_warning(DE265_WARNING_BOTH_PREDFLAGS_ZERO, false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
}
}
else {
assert(shdr->slice_type == SLICE_TYPE_B);
if (predFlag[0]==1 && predFlag[1]==1) {
if (pps->weighted_bipred_flag==0) {
int16_t* in0 = predSamplesL[0];
int16_t* in1 = predSamplesL[1];
ctx->acceleration.put_weighted_pred_avg(pixels[0], stride[0],
in0,in1, nCS, nPbW, nPbH, bit_depth_L);
int16_t* in00 = predSamplesC[0][0];
int16_t* in01 = predSamplesC[0][1];
int16_t* in10 = predSamplesC[1][0];
int16_t* in11 = predSamplesC[1][1];
ctx->acceleration.put_weighted_pred_avg(pixels[1], stride[1],
in00,in01, nCS,
nPbW/SubWidthC, nPbH/SubHeightC, bit_depth_C);
ctx->acceleration.put_weighted_pred_avg(pixels[2], stride[2],
in10,in11, nCS,
nPbW/SubWidthC, nPbH/SubHeightC, bit_depth_C);
}
else {
int refIdx0 = vi->refIdx[0];
int refIdx1 = vi->refIdx[1];
int luma_log2WD = shdr->luma_log2_weight_denom + shift1_L;
int chroma_log2WD = shdr->ChromaLog2WeightDenom + shift1_C;
int luma_w0 = shdr->LumaWeight[0][refIdx0];
int luma_o0 = shdr->luma_offset[0][refIdx0] * (1<<(offset_shift1_L));
int luma_w1 = shdr->LumaWeight[1][refIdx1];
int luma_o1 = shdr->luma_offset[1][refIdx1] * (1<<(offset_shift1_L));
int chroma0_w0 = shdr->ChromaWeight[0][refIdx0][0];
int chroma0_o0 = shdr->ChromaOffset[0][refIdx0][0] * (1<<(offset_shift1_C));
int chroma1_w0 = shdr->ChromaWeight[0][refIdx0][1];
int chroma1_o0 = shdr->ChromaOffset[0][refIdx0][1] * (1<<(offset_shift1_C));
int chroma0_w1 = shdr->ChromaWeight[1][refIdx1][0];
int chroma0_o1 = shdr->ChromaOffset[1][refIdx1][0] * (1<<(offset_shift1_C));
int chroma1_w1 = shdr->ChromaWeight[1][refIdx1][1];
int chroma1_o1 = shdr->ChromaOffset[1][refIdx1][1] * (1<<(offset_shift1_C));
logtrace(LogMotion,"weighted-BI-0 [%d] %d %d %d %dx%d\n", refIdx0, luma_log2WD-6,luma_w0,luma_o0,nPbW,nPbH);
logtrace(LogMotion,"weighted-BI-1 [%d] %d %d %d %dx%d\n", refIdx1, luma_log2WD-6,luma_w1,luma_o1,nPbW,nPbH);
int16_t* in0 = predSamplesL[0];
int16_t* in1 = predSamplesL[1];
ctx->acceleration.put_weighted_bipred(pixels[0], stride[0],
in0,in1, nCS, nPbW, nPbH,
luma_w0,luma_o0,
luma_w1,luma_o1,
luma_log2WD, bit_depth_L);
int16_t* in00 = predSamplesC[0][0];
int16_t* in01 = predSamplesC[0][1];
int16_t* in10 = predSamplesC[1][0];
int16_t* in11 = predSamplesC[1][1];
ctx->acceleration.put_weighted_bipred(pixels[1], stride[1],
in00,in01, nCS, nPbW/SubWidthC, nPbH/SubHeightC,
chroma0_w0,chroma0_o0,
chroma0_w1,chroma0_o1,
chroma_log2WD, bit_depth_C);
ctx->acceleration.put_weighted_bipred(pixels[2], stride[2],
in10,in11, nCS, nPbW/SubWidthC, nPbH/SubHeightC,
chroma1_w0,chroma1_o0,
chroma1_w1,chroma1_o1,
chroma_log2WD, bit_depth_C);
}
}
else if (predFlag[0]==1 || predFlag[1]==1) {
int l = predFlag[0] ? 0 : 1;
if (pps->weighted_bipred_flag==0) {
ctx->acceleration.put_unweighted_pred(pixels[0], stride[0],
predSamplesL[l],nCS, nPbW,nPbH, bit_depth_L);
ctx->acceleration.put_unweighted_pred(pixels[1], stride[1],
predSamplesC[0][l],nCS,
nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
ctx->acceleration.put_unweighted_pred(pixels[2], stride[2],
predSamplesC[1][l],nCS,
nPbW/SubWidthC,nPbH/SubHeightC, bit_depth_C);
}
else {
int refIdx = vi->refIdx[l];
int luma_log2WD = shdr->luma_log2_weight_denom + shift1_L;
int chroma_log2WD = shdr->ChromaLog2WeightDenom + shift1_C;
int luma_w = shdr->LumaWeight[l][refIdx];
int luma_o = shdr->luma_offset[l][refIdx] * (1<<(offset_shift1_L));
int chroma0_w = shdr->ChromaWeight[l][refIdx][0];
int chroma0_o = shdr->ChromaOffset[l][refIdx][0] * (1<<(offset_shift1_C));
int chroma1_w = shdr->ChromaWeight[l][refIdx][1];
int chroma1_o = shdr->ChromaOffset[l][refIdx][1] * (1<<(offset_shift1_C));
logtrace(LogMotion,"weighted-B-L%d [%d] %d %d %d %dx%d\n", l, refIdx, luma_log2WD-6,luma_w,luma_o,nPbW,nPbH);
ctx->acceleration.put_weighted_pred(pixels[0], stride[0],
predSamplesL[l],nCS, nPbW,nPbH,
luma_w, luma_o, luma_log2WD, bit_depth_L);
ctx->acceleration.put_weighted_pred(pixels[1], stride[1],
predSamplesC[0][l],nCS,
nPbW/SubWidthC,nPbH/SubHeightC,
chroma0_w, chroma0_o, chroma_log2WD, bit_depth_C);
ctx->acceleration.put_weighted_pred(pixels[2], stride[2],
predSamplesC[1][l],nCS,
nPbW/SubWidthC,nPbH/SubHeightC,
chroma1_w, chroma1_o, chroma_log2WD, bit_depth_C);
}
}
else {
ctx->add_warning(DE265_WARNING_BOTH_PREDFLAGS_ZERO, false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
}
#if defined(DE265_LOG_TRACE) && 0
logtrace(LogTransform,"MC pixels (luma), position %d %d:\n", xP,yP);
for (int y=0;y<nPbH;y++) {
logtrace(LogTransform,"MC-y-%d-%d ",xP,yP+y);
for (int x=0;x<nPbW;x++) {
logtrace(LogTransform,"*%02x ", pixels[0][x+y*stride[0]]);
}
logtrace(LogTransform,"*\n");
}
logtrace(LogTransform,"MC pixels (chroma cb), position %d %d:\n", xP/2,yP/2);
for (int y=0;y<nPbH/2;y++) {
logtrace(LogTransform,"MC-cb-%d-%d ",xP/2,yP/2+y);
for (int x=0;x<nPbW/2;x++) {
logtrace(LogTransform,"*%02x ", pixels[1][x+y*stride[1]]);
}
logtrace(LogTransform,"*\n");
}
logtrace(LogTransform,"MC pixels (chroma cr), position %d %d:\n", xP/2,yP/2);
for (int y=0;y<nPbH/2;y++) {
logtrace(LogTransform,"MC-cr-%d-%d ",xP/2,yP/2+y);
for (int x=0;x<nPbW/2;x++) {
logtrace(LogTransform,"*%02x ", pixels[2][x+y*stride[2]]);
}
logtrace(LogTransform,"*\n");
}
#endif
}
#ifdef DE265_LOG_TRACE
void logmvcand(const PBMotion& p)
{
for (int v=0;v<2;v++) {
if (p.predFlag[v]) {
logtrace(LogMotion," %d: %s %d;%d ref=%d\n", v, p.predFlag[v] ? "yes":"no ",
p.mv[v].x,p.mv[v].y, p.refIdx[v]);
} else {
logtrace(LogMotion," %d: %s --;-- ref=--\n", v, p.predFlag[v] ? "yes":"no ");
}
}
}
#else
#define logmvcand(p)
#endif
bool PBMotion::operator==(const PBMotion& b) const
{
const PBMotion& a = *this;
for (int i=0;i<2;i++) {
if (a.predFlag[i] != b.predFlag[i]) return false;
if (a.predFlag[i]) {
if (a.mv[i].x != b.mv[i].x) return false;
if (a.mv[i].y != b.mv[i].y) return false;
if (a.refIdx[i] != b.refIdx[i]) return false;
}
}
return true;
}
template <class T> class MotionVectorAccess
{
public:
enum PartMode get_PartMode(int x,int y);
const PBMotion& get_mv_info(int x,int y);
};
template <> class MotionVectorAccess<de265_image>
{
public:
MotionVectorAccess(const de265_image* i) : img(i) { }
enum PartMode get_PartMode(int x,int y) { return img->get_PartMode(x,y); }
const PBMotion& get_mv_info(int x,int y) { return img->get_mv_info(x,y); }
private:
const de265_image* img;
};
template <> class MotionVectorAccess<encoder_context>
{
public:
MotionVectorAccess(const encoder_context* e) : ectx(e) { }
enum PartMode get_PartMode(int x,int y) { return ectx->ctbs.getCB(x,y)->PartMode; }
const PBMotion& get_mv_info(int x,int y) {
return ectx->ctbs.getPB(x,y)->motion;
}
private:
const encoder_context* ectx;
};
template <class MVAccessType>
int derive_spatial_merging_candidates(
MotionVectorAccess<MVAccessType> mvaccess,
const de265_image* img,
int xC, int yC, int nCS, int xP, int yP,
uint8_t singleMCLFlag,
int nPbW, int nPbH,
int partIdx,
PBMotion* out_cand,
int maxCandidates)
{
const pic_parameter_set* pps = &img->get_pps();
const int log2_parallel_merge_level = pps->log2_parallel_merge_level;
enum PartMode PartMode = mvaccess.get_PartMode(xC,yC);
int xA1 = xP-1;
int yA1 = yP+nPbH-1;
bool availableA1;
int idxA1;
int computed_candidates = 0;
if ((xP>>log2_parallel_merge_level) == (xA1>>log2_parallel_merge_level) &&
(yP>>log2_parallel_merge_level) == (yA1>>log2_parallel_merge_level)) {
availableA1 = false;
logtrace(LogMotion,"spatial merging candidate A1: below parallel merge level\n");
}
else if (
partIdx==1 &&
(PartMode==PART_Nx2N ||
PartMode==PART_nLx2N ||
PartMode==PART_nRx2N)) {
availableA1 = false;
logtrace(LogMotion,"spatial merging candidate A1: second part ignore\n");
}
else {
availableA1 = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xA1,yA1);
if (!availableA1) logtrace(LogMotion,"spatial merging candidate A1: unavailable\n");
}
if (availableA1) {
idxA1 = computed_candidates++;
out_cand[idxA1] = mvaccess.get_mv_info(xA1,yA1);
logtrace(LogMotion,"spatial merging candidate A1:\n");
logmvcand(out_cand[idxA1]);
}
if (computed_candidates>=maxCandidates) return computed_candidates;
int xB1 = xP+nPbW-1;
int yB1 = yP-1;
bool availableB1;
int idxB1;
if ((xP>>log2_parallel_merge_level) == (xB1>>log2_parallel_merge_level) &&
(yP>>log2_parallel_merge_level) == (yB1>>log2_parallel_merge_level)) {
availableB1 = false;
logtrace(LogMotion,"spatial merging candidate B1: below parallel merge level\n");
}
else if (
partIdx==1 &&
(PartMode==PART_2NxN ||
PartMode==PART_2NxnU ||
PartMode==PART_2NxnD)) {
availableB1 = false;
logtrace(LogMotion,"spatial merging candidate B1: second part ignore\n");
}
else {
availableB1 = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xB1,yB1);
if (!availableB1) logtrace(LogMotion,"spatial merging candidate B1: unavailable\n");
}
if (availableB1) {
const PBMotion& b1 = img->get_mv_info(xB1,yB1);
if (availableA1 && out_cand[idxA1] == b1) {
idxB1 = idxA1;
logtrace(LogMotion,"spatial merging candidate B1: redundant to A1\n");
}
else {
idxB1 = computed_candidates++;
out_cand[idxB1] = b1;
logtrace(LogMotion,"spatial merging candidate B1:\n");
logmvcand(out_cand[idxB1]);
}
}
if (computed_candidates>=maxCandidates) return computed_candidates;
int xB0 = xP+nPbW;
int yB0 = yP-1;
bool availableB0;
int idxB0;
if ((xP>>log2_parallel_merge_level) == (xB0>>log2_parallel_merge_level) &&
(yP>>log2_parallel_merge_level) == (yB0>>log2_parallel_merge_level)) {
availableB0 = false;
logtrace(LogMotion,"spatial merging candidate B0: below parallel merge level\n");
}
else {
availableB0 = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xB0,yB0);
if (!availableB0) logtrace(LogMotion,"spatial merging candidate B0: unavailable\n");
}
if (availableB0) {
const PBMotion& b0 = img->get_mv_info(xB0,yB0);
if (availableB1 && out_cand[idxB1]==b0) {
idxB0 = idxB1;
logtrace(LogMotion,"spatial merging candidate B0: redundant to B1\n");
}
else {
idxB0 = computed_candidates++;
out_cand[idxB0] = b0;
logtrace(LogMotion,"spatial merging candidate B0:\n");
logmvcand(out_cand[idxB0]);
}
}
if (computed_candidates>=maxCandidates) return computed_candidates;
int xA0 = xP-1;
int yA0 = yP+nPbH;
bool availableA0;
int idxA0;
if ((xP>>log2_parallel_merge_level) == (xA0>>log2_parallel_merge_level) &&
(yP>>log2_parallel_merge_level) == (yA0>>log2_parallel_merge_level)) {
availableA0 = false;
logtrace(LogMotion,"spatial merging candidate A0: below parallel merge level\n");
}
else {
availableA0 = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xA0,yA0);
if (!availableA0) logtrace(LogMotion,"spatial merging candidate A0: unavailable\n");
}
if (availableA0) {
const PBMotion& a0 = img->get_mv_info(xA0,yA0);
if (availableA1 && out_cand[idxA1]==a0) {
idxA0 = idxA1;
logtrace(LogMotion,"spatial merging candidate A0: redundant to A1\n");
}
else {
idxA0 = computed_candidates++;
out_cand[idxA0] = a0;
logtrace(LogMotion,"spatial merging candidate A0:\n");
logmvcand(out_cand[idxA0]);
}
}
if (computed_candidates>=maxCandidates) return computed_candidates;
int xB2 = xP-1;
int yB2 = yP-1;
bool availableB2;
int idxB2;
if (computed_candidates==4) {
availableB2 = false;
logtrace(LogMotion,"spatial merging candidate B2: ignore\n");
}
else if ((xP>>log2_parallel_merge_level) == (xB2>>log2_parallel_merge_level) &&
(yP>>log2_parallel_merge_level) == (yB2>>log2_parallel_merge_level)) {
availableB2 = false;
logtrace(LogMotion,"spatial merging candidate B2: below parallel merge level\n");
}
else {
availableB2 = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xB2,yB2);
if (!availableB2) logtrace(LogMotion,"spatial merging candidate B2: unavailable\n");
}
if (availableB2) {
const PBMotion& b2 = img->get_mv_info(xB2,yB2);
if (availableB1 && out_cand[idxB1]==b2) {
idxB2 = idxB1;
logtrace(LogMotion,"spatial merging candidate B2: redundant to B1\n");
}
else if (availableA1 && out_cand[idxA1]==b2) {
idxB2 = idxA1;
logtrace(LogMotion,"spatial merging candidate B2: redundant to A1\n");
}
else {
idxB2 = computed_candidates++;
out_cand[idxB2] = b2;
logtrace(LogMotion,"spatial merging candidate B2:\n");
logmvcand(out_cand[idxB2]);
}
}
return computed_candidates;
}
void derive_zero_motion_vector_candidates(const slice_segment_header* shdr,
PBMotion* out_mergeCandList,
int* inout_numCurrMergeCand,
int maxCandidates)
{
logtrace(LogMotion,"derive_zero_motion_vector_candidates\n");
int numRefIdx;
if (shdr->slice_type==SLICE_TYPE_P) {
numRefIdx = shdr->num_ref_idx_l0_active;
}
else {
numRefIdx = libde265_min(shdr->num_ref_idx_l0_active,
shdr->num_ref_idx_l1_active);
}
int zeroIdx = 0;
while (*inout_numCurrMergeCand < maxCandidates) {
logtrace(LogMotion,"zeroIdx:%d numRefIdx:%d\n", zeroIdx, numRefIdx);
PBMotion* newCand = &out_mergeCandList[*inout_numCurrMergeCand];
const int refIdx = (zeroIdx < numRefIdx) ? zeroIdx : 0;
if (shdr->slice_type==SLICE_TYPE_P) {
newCand->refIdx[0] = refIdx;
newCand->refIdx[1] = -1;
newCand->predFlag[0] = 1;
newCand->predFlag[1] = 0;
}
else {
newCand->refIdx[0] = refIdx;
newCand->refIdx[1] = refIdx;
newCand->predFlag[0] = 1;
newCand->predFlag[1] = 1;
}
newCand->mv[0].x = 0;
newCand->mv[0].y = 0;
newCand->mv[1].x = 0;
newCand->mv[1].y = 0;
(*inout_numCurrMergeCand)++;
zeroIdx++;
}
}
bool scale_mv(MotionVector* out_mv, MotionVector mv, int colDist, int currDist)
{
int td = Clip3(-128,127, colDist);
int tb = Clip3(-128,127, currDist);
if (td==0) {
*out_mv = mv;
return false;
}
else {
int tx = (16384 + (abs_value(td)>>1)) / td;
int distScaleFactor = Clip3(-4096,4095, (tb*tx+32)>>6);
out_mv->x = Clip3(-32768,32767,
Sign(distScaleFactor*mv.x)*((abs_value(distScaleFactor*mv.x)+127)>>8));
out_mv->y = Clip3(-32768,32767,
Sign(distScaleFactor*mv.y)*((abs_value(distScaleFactor*mv.y)+127)>>8));
return true;
}
}
void derive_collocated_motion_vectors(base_context* ctx,
de265_image* img,
const slice_segment_header* shdr,
int xP,int yP,
int colPic,
int xColPb,int yColPb,
int refIdxLX,
int X,
MotionVector* out_mvLXCol,
uint8_t* out_availableFlagLXCol)
{
logtrace(LogMotion,"derive_collocated_motion_vectors %d;%d\n",xP,yP);
assert(ctx->has_image(colPic));
const de265_image* colImg = ctx->get_image(colPic);
if (xColPb >= colImg->get_width() ||
yColPb >= colImg->get_height()) {
ctx->add_warning(DE265_WARNING_COLLOCATED_MOTION_VECTOR_OUTSIDE_IMAGE_AREA, false);
*out_availableFlagLXCol = 0;
return;
}
enum PredMode predMode = colImg->get_pred_mode(xColPb,yColPb);
if (predMode == MODE_INTRA) {
out_mvLXCol->x = 0;
out_mvLXCol->y = 0;
*out_availableFlagLXCol = 0;
return;
}
logtrace(LogMotion,"colPic:%d (POC=%d) X:%d refIdxLX:%d refpiclist:%d\n",
colPic,
colImg->PicOrderCntVal,
X,refIdxLX,shdr->RefPicList[X][refIdxLX]);
if (colImg->integrity == INTEGRITY_UNAVAILABLE_REFERENCE) {
out_mvLXCol->x = 0;
out_mvLXCol->y = 0;
*out_availableFlagLXCol = 0;
return;
}
const PBMotion& mvi = colImg->get_mv_info(xColPb,yColPb);
int listCol;
int refIdxCol;
MotionVector mvCol;
logtrace(LogMotion,"read MVI %d;%d:\n",xColPb,yColPb);
logmvcand(mvi);
if (mvi.predFlag[0]==0) {
mvCol = mvi.mv[1];
refIdxCol = mvi.refIdx[1];
listCol = 1;
}
else if (mvi.predFlag[1]==0) {
mvCol = mvi.mv[0];
refIdxCol = mvi.refIdx[0];
listCol = 0;
}
else {
bool allRefFramesBeforeCurrentFrame = true;
const int currentPOC = img->PicOrderCntVal;
for (int rIdx=0; rIdx<shdr->num_ref_idx_l1_active && allRefFramesBeforeCurrentFrame; rIdx++)
{
const de265_image* refimg = ctx->get_image(shdr->RefPicList[1][rIdx]);
int refPOC = refimg->PicOrderCntVal;
if (refPOC > currentPOC) {
allRefFramesBeforeCurrentFrame = false;
}
}
for (int rIdx=0; rIdx<shdr->num_ref_idx_l0_active && allRefFramesBeforeCurrentFrame; rIdx++)
{
const de265_image* refimg = ctx->get_image(shdr->RefPicList[0][rIdx]);
int refPOC = refimg->PicOrderCntVal;
if (refPOC > currentPOC) {
allRefFramesBeforeCurrentFrame = false;
}
}
if (allRefFramesBeforeCurrentFrame) {
mvCol = mvi.mv[X];
refIdxCol = mvi.refIdx[X];
listCol = X;
}
else {
int N = shdr->collocated_from_l0_flag;
mvCol = mvi.mv[N];
refIdxCol = mvi.refIdx[N];
listCol = N;
}
}
const slice_segment_header* colShdr = colImg->slices[ colImg->get_SliceHeaderIndex(xColPb,yColPb) ];
if (shdr->LongTermRefPic[X][refIdxLX] !=
colShdr->LongTermRefPic[listCol][refIdxCol]) {
*out_availableFlagLXCol = 0;
out_mvLXCol->x = 0;
out_mvLXCol->y = 0;
}
else {
*out_availableFlagLXCol = 1;
const bool isLongTerm = shdr->LongTermRefPic[X][refIdxLX];
int colDist = colImg->PicOrderCntVal - colShdr->RefPicList_POC[listCol][refIdxCol];
int currDist = img->PicOrderCntVal - shdr->RefPicList_POC[X][refIdxLX];
logtrace(LogMotion,"COLPOCDIFF %d %d [%d %d / %d %d]\n",colDist, currDist,
colImg->PicOrderCntVal, colShdr->RefPicList_POC[listCol][refIdxCol],
img->PicOrderCntVal, shdr->RefPicList_POC[X][refIdxLX]
);
if (isLongTerm || colDist == currDist) {
*out_mvLXCol = mvCol;
}
else {
if (!scale_mv(out_mvLXCol, mvCol, colDist, currDist)) {
ctx->add_warning(DE265_WARNING_INCORRECT_MOTION_VECTOR_SCALING, false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
logtrace(LogMotion,"scale: %d;%d to %d;%d\n",
mvCol.x,mvCol.y, out_mvLXCol->x,out_mvLXCol->y);
}
}
}
void derive_temporal_luma_vector_prediction(base_context* ctx,
de265_image* img,
const slice_segment_header* shdr,
int xP,int yP,
int nPbW,int nPbH,
int refIdxL,
int X,
MotionVector* out_mvLXCol,
uint8_t* out_availableFlagLXCol)
{
if (shdr->slice_temporal_mvp_enabled_flag == 0) {
out_mvLXCol->x = 0;
out_mvLXCol->y = 0;
*out_availableFlagLXCol = 0;
return;
}
int Log2CtbSizeY = img->get_sps().Log2CtbSizeY;
int colPic;
if (shdr->slice_type == SLICE_TYPE_B &&
shdr->collocated_from_l0_flag == 0)
{
logtrace(LogMotion,"collocated L1 ref_idx=%d\n",shdr->collocated_ref_idx);
colPic = shdr->RefPicList[1][ shdr->collocated_ref_idx ];
}
else
{
logtrace(LogMotion,"collocated L0 ref_idx=%d\n",shdr->collocated_ref_idx);
colPic = shdr->RefPicList[0][ shdr->collocated_ref_idx ];
}
if (!ctx->has_image(colPic)) {
out_mvLXCol->x = 0;
out_mvLXCol->y = 0;
*out_availableFlagLXCol = 0;
ctx->add_warning(DE265_WARNING_NONEXISTING_REFERENCE_PICTURE_ACCESSED, false);
return;
}
int xColPb,yColPb;
int yColBr = yP + nPbH;
int xColBr = xP + nPbW;
if ((yP>>Log2CtbSizeY) == (yColBr>>Log2CtbSizeY) &&
xColBr < img->get_sps().pic_width_in_luma_samples &&
yColBr < img->get_sps().pic_height_in_luma_samples)
{
xColPb = xColBr & ~0x0F;
yColPb = yColBr & ~0x0F;
derive_collocated_motion_vectors(ctx,img,shdr, xP,yP, colPic, xColPb,yColPb, refIdxL, X,
out_mvLXCol, out_availableFlagLXCol);
}
else
{
out_mvLXCol->x = 0;
out_mvLXCol->y = 0;
*out_availableFlagLXCol = 0;
}
if (*out_availableFlagLXCol==0) {
int xColCtr = xP+(nPbW>>1);
int yColCtr = yP+(nPbH>>1);
xColPb = xColCtr & ~0x0F;
yColPb = yColCtr & ~0x0F;
derive_collocated_motion_vectors(ctx,img,shdr, xP,yP, colPic, xColPb,yColPb, refIdxL, X,
out_mvLXCol, out_availableFlagLXCol);
}
}
static int table_8_19[2][12] = {
{ 0,1,0,2,1,2,0,3,1,3,2,3 },
{ 1,0,2,0,2,1,3,0,3,1,3,2 }
};
void derive_combined_bipredictive_merging_candidates(const base_context* ctx,
const slice_segment_header* shdr,
PBMotion* inout_mergeCandList,
int* inout_numMergeCand,
int maxCandidates)
{
if (*inout_numMergeCand>1 && *inout_numMergeCand < maxCandidates) {
int numOrigMergeCand = *inout_numMergeCand;
int numInputMergeCand = *inout_numMergeCand;
int combIdx = 0;
uint8_t combStop = false;
while (!combStop) {
int l0CandIdx = table_8_19[0][combIdx];
int l1CandIdx = table_8_19[1][combIdx];
if (l0CandIdx >= numInputMergeCand ||
l1CandIdx >= numInputMergeCand) {
assert(false);
}
PBMotion& l0Cand = inout_mergeCandList[l0CandIdx];
PBMotion& l1Cand = inout_mergeCandList[l1CandIdx];
logtrace(LogMotion,"add bipredictive merging candidate (combIdx:%d)\n",combIdx);
logtrace(LogMotion,"l0Cand:\n"); logmvcand(l0Cand);
logtrace(LogMotion,"l1Cand:\n"); logmvcand(l1Cand);
const de265_image* img0 = l0Cand.predFlag[0] ? ctx->get_image(shdr->RefPicList[0][l0Cand.refIdx[0]]) : NULL;
const de265_image* img1 = l1Cand.predFlag[1] ? ctx->get_image(shdr->RefPicList[1][l1Cand.refIdx[1]]) : NULL;
if (l0Cand.predFlag[0] && !img0) {
return;
}
if (l1Cand.predFlag[1] && !img1) {
return;
}
if (l0Cand.predFlag[0] && l1Cand.predFlag[1] &&
(img0->PicOrderCntVal != img1->PicOrderCntVal ||
l0Cand.mv[0].x != l1Cand.mv[1].x ||
l0Cand.mv[0].y != l1Cand.mv[1].y)) {
PBMotion& p = inout_mergeCandList[ *inout_numMergeCand ];
p.refIdx[0] = l0Cand.refIdx[0];
p.refIdx[1] = l1Cand.refIdx[1];
p.predFlag[0] = l0Cand.predFlag[0];
p.predFlag[1] = l1Cand.predFlag[1];
p.mv[0] = l0Cand.mv[0];
p.mv[1] = l1Cand.mv[1];
(*inout_numMergeCand)++;
logtrace(LogMotion,"result:\n");
logmvcand(p);
}
combIdx++;
if (combIdx == numOrigMergeCand*(numOrigMergeCand-1) ||
*inout_numMergeCand == maxCandidates) {
combStop = true;
}
}
}
}
template <class MVAccess>
void get_merge_candidate_list_without_step_9(base_context* ctx,
const slice_segment_header* shdr,
MotionVectorAccess<MVAccess> mvaccess,
de265_image* img,
int xC,int yC, int xP,int yP,
int nCS, int nPbW,int nPbH, int partIdx,
int max_merge_idx,
PBMotion* mergeCandList)
{
int nOrigPbW = nPbW;
int nOrigPbH = nPbH;
int singleMCLFlag;
singleMCLFlag = (img->get_pps().log2_parallel_merge_level > 2 && nCS==8);
if (singleMCLFlag) {
xP=xC;
yP=yC;
nPbW=nCS;
nPbH=nCS;
partIdx=0;
}
int maxCandidates = max_merge_idx+1;
int numMergeCand=0;
numMergeCand = derive_spatial_merging_candidates(mvaccess,
img, xC,yC, nCS, xP,yP, singleMCLFlag,
nPbW,nPbH,partIdx, mergeCandList,
maxCandidates);
if (numMergeCand < maxCandidates) {
int refIdxCol[2] = { 0,0 };
MotionVector mvCol[2];
uint8_t predFlagLCol[2];
derive_temporal_luma_vector_prediction(ctx,img,shdr, xP,yP,nPbW,nPbH,
refIdxCol[0],0, &mvCol[0],
&predFlagLCol[0]);
uint8_t availableFlagCol = predFlagLCol[0];
predFlagLCol[1] = 0;
if (shdr->slice_type == SLICE_TYPE_B) {
derive_temporal_luma_vector_prediction(ctx,img,shdr,
xP,yP,nPbW,nPbH, refIdxCol[1],1, &mvCol[1],
&predFlagLCol[1]);
availableFlagCol |= predFlagLCol[1];
}
if (availableFlagCol) {
PBMotion* colVec = &mergeCandList[numMergeCand++];
colVec->mv[0] = mvCol[0];
colVec->mv[1] = mvCol[1];
colVec->predFlag[0] = predFlagLCol[0];
colVec->predFlag[1] = predFlagLCol[1];
colVec->refIdx[0] = refIdxCol[0];
colVec->refIdx[1] = refIdxCol[1];
}
}
if (shdr->slice_type == SLICE_TYPE_B) {
derive_combined_bipredictive_merging_candidates(ctx, shdr,
mergeCandList, &numMergeCand, maxCandidates);
}
derive_zero_motion_vector_candidates(shdr, mergeCandList, &numMergeCand, maxCandidates);
logtrace(LogMotion,"mergeCandList:\n");
for (int i=0;i<shdr->MaxNumMergeCand;i++)
{
logmvcand(mergeCandList[i]);
}
}
void get_merge_candidate_list(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
int xC,int yC, int xP,int yP,
int nCS, int nPbW,int nPbH, int partIdx,
PBMotion* mergeCandList)
{
int max_merge_idx = 5-shdr->five_minus_max_num_merge_cand -1;
get_merge_candidate_list_without_step_9(ctx, shdr,
MotionVectorAccess<de265_image>(img), img,
xC,yC,xP,yP,nCS,nPbW,nPbH, partIdx,
max_merge_idx, mergeCandList);
for (int i=0;i<=max_merge_idx;i++) {
if (mergeCandList[i].predFlag[0] &&
mergeCandList[i].predFlag[1] &&
nPbW+nPbH==12)
{
mergeCandList[i].refIdx[1] = -1;
mergeCandList[i].predFlag[1] = 0;
}
}
}
void get_merge_candidate_list_from_tree(encoder_context* ectx,
const slice_segment_header* shdr,
int xC,int yC, int xP,int yP,
int nCS, int nPbW,int nPbH, int partIdx,
PBMotion* mergeCandList)
{
int max_merge_idx = 5-shdr->five_minus_max_num_merge_cand -1;
get_merge_candidate_list_without_step_9(ectx, shdr,
MotionVectorAccess<encoder_context>(ectx), ectx->img,
xC,yC,xP,yP,nCS,nPbW,nPbH, partIdx,
max_merge_idx, mergeCandList);
for (int i=0;i<=max_merge_idx;i++) {
if (mergeCandList[i].predFlag[0] &&
mergeCandList[i].predFlag[1] &&
nPbW+nPbH==12)
{
mergeCandList[i].refIdx[1] = -1;
mergeCandList[i].predFlag[1] = 0;
}
}
}
void derive_luma_motion_merge_mode(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
int xC,int yC, int xP,int yP,
int nCS, int nPbW,int nPbH, int partIdx,
int merge_idx,
PBMotion* out_vi)
{
PBMotion mergeCandList[5];
get_merge_candidate_list_without_step_9(ctx, shdr,
MotionVectorAccess<de265_image>(img), img,
xC,yC,xP,yP,nCS,nPbW,nPbH, partIdx,
merge_idx, mergeCandList);
*out_vi = mergeCandList[merge_idx];
if (out_vi->predFlag[0] && out_vi->predFlag[1] && nPbW+nPbH==12) {
out_vi->refIdx[1] = -1;
out_vi->predFlag[1] = 0;
}
}
void derive_spatial_luma_vector_prediction(base_context* ctx,
de265_image* img,
const slice_segment_header* shdr,
int xC,int yC,int nCS,int xP,int yP,
int nPbW,int nPbH, int X,
int refIdxLX, int partIdx,
uint8_t out_availableFlagLXN[2],
MotionVector out_mvLXN[2])
{
int isScaledFlagLX = 0;
const int A=0;
const int B=1;
out_availableFlagLXN[A] = 0;
out_availableFlagLXN[B] = 0;
int xA[2], yA[2];
xA[0] = xP-1;
yA[0] = yP + nPbH;
xA[1] = xA[0];
yA[1] = yA[0]-1;
out_availableFlagLXN[A] = 0;
out_mvLXN[A].x = 0;
out_mvLXN[A].y = 0;
bool availableA[2];
availableA[0] = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xA[0],yA[0]);
availableA[1] = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xA[1],yA[1]);
if (availableA[0] || availableA[1]) {
isScaledFlagLX = 1;
}
int refIdxA=-1;
const de265_image* tmpimg = ctx->get_image(shdr->RefPicList[X][ refIdxLX ]);
if (tmpimg==NULL) { return; }
const int referenced_POC = tmpimg->PicOrderCntVal;
for (int k=0;k<=1;k++) {
if (availableA[k] &&
out_availableFlagLXN[A]==0 &&
img->get_pred_mode(xA[k],yA[k]) != MODE_INTRA) {
int Y=1-X;
const PBMotion& vi = img->get_mv_info(xA[k],yA[k]);
logtrace(LogMotion,"MVP A%d=\n",k);
logmvcand(vi);
const de265_image* imgX = NULL;
if (vi.predFlag[X]) imgX = ctx->get_image(shdr->RefPicList[X][ vi.refIdx[X] ]);
const de265_image* imgY = NULL;
if (vi.predFlag[Y]) imgY = ctx->get_image(shdr->RefPicList[Y][ vi.refIdx[Y] ]);
if (vi.predFlag[X] && imgX && imgX->PicOrderCntVal == referenced_POC) {
logtrace(LogMotion,"take A%d/L%d as A candidate with same POC\n",k,X);
out_availableFlagLXN[A]=1;
out_mvLXN[A] = vi.mv[X];
refIdxA = vi.refIdx[X];
}
else if (vi.predFlag[Y] && imgY && imgY->PicOrderCntVal == referenced_POC) {
logtrace(LogMotion,"take A%d/L%d as A candidate with same POC\n",k,Y);
out_availableFlagLXN[A]=1;
out_mvLXN[A] = vi.mv[Y];
refIdxA = vi.refIdx[Y];
}
}
}
for (int k=0 ; k<=1 && out_availableFlagLXN[A]==0 ; k++) {
int refPicList=-1;
if (availableA[k] &&
img->get_pred_mode(xA[k],yA[k]) != MODE_INTRA) {
int Y=1-X;
const PBMotion& vi = img->get_mv_info(xA[k],yA[k]);
if (vi.predFlag[X]==1 &&
shdr->LongTermRefPic[X][refIdxLX] == shdr->LongTermRefPic[X][ vi.refIdx[X] ]) {
logtrace(LogMotion,"take A%D/L%d as A candidate with different POCs\n",k,X);
out_availableFlagLXN[A]=1;
out_mvLXN[A] = vi.mv[X];
refIdxA = vi.refIdx[X];
refPicList = X;
}
else if (vi.predFlag[Y]==1 &&
shdr->LongTermRefPic[X][refIdxLX] == shdr->LongTermRefPic[Y][ vi.refIdx[Y] ]) {
logtrace(LogMotion,"take A%d/L%d as A candidate with different POCs\n",k,Y);
out_availableFlagLXN[A]=1;
out_mvLXN[A] = vi.mv[Y];
refIdxA = vi.refIdx[Y];
refPicList = Y;
}
}
if (out_availableFlagLXN[A]==1) {
if (refIdxA<0) {
out_availableFlagLXN[0] = out_availableFlagLXN[1] = false;
return;
}
assert(refIdxA>=0);
assert(refPicList>=0);
const de265_image* refPicA = ctx->get_image(shdr->RefPicList[refPicList][refIdxA ]);
const de265_image* refPicX = ctx->get_image(shdr->RefPicList[X ][refIdxLX]);
int isLongTermA = shdr->LongTermRefPic[refPicList][refIdxA ];
int isLongTermX = shdr->LongTermRefPic[X ][refIdxLX];
logtrace(LogMotion,"scale MVP A: A-POC:%d X-POC:%d\n",
refPicA->PicOrderCntVal,refPicX->PicOrderCntVal);
if (!isLongTermA && !isLongTermX)
{
int distA = img->PicOrderCntVal - refPicA->PicOrderCntVal;
int distX = img->PicOrderCntVal - referenced_POC;
if (!scale_mv(&out_mvLXN[A], out_mvLXN[A], distA, distX)) {
ctx->add_warning(DE265_WARNING_INCORRECT_MOTION_VECTOR_SCALING, false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
}
}
}
int xB[3], yB[3];
xB[0] = xP+nPbW;
yB[0] = yP-1;
xB[1] = xB[0]-1;
yB[1] = yP-1;
xB[2] = xP-1;
yB[2] = yP-1;
out_availableFlagLXN[B] = 0;
out_mvLXN[B].x = 0;
out_mvLXN[B].y = 0;
int refIdxB=-1;
bool availableB[3];
for (int k=0;k<3;k++) {
availableB[k] = img->available_pred_blk(xC,yC, nCS, xP,yP, nPbW,nPbH,partIdx, xB[k],yB[k]);
if (availableB[k] && out_availableFlagLXN[B]==0) {
int Y=1-X;
const PBMotion& vi = img->get_mv_info(xB[k],yB[k]);
logtrace(LogMotion,"MVP B%d=\n",k);
logmvcand(vi);
const de265_image* imgX = NULL;
if (vi.predFlag[X]) imgX = ctx->get_image(shdr->RefPicList[X][ vi.refIdx[X] ]);
const de265_image* imgY = NULL;
if (vi.predFlag[Y]) imgY = ctx->get_image(shdr->RefPicList[Y][ vi.refIdx[Y] ]);
if (vi.predFlag[X] && imgX && imgX->PicOrderCntVal == referenced_POC) {
logtrace(LogMotion,"a) take B%d/L%d as B candidate with same POC\n",k,X);
out_availableFlagLXN[B]=1;
out_mvLXN[B] = vi.mv[X];
refIdxB = vi.refIdx[X];
}
else if (vi.predFlag[Y] && imgY && imgY->PicOrderCntVal == referenced_POC) {
logtrace(LogMotion,"b) take B%d/L%d as B candidate with same POC\n",k,Y);
out_availableFlagLXN[B]=1;
out_mvLXN[B] = vi.mv[Y];
refIdxB = vi.refIdx[Y];
}
}
}
if (isScaledFlagLX==0 &&
out_availableFlagLXN[B])
{
logtrace(LogMotion,"copy the same-POC B candidate as additional A candidate\n");
out_availableFlagLXN[A]=1;
out_mvLXN[A] = out_mvLXN[B];
refIdxA = refIdxB;
}
if (isScaledFlagLX==0) {
out_availableFlagLXN[B]=0;
for (int k=0 ; k<=2 && out_availableFlagLXN[B]==0 ; k++) {
int refPicList=-1;
if (availableB[k]) {
int Y=1-X;
const PBMotion& vi = img->get_mv_info(xB[k],yB[k]);
if (vi.predFlag[X]==1 &&
shdr->LongTermRefPic[X][refIdxLX] == shdr->LongTermRefPic[X][ vi.refIdx[X] ]) {
out_availableFlagLXN[B]=1;
out_mvLXN[B] = vi.mv[X];
refIdxB = vi.refIdx[X];
refPicList = X;
}
else if (vi.predFlag[Y]==1 &&
shdr->LongTermRefPic[X][refIdxLX] == shdr->LongTermRefPic[Y][ vi.refIdx[Y] ]) {
out_availableFlagLXN[B]=1;
out_mvLXN[B] = vi.mv[Y];
refIdxB = vi.refIdx[Y];
refPicList = Y;
}
}
if (out_availableFlagLXN[B]==1) {
if (refIdxB<0) {
out_availableFlagLXN[0] = out_availableFlagLXN[1] = false;
return;
}
assert(refPicList>=0);
assert(refIdxB>=0);
const de265_image* refPicB=ctx->get_image(shdr->RefPicList[refPicList][refIdxB ]);
const de265_image* refPicX=ctx->get_image(shdr->RefPicList[X ][refIdxLX]);
int isLongTermB = shdr->LongTermRefPic[refPicList][refIdxB ];
int isLongTermX = shdr->LongTermRefPic[X ][refIdxLX];
if (refPicB==NULL || refPicX==NULL) {
img->decctx->add_warning(DE265_WARNING_NONEXISTING_REFERENCE_PICTURE_ACCESSED,false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
else if (refPicB->PicOrderCntVal != refPicX->PicOrderCntVal &&
!isLongTermB && !isLongTermX) {
int distB = img->PicOrderCntVal - refPicB->PicOrderCntVal;
int distX = img->PicOrderCntVal - referenced_POC;
logtrace(LogMotion,"scale MVP B: B-POC:%d X-POC:%d\n",refPicB->PicOrderCntVal,refPicX->PicOrderCntVal);
if (!scale_mv(&out_mvLXN[B], out_mvLXN[B], distB, distX)) {
ctx->add_warning(DE265_WARNING_INCORRECT_MOTION_VECTOR_SCALING, false);
img->integrity = INTEGRITY_DECODING_ERRORS;
}
}
}
}
}
}
void fill_luma_motion_vector_predictors(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
int xC,int yC,int nCS,int xP,int yP,
int nPbW,int nPbH, int l,
int refIdx, int partIdx,
MotionVector out_mvpList[2])
{
uint8_t availableFlagLXN[2];
MotionVector mvLXN[2];
derive_spatial_luma_vector_prediction(ctx, img, shdr, xC,yC, nCS, xP,yP,
nPbW,nPbH, l, refIdx, partIdx,
availableFlagLXN, mvLXN);
uint8_t availableFlagLXCol;
MotionVector mvLXCol;
if (availableFlagLXN[0] &&
availableFlagLXN[1] &&
(mvLXN[0].x != mvLXN[1].x || mvLXN[0].y != mvLXN[1].y)) {
availableFlagLXCol = 0;
}
else {
derive_temporal_luma_vector_prediction(ctx, img, shdr,
xP,yP, nPbW,nPbH, refIdx,l,
&mvLXCol, &availableFlagLXCol);
}
int numMVPCandLX=0;
if (availableFlagLXN[0])
{
out_mvpList[numMVPCandLX++] = mvLXN[0];
}
if (availableFlagLXN[1] &&
(!availableFlagLXN[0] ||
(mvLXN[0].x != mvLXN[1].x || mvLXN[0].y != mvLXN[1].y)))
{
out_mvpList[numMVPCandLX++] = mvLXN[1];
}
if (availableFlagLXCol)
{
out_mvpList[numMVPCandLX++] = mvLXCol;
}
while (numMVPCandLX<2) {
out_mvpList[numMVPCandLX].x = 0;
out_mvpList[numMVPCandLX].y = 0;
numMVPCandLX++;
}
assert(numMVPCandLX==2);
}
MotionVector luma_motion_vector_prediction(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
const PBMotionCoding& motion,
int xC,int yC,int nCS,int xP,int yP,
int nPbW,int nPbH, int l,
int refIdx, int partIdx)
{
MotionVector mvpList[2];
fill_luma_motion_vector_predictors(ctx, shdr, img,
xC,yC,nCS,xP,yP,
nPbW, nPbH, l, refIdx, partIdx,
mvpList);
return mvpList[ l ? motion.mvp_l1_flag : motion.mvp_l0_flag ];
}
#if DE265_LOG_TRACE
void logMV(int x0,int y0,int nPbW,int nPbH, const char* mode,const PBMotion* mv)
{
int pred0 = mv->predFlag[0];
int pred1 = mv->predFlag[1];
logtrace(LogMotion,
"*MV %d;%d [%d;%d] %s: (%d) %d;%d @%d (%d) %d;%d @%d\n", x0,y0,nPbW,nPbH,mode,
pred0,
pred0 ? mv->mv[0].x : 0,pred0 ? mv->mv[0].y : 0, pred0 ? mv->refIdx[0] : 0,
pred1,
pred1 ? mv->mv[1].x : 0,pred1 ? mv->mv[1].y : 0, pred1 ? mv->refIdx[1] : 0);
}
#else
#define logMV(x0,y0,nPbW,nPbH,mode,mv)
#endif
void motion_vectors_and_ref_indices(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
const PBMotionCoding& motion,
int xC,int yC, int xB,int yB, int nCS, int nPbW,int nPbH,
int partIdx,
PBMotion* out_vi)
{
int xP = xC+xB;
int yP = yC+yB;
enum PredMode predMode = img->get_pred_mode(xC,yC);
if (predMode == MODE_SKIP ||
(predMode == MODE_INTER && motion.merge_flag))
{
derive_luma_motion_merge_mode(ctx,shdr,img,
xC,yC, xP,yP, nCS,nPbW,nPbH, partIdx,
motion.merge_idx, out_vi);
logMV(xP,yP,nPbW,nPbH, "merge_mode", out_vi);
}
else {
int mvdL[2][2];
MotionVector mvpL[2];
for (int l=0;l<2;l++) {
enum InterPredIdc inter_pred_idc = (enum InterPredIdc)motion.inter_pred_idc;
if (inter_pred_idc == PRED_BI ||
(inter_pred_idc == PRED_L0 && l==0) ||
(inter_pred_idc == PRED_L1 && l==1)) {
out_vi->refIdx[l] = motion.refIdx[l];
out_vi->predFlag[l] = 1;
}
else {
out_vi->refIdx[l] = -1;
out_vi->predFlag[l] = 0;
}
mvdL[l][0] = motion.mvd[l][0];
mvdL[l][1] = motion.mvd[l][1];
if (out_vi->predFlag[l]) {
mvpL[l] = luma_motion_vector_prediction(ctx,shdr,img,motion,
xC,yC,nCS,xP,yP, nPbW,nPbH, l,
out_vi->refIdx[l], partIdx);
int32_t x = (mvpL[l].x + mvdL[l][0] + 0x10000) & 0xFFFF;
int32_t y = (mvpL[l].y + mvdL[l][1] + 0x10000) & 0xFFFF;
out_vi->mv[l].x = (x>=0x8000) ? x-0x10000 : x;
out_vi->mv[l].y = (y>=0x8000) ? y-0x10000 : y;
}
}
logMV(xP,yP,nPbW,nPbH, "mvp", out_vi);
}
}
void decode_prediction_unit(base_context* ctx,
const slice_segment_header* shdr,
de265_image* img,
const PBMotionCoding& motion,
int xC,int yC, int xB,int yB, int nCS, int nPbW,int nPbH, int partIdx)
{
logtrace(LogMotion,"decode_prediction_unit POC=%d %d;%d %dx%d\n",
img->PicOrderCntVal, xC+xB,yC+yB, nPbW,nPbH);
PBMotion vi;
motion_vectors_and_ref_indices(ctx, shdr, img, motion,
xC,yC, xB,yB, nCS, nPbW,nPbH, partIdx, &vi);
generate_inter_prediction_samples(ctx,shdr, img, xC,yC, xB,yB, nCS, nPbW,nPbH, &vi);
img->set_mv_info(xC+xB,yC+yB,nPbW,nPbH, vi);
}