mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-27 06:07:14 +01:00
768 lines
28 KiB
C
768 lines
28 KiB
C
/********************************************************************
|
|
* *
|
|
* THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
|
|
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
|
|
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
|
|
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
|
|
* *
|
|
* THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 *
|
|
* by the Xiph.Org Foundation http://www.xiph.org/ *
|
|
* *
|
|
********************************************************************
|
|
|
|
function:
|
|
last mod: $Id$
|
|
|
|
********************************************************************/
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include "encint.h"
|
|
|
|
|
|
|
|
typedef struct oc_mcenc_ctx oc_mcenc_ctx;
|
|
|
|
|
|
|
|
/*Temporary state used for motion estimation.*/
|
|
struct oc_mcenc_ctx{
|
|
/*The candidate motion vectors.*/
|
|
int candidates[13][2];
|
|
/*The start of the Set B candidates.*/
|
|
int setb0;
|
|
/*The total number of candidates.*/
|
|
int ncandidates;
|
|
};
|
|
|
|
|
|
|
|
/*The maximum Y plane SAD value for accepting the median predictor.*/
|
|
#define OC_YSAD_THRESH1 (256)
|
|
/*The amount to right shift the minimum error by when inflating it for
|
|
computing the second maximum Y plane SAD threshold.*/
|
|
#define OC_YSAD_THRESH2_SCALE_BITS (4)
|
|
/*The amount to add to the second maximum Y plane threshold when inflating
|
|
it.*/
|
|
#define OC_YSAD_THRESH2_OFFSET (64)
|
|
|
|
/*The vector offsets in the X direction for each search site in the square
|
|
pattern.*/
|
|
static const int OC_SQUARE_DX[9]={-1,0,1,-1,0,1,-1,0,1};
|
|
/*The vector offsets in the Y direction for each search site in the square
|
|
pattern.*/
|
|
static const int OC_SQUARE_DY[9]={-1,-1,-1,0,0,0,1,1,1};
|
|
/*The number of sites to search for each boundary condition in the square
|
|
pattern.
|
|
Bit flags for the boundary conditions are as follows:
|
|
1: -16==dx
|
|
2: dx==15(.5)
|
|
4: -16==dy
|
|
8: dy==15(.5)*/
|
|
static const int OC_SQUARE_NSITES[11]={8,5,5,0,5,3,3,0,5,3,3};
|
|
/*The list of sites to search for each boundary condition in the square
|
|
pattern.*/
|
|
static const int OC_SQUARE_SITES[11][8]={
|
|
/* -15.5<dx<31, -15.5<dy<15(.5)*/
|
|
{0,1,2,3,5,6,7,8},
|
|
/*-15.5==dx, -15.5<dy<15(.5)*/
|
|
{1,2,5,7,8},
|
|
/* dx==15(.5), -15.5<dy<15(.5)*/
|
|
{0,1,3,6,7},
|
|
/*-15.5==dx==15(.5), -15.5<dy<15(.5)*/
|
|
{-1},
|
|
/* -15.5<dx<15(.5), -15.5==dy*/
|
|
{3,5,6,7,8},
|
|
/*-15.5==dx, -15.5==dy*/
|
|
{5,7,8},
|
|
/* dx==15(.5), -15.5==dy*/
|
|
{3,6,7},
|
|
/*-15.5==dx==15(.5), -15.5==dy*/
|
|
{-1},
|
|
/*-15.5dx<15(.5), dy==15(.5)*/
|
|
{0,1,2,3,5},
|
|
/*-15.5==dx, dy==15(.5)*/
|
|
{1,2,5},
|
|
/* dx==15(.5), dy==15(.5)*/
|
|
{0,1,3}
|
|
};
|
|
|
|
|
|
static void oc_mcenc_find_candidates(oc_enc_ctx *_enc,oc_mcenc_ctx *_mcenc,
|
|
int _accum[2],int _mbi,int _frame){
|
|
oc_mb_enc_info *embs;
|
|
int a[3][2];
|
|
int ncandidates;
|
|
unsigned nmbi;
|
|
int i;
|
|
embs=_enc->mb_info;
|
|
/*Skip a position to store the median predictor in.*/
|
|
ncandidates=1;
|
|
if(embs[_mbi].ncneighbors>0){
|
|
/*Fill in the first part of set A: the vectors from adjacent blocks.*/
|
|
for(i=0;i<embs[_mbi].ncneighbors;i++){
|
|
nmbi=embs[_mbi].cneighbors[i];
|
|
_mcenc->candidates[ncandidates][0]=embs[nmbi].analysis_mv[0][_frame][0];
|
|
_mcenc->candidates[ncandidates][1]=embs[nmbi].analysis_mv[0][_frame][1];
|
|
ncandidates++;
|
|
}
|
|
}
|
|
/*Add a few additional vectors to set A: the vectors used in the previous
|
|
frames and the (0,0) vector.*/
|
|
_mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,_accum[0],31);
|
|
_mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,_accum[1],31);
|
|
ncandidates++;
|
|
_mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,
|
|
embs[_mbi].analysis_mv[1][_frame][0]+_accum[0],31);
|
|
_mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,
|
|
embs[_mbi].analysis_mv[1][_frame][1]+_accum[1],31);
|
|
ncandidates++;
|
|
_mcenc->candidates[ncandidates][0]=0;
|
|
_mcenc->candidates[ncandidates][1]=0;
|
|
ncandidates++;
|
|
/*Use the first three vectors of set A to find our best predictor: their
|
|
median.*/
|
|
memcpy(a,_mcenc->candidates+1,sizeof(a));
|
|
OC_SORT2I(a[0][0],a[1][0]);
|
|
OC_SORT2I(a[0][1],a[1][1]);
|
|
OC_SORT2I(a[1][0],a[2][0]);
|
|
OC_SORT2I(a[1][1],a[2][1]);
|
|
OC_SORT2I(a[0][0],a[1][0]);
|
|
OC_SORT2I(a[0][1],a[1][1]);
|
|
_mcenc->candidates[0][0]=a[1][0];
|
|
_mcenc->candidates[0][1]=a[1][1];
|
|
/*Fill in set B: accelerated predictors for this and adjacent macro blocks.*/
|
|
_mcenc->setb0=ncandidates;
|
|
/*The first time through the loop use the current macro block.*/
|
|
nmbi=_mbi;
|
|
for(i=0;;i++){
|
|
_mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,
|
|
2*embs[_mbi].analysis_mv[1][_frame][0]
|
|
-embs[_mbi].analysis_mv[2][_frame][0]+_accum[0],31);
|
|
_mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,
|
|
2*embs[_mbi].analysis_mv[1][_frame][1]
|
|
-embs[_mbi].analysis_mv[2][_frame][1]+_accum[1],31);
|
|
ncandidates++;
|
|
if(i>=embs[_mbi].npneighbors)break;
|
|
nmbi=embs[_mbi].pneighbors[i];
|
|
}
|
|
/*Truncate to full-pel positions.*/
|
|
for(i=0;i<ncandidates;i++){
|
|
_mcenc->candidates[i][0]=OC_DIV2(_mcenc->candidates[i][0]);
|
|
_mcenc->candidates[i][1]=OC_DIV2(_mcenc->candidates[i][1]);
|
|
}
|
|
_mcenc->ncandidates=ncandidates;
|
|
}
|
|
|
|
#if 0
|
|
static unsigned oc_sad16_halfpel(const oc_enc_ctx *_enc,
|
|
const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4],
|
|
int _mvoffset0,int _mvoffset1,const unsigned char *_src,
|
|
const unsigned char *_ref,int _ystride,unsigned _best_err){
|
|
unsigned err;
|
|
int bi;
|
|
err=0;
|
|
for(bi=0;bi<4;bi++){
|
|
ptrdiff_t frag_offs;
|
|
frag_offs=_frag_buf_offs[_fragis[bi]];
|
|
err+=oc_enc_frag_sad2_thresh(_enc,_src+frag_offs,_ref+frag_offs+_mvoffset0,
|
|
_ref+frag_offs+_mvoffset1,_ystride,_best_err-err);
|
|
}
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
static unsigned oc_satd16_halfpel(const oc_enc_ctx *_enc,
|
|
const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4],
|
|
int _mvoffset0,int _mvoffset1,const unsigned char *_src,
|
|
const unsigned char *_ref,int _ystride,unsigned _best_err){
|
|
unsigned err;
|
|
int bi;
|
|
err=0;
|
|
for(bi=0;bi<4;bi++){
|
|
ptrdiff_t frag_offs;
|
|
frag_offs=_frag_buf_offs[_fragis[bi]];
|
|
err+=oc_enc_frag_satd2_thresh(_enc,_src+frag_offs,_ref+frag_offs+_mvoffset0,
|
|
_ref+frag_offs+_mvoffset1,_ystride,_best_err-err);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static unsigned oc_mcenc_ysad_check_mbcandidate_fullpel(const oc_enc_ctx *_enc,
|
|
const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4],int _dx,int _dy,
|
|
const unsigned char *_src,const unsigned char *_ref,int _ystride,
|
|
unsigned _block_err[4]){
|
|
unsigned err;
|
|
int mvoffset;
|
|
int bi;
|
|
mvoffset=_dx+_dy*_ystride;
|
|
err=0;
|
|
for(bi=0;bi<4;bi++){
|
|
ptrdiff_t frag_offs;
|
|
unsigned block_err;
|
|
frag_offs=_frag_buf_offs[_fragis[bi]];
|
|
block_err=oc_enc_frag_sad(_enc,
|
|
_src+frag_offs,_ref+frag_offs+mvoffset,_ystride);
|
|
_block_err[bi]=block_err;
|
|
err+=block_err;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int oc_mcenc_ysatd_check_mbcandidate_fullpel(const oc_enc_ctx *_enc,
|
|
const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4],int _dx,int _dy,
|
|
const unsigned char *_src,const unsigned char *_ref,int _ystride){
|
|
int mvoffset;
|
|
int err;
|
|
int bi;
|
|
mvoffset=_dx+_dy*_ystride;
|
|
err=0;
|
|
for(bi=0;bi<4;bi++){
|
|
ptrdiff_t frag_offs;
|
|
frag_offs=_frag_buf_offs[_fragis[bi]];
|
|
err+=oc_enc_frag_satd_thresh(_enc,
|
|
_src+frag_offs,_ref+frag_offs+mvoffset,_ystride,UINT_MAX);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static unsigned oc_mcenc_ysatd_check_bcandidate_fullpel(const oc_enc_ctx *_enc,
|
|
ptrdiff_t _frag_offs,int _dx,int _dy,
|
|
const unsigned char *_src,const unsigned char *_ref,int _ystride){
|
|
return oc_enc_frag_satd_thresh(_enc,
|
|
_src+_frag_offs,_ref+_frag_offs+_dx+_dy*_ystride,_ystride,UINT_MAX);
|
|
}
|
|
|
|
/*Perform a motion vector search for this macro block against a single
|
|
reference frame.
|
|
As a bonus, individual block motion vectors are computed as well, as much of
|
|
the work can be shared.
|
|
The actual motion vector is stored in the appropriate place in the
|
|
oc_mb_enc_info structure.
|
|
_mcenc: The motion compensation context.
|
|
_accum: Drop frame/golden MV accumulators.
|
|
_mbi: The macro block index.
|
|
_frame: The frame to search, either OC_FRAME_PREV or OC_FRAME_GOLD.*/
|
|
void oc_mcenc_search_frame(oc_enc_ctx *_enc,int _accum[2],int _mbi,int _frame){
|
|
/*Note: Traditionally this search is done using a rate-distortion objective
|
|
function of the form D+lambda*R.
|
|
However, xiphmont tested this and found it produced a small degredation,
|
|
while requiring extra computation.
|
|
This is most likely due to Theora's peculiar MV encoding scheme: MVs are
|
|
not coded relative to a predictor, and the only truly cheap way to use a
|
|
MV is in the LAST or LAST2 MB modes, which are not being considered here.
|
|
Therefore if we use the MV found here, it's only because both LAST and
|
|
LAST2 performed poorly, and therefore the MB is not likely to be uniform
|
|
or suffer from the aperture problem.
|
|
Furthermore we would like to re-use the MV found here for as many MBs as
|
|
possible, so picking a slightly sub-optimal vector to save a bit or two
|
|
may cause increased degredation in many blocks to come.
|
|
We could artificially reduce lambda to compensate, but it's faster to just
|
|
disable it entirely, and use D (the distortion) as the sole criterion.*/
|
|
oc_mcenc_ctx mcenc;
|
|
const ptrdiff_t *frag_buf_offs;
|
|
const ptrdiff_t *fragis;
|
|
const unsigned char *src;
|
|
const unsigned char *ref;
|
|
int ystride;
|
|
oc_mb_enc_info *embs;
|
|
ogg_int32_t hit_cache[31];
|
|
ogg_int32_t hitbit;
|
|
unsigned best_block_err[4];
|
|
unsigned block_err[4];
|
|
unsigned best_err;
|
|
int best_vec[2];
|
|
int best_block_vec[4][2];
|
|
int candx;
|
|
int candy;
|
|
int bi;
|
|
embs=_enc->mb_info;
|
|
/*Find some candidate motion vectors.*/
|
|
oc_mcenc_find_candidates(_enc,&mcenc,_accum,_mbi,_frame);
|
|
/*Clear the cache of locations we've examined.*/
|
|
memset(hit_cache,0,sizeof(hit_cache));
|
|
/*Start with the median predictor.*/
|
|
candx=mcenc.candidates[0][0];
|
|
candy=mcenc.candidates[0][1];
|
|
hit_cache[candy+15]|=(ogg_int32_t)1<<candx+15;
|
|
frag_buf_offs=_enc->state.frag_buf_offs;
|
|
fragis=_enc->state.mb_maps[_mbi][0];
|
|
src=_enc->state.ref_frame_data[OC_FRAME_IO];
|
|
ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[_frame]];
|
|
ystride=_enc->state.ref_ystride[0];
|
|
/*TODO: customize error function for speed/(quality+size) tradeoff.*/
|
|
best_err=oc_mcenc_ysad_check_mbcandidate_fullpel(_enc,
|
|
frag_buf_offs,fragis,candx,candy,src,ref,ystride,block_err);
|
|
best_vec[0]=candx;
|
|
best_vec[1]=candy;
|
|
if(_frame==OC_FRAME_PREV){
|
|
for(bi=0;bi<4;bi++){
|
|
best_block_err[bi]=block_err[bi];
|
|
best_block_vec[bi][0]=candx;
|
|
best_block_vec[bi][1]=candy;
|
|
}
|
|
}
|
|
/*If this predictor fails, move on to set A.*/
|
|
if(best_err>OC_YSAD_THRESH1){
|
|
unsigned err;
|
|
unsigned t2;
|
|
int ncs;
|
|
int ci;
|
|
/*Compute the early termination threshold for set A.*/
|
|
t2=embs[_mbi].error[_frame];
|
|
ncs=OC_MINI(3,embs[_mbi].ncneighbors);
|
|
for(ci=0;ci<ncs;ci++){
|
|
t2=OC_MAXI(t2,embs[embs[_mbi].cneighbors[ci]].error[_frame]);
|
|
}
|
|
t2+=(t2>>OC_YSAD_THRESH2_SCALE_BITS)+OC_YSAD_THRESH2_OFFSET;
|
|
/*Examine the candidates in set A.*/
|
|
for(ci=1;ci<mcenc.setb0;ci++){
|
|
candx=mcenc.candidates[ci][0];
|
|
candy=mcenc.candidates[ci][1];
|
|
/*If we've already examined this vector, then we would be using it if it
|
|
was better than what we are using.*/
|
|
hitbit=(ogg_int32_t)1<<candx+15;
|
|
if(hit_cache[candy+15]&hitbit)continue;
|
|
hit_cache[candy+15]|=hitbit;
|
|
err=oc_mcenc_ysad_check_mbcandidate_fullpel(_enc,
|
|
frag_buf_offs,fragis,candx,candy,src,ref,ystride,block_err);
|
|
if(err<best_err){
|
|
best_err=err;
|
|
best_vec[0]=candx;
|
|
best_vec[1]=candy;
|
|
}
|
|
if(_frame==OC_FRAME_PREV){
|
|
for(bi=0;bi<4;bi++)if(block_err[bi]<best_block_err[bi]){
|
|
best_block_err[bi]=block_err[bi];
|
|
best_block_vec[bi][0]=candx;
|
|
best_block_vec[bi][1]=candy;
|
|
}
|
|
}
|
|
}
|
|
if(best_err>t2){
|
|
/*Examine the candidates in set B.*/
|
|
for(;ci<mcenc.ncandidates;ci++){
|
|
candx=mcenc.candidates[ci][0];
|
|
candy=mcenc.candidates[ci][1];
|
|
hitbit=(ogg_int32_t)1<<candx+15;
|
|
if(hit_cache[candy+15]&hitbit)continue;
|
|
hit_cache[candy+15]|=hitbit;
|
|
err=oc_mcenc_ysad_check_mbcandidate_fullpel(_enc,
|
|
frag_buf_offs,fragis,candx,candy,src,ref,ystride,block_err);
|
|
if(err<best_err){
|
|
best_err=err;
|
|
best_vec[0]=candx;
|
|
best_vec[1]=candy;
|
|
}
|
|
if(_frame==OC_FRAME_PREV){
|
|
for(bi=0;bi<4;bi++)if(block_err[bi]<best_block_err[bi]){
|
|
best_block_err[bi]=block_err[bi];
|
|
best_block_vec[bi][0]=candx;
|
|
best_block_vec[bi][1]=candy;
|
|
}
|
|
}
|
|
}
|
|
/*Use the same threshold for set B as in set A.*/
|
|
if(best_err>t2){
|
|
int best_site;
|
|
int nsites;
|
|
int sitei;
|
|
int site;
|
|
int b;
|
|
/*Square pattern search.*/
|
|
for(;;){
|
|
best_site=4;
|
|
/*Compose the bit flags for boundary conditions.*/
|
|
b=OC_DIV16(-best_vec[0]+1)|OC_DIV16(best_vec[0]+1)<<1|
|
|
OC_DIV16(-best_vec[1]+1)<<2|OC_DIV16(best_vec[1]+1)<<3;
|
|
nsites=OC_SQUARE_NSITES[b];
|
|
for(sitei=0;sitei<nsites;sitei++){
|
|
site=OC_SQUARE_SITES[b][sitei];
|
|
candx=best_vec[0]+OC_SQUARE_DX[site];
|
|
candy=best_vec[1]+OC_SQUARE_DY[site];
|
|
hitbit=(ogg_int32_t)1<<candx+15;
|
|
if(hit_cache[candy+15]&hitbit)continue;
|
|
hit_cache[candy+15]|=hitbit;
|
|
err=oc_mcenc_ysad_check_mbcandidate_fullpel(_enc,
|
|
frag_buf_offs,fragis,candx,candy,src,ref,ystride,block_err);
|
|
if(err<best_err){
|
|
best_err=err;
|
|
best_site=site;
|
|
}
|
|
if(_frame==OC_FRAME_PREV){
|
|
for(bi=0;bi<4;bi++)if(block_err[bi]<best_block_err[bi]){
|
|
best_block_err[bi]=block_err[bi];
|
|
best_block_vec[bi][0]=candx;
|
|
best_block_vec[bi][1]=candy;
|
|
}
|
|
}
|
|
}
|
|
if(best_site==4)break;
|
|
best_vec[0]+=OC_SQUARE_DX[best_site];
|
|
best_vec[1]+=OC_SQUARE_DY[best_site];
|
|
}
|
|
/*Final 4-MV search.*/
|
|
/*Simply use 1/4 of the macro block set A and B threshold as the
|
|
individual block threshold.*/
|
|
if(_frame==OC_FRAME_PREV){
|
|
t2>>=2;
|
|
for(bi=0;bi<4;bi++){
|
|
if(best_block_err[bi]>t2){
|
|
/*Square pattern search.
|
|
We do this in a slightly interesting manner.
|
|
We continue to check the SAD of all four blocks in the
|
|
macro block.
|
|
This gives us two things:
|
|
1) We can continue to use the hit_cache to avoid duplicate
|
|
checks.
|
|
Otherwise we could continue to read it, but not write to it
|
|
without saving and restoring it for each block.
|
|
Note that we could still eliminate a large number of
|
|
duplicate checks by taking into account the site we came
|
|
from when choosing the site list.
|
|
We can still do that to avoid extra hit_cache queries, and
|
|
it might even be a speed win.
|
|
2) It gives us a slightly better chance of escaping local
|
|
minima.
|
|
We would not be here if we weren't doing a fairly bad job
|
|
in finding a good vector, and checking these vectors can
|
|
save us from 100 to several thousand points off our SAD 1
|
|
in 15 times.
|
|
TODO: Is this a good idea?
|
|
Who knows.
|
|
It needs more testing.*/
|
|
for(;;){
|
|
int bestx;
|
|
int besty;
|
|
int bj;
|
|
bestx=best_block_vec[bi][0];
|
|
besty=best_block_vec[bi][1];
|
|
/*Compose the bit flags for boundary conditions.*/
|
|
b=OC_DIV16(-bestx+1)|OC_DIV16(bestx+1)<<1|
|
|
OC_DIV16(-besty+1)<<2|OC_DIV16(besty+1)<<3;
|
|
nsites=OC_SQUARE_NSITES[b];
|
|
for(sitei=0;sitei<nsites;sitei++){
|
|
site=OC_SQUARE_SITES[b][sitei];
|
|
candx=bestx+OC_SQUARE_DX[site];
|
|
candy=besty+OC_SQUARE_DY[site];
|
|
hitbit=(ogg_int32_t)1<<candx+15;
|
|
if(hit_cache[candy+15]&hitbit)continue;
|
|
hit_cache[candy+15]|=hitbit;
|
|
err=oc_mcenc_ysad_check_mbcandidate_fullpel(_enc,
|
|
frag_buf_offs,fragis,candx,candy,src,ref,ystride,block_err);
|
|
if(err<best_err){
|
|
best_err=err;
|
|
best_vec[0]=candx;
|
|
best_vec[1]=candy;
|
|
}
|
|
for(bj=0;bj<4;bj++)if(block_err[bj]<best_block_err[bj]){
|
|
best_block_err[bj]=block_err[bj];
|
|
best_block_vec[bj][0]=candx;
|
|
best_block_vec[bj][1]=candy;
|
|
}
|
|
}
|
|
if(best_block_vec[bi][0]==bestx&&best_block_vec[bi][1]==besty){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
embs[_mbi].error[_frame]=(ogg_uint16_t)best_err;
|
|
candx=best_vec[0];
|
|
candy=best_vec[1];
|
|
embs[_mbi].satd[_frame]=oc_mcenc_ysatd_check_mbcandidate_fullpel(_enc,
|
|
frag_buf_offs,fragis,candx,candy,src,ref,ystride);
|
|
embs[_mbi].analysis_mv[0][_frame][0]=(signed char)(candx<<1);
|
|
embs[_mbi].analysis_mv[0][_frame][1]=(signed char)(candy<<1);
|
|
if(_frame==OC_FRAME_PREV){
|
|
for(bi=0;bi<4;bi++){
|
|
candx=best_block_vec[bi][0];
|
|
candy=best_block_vec[bi][1];
|
|
embs[_mbi].block_satd[bi]=oc_mcenc_ysatd_check_bcandidate_fullpel(_enc,
|
|
frag_buf_offs[fragis[bi]],candx,candy,src,ref,ystride);
|
|
embs[_mbi].block_mv[bi][0]=(signed char)(candx<<1);
|
|
embs[_mbi].block_mv[bi][1]=(signed char)(candy<<1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void oc_mcenc_search(oc_enc_ctx *_enc,int _mbi){
|
|
oc_mv2 *mvs;
|
|
int accum_p[2];
|
|
int accum_g[2];
|
|
mvs=_enc->mb_info[_mbi].analysis_mv;
|
|
if(_enc->prevframe_dropped){
|
|
accum_p[0]=mvs[0][OC_FRAME_PREV][0];
|
|
accum_p[1]=mvs[0][OC_FRAME_PREV][1];
|
|
}
|
|
else accum_p[1]=accum_p[0]=0;
|
|
accum_g[0]=mvs[2][OC_FRAME_GOLD][0];
|
|
accum_g[1]=mvs[2][OC_FRAME_GOLD][1];
|
|
mvs[0][OC_FRAME_PREV][0]-=mvs[2][OC_FRAME_PREV][0];
|
|
mvs[0][OC_FRAME_PREV][1]-=mvs[2][OC_FRAME_PREV][1];
|
|
/*Move the motion vector predictors back a frame.*/
|
|
memmove(mvs+1,mvs,2*sizeof(*mvs));
|
|
/*Search the last frame.*/
|
|
oc_mcenc_search_frame(_enc,accum_p,_mbi,OC_FRAME_PREV);
|
|
mvs[2][OC_FRAME_PREV][0]=accum_p[0];
|
|
mvs[2][OC_FRAME_PREV][1]=accum_p[1];
|
|
/*GOLDEN MVs are different from PREV MVs in that they're each absolute
|
|
offsets from some frame in the past rather than relative offsets from the
|
|
frame before.
|
|
For predictor calculation to make sense, we need them to be in the same
|
|
form as PREV MVs.*/
|
|
mvs[1][OC_FRAME_GOLD][0]-=mvs[2][OC_FRAME_GOLD][0];
|
|
mvs[1][OC_FRAME_GOLD][1]-=mvs[2][OC_FRAME_GOLD][1];
|
|
mvs[2][OC_FRAME_GOLD][0]-=accum_g[0];
|
|
mvs[2][OC_FRAME_GOLD][1]-=accum_g[1];
|
|
/*Search the golden frame.*/
|
|
oc_mcenc_search_frame(_enc,accum_g,_mbi,OC_FRAME_GOLD);
|
|
/*Put GOLDEN MVs back into absolute offset form.
|
|
The newest MV is already an absolute offset.*/
|
|
mvs[2][OC_FRAME_GOLD][0]+=accum_g[0];
|
|
mvs[2][OC_FRAME_GOLD][1]+=accum_g[1];
|
|
mvs[1][OC_FRAME_GOLD][0]+=mvs[2][OC_FRAME_GOLD][0];
|
|
mvs[1][OC_FRAME_GOLD][1]+=mvs[2][OC_FRAME_GOLD][1];
|
|
}
|
|
|
|
#if 0
|
|
static int oc_mcenc_ysad_halfpel_mbrefine(const oc_enc_ctx *_enc,int _mbi,
|
|
int _vec[2],int _best_err,int _frame){
|
|
const unsigned char *src;
|
|
const unsigned char *ref;
|
|
const ptrdiff_t *frag_buf_offs;
|
|
const ptrdiff_t *fragis;
|
|
int offset_y[9];
|
|
int ystride;
|
|
int mvoffset_base;
|
|
int best_site;
|
|
int sitei;
|
|
int err;
|
|
src=_enc->state.ref_frame_data[OC_FRAME_IO];
|
|
ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[_framei]];
|
|
frag_buf_offs=_enc->state.frag_buf_offs;
|
|
fragis=_enc->state.mb_maps[_mbi][0];
|
|
ystride=_enc->state.ref_ystride[0];
|
|
mvoffset_base=_vec[0]+_vec[1]*ystride;
|
|
offset_y[0]=offset_y[1]=offset_y[2]=-ystride;
|
|
offset_y[3]=offset_y[5]=0;
|
|
offset_y[6]=offset_y[7]=offset_y[8]=ystride;
|
|
best_site=4;
|
|
for(sitei=0;sitei<8;sitei++){
|
|
int site;
|
|
int xmask;
|
|
int ymask;
|
|
int dx;
|
|
int dy;
|
|
int mvoffset0;
|
|
int mvoffset1;
|
|
site=OC_SQUARE_SITES[0][sitei];
|
|
dx=OC_SQUARE_DX[site];
|
|
dy=OC_SQUARE_DY[site];
|
|
/*The following code SHOULD be equivalent to
|
|
oc_state_get_mv_offsets(&_mcenc->enc.state,&mvoffset0,&mvoffset1,
|
|
(_vec[0]<<1)+dx,(_vec[1]<<1)+dy,ref_ystride,0);
|
|
However, it should also be much faster, as it involves no multiplies and
|
|
doesn't have to handle chroma vectors.*/
|
|
xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx);
|
|
ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy);
|
|
mvoffset0=mvoffset_base+(dx&xmask)+(offset_y[site]&ymask);
|
|
mvoffset1=mvoffset_base+(dx&~xmask)+(offset_y[site]&~ymask);
|
|
err=oc_sad16_halfpel(_enc,frag_buf_offs,fragis,
|
|
mvoffset0,mvoffset1,src,ref,ystride,_best_err);
|
|
if(err<_best_err){
|
|
_best_err=err;
|
|
best_site=site;
|
|
}
|
|
}
|
|
_vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site];
|
|
_vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site];
|
|
return _best_err;
|
|
}
|
|
#endif
|
|
|
|
static unsigned oc_mcenc_ysatd_halfpel_mbrefine(const oc_enc_ctx *_enc,
|
|
int _mbi,int _vec[2],unsigned _best_err,int _frame){
|
|
const unsigned char *src;
|
|
const unsigned char *ref;
|
|
const ptrdiff_t *frag_buf_offs;
|
|
const ptrdiff_t *fragis;
|
|
int offset_y[9];
|
|
int ystride;
|
|
int mvoffset_base;
|
|
int best_site;
|
|
int sitei;
|
|
int err;
|
|
src=_enc->state.ref_frame_data[OC_FRAME_IO];
|
|
ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[_frame]];
|
|
frag_buf_offs=_enc->state.frag_buf_offs;
|
|
fragis=_enc->state.mb_maps[_mbi][0];
|
|
ystride=_enc->state.ref_ystride[0];
|
|
mvoffset_base=_vec[0]+_vec[1]*ystride;
|
|
offset_y[0]=offset_y[1]=offset_y[2]=-ystride;
|
|
offset_y[3]=offset_y[5]=0;
|
|
offset_y[6]=offset_y[7]=offset_y[8]=ystride;
|
|
best_site=4;
|
|
for(sitei=0;sitei<8;sitei++){
|
|
int site;
|
|
int xmask;
|
|
int ymask;
|
|
int dx;
|
|
int dy;
|
|
int mvoffset0;
|
|
int mvoffset1;
|
|
site=OC_SQUARE_SITES[0][sitei];
|
|
dx=OC_SQUARE_DX[site];
|
|
dy=OC_SQUARE_DY[site];
|
|
/*The following code SHOULD be equivalent to
|
|
oc_state_get_mv_offsets(&_mcenc->enc.state,&mvoffset0,&mvoffset1,
|
|
(_vec[0]<<1)+dx,(_vec[1]<<1)+dy,ref_ystride,0);
|
|
However, it should also be much faster, as it involves no multiplies and
|
|
doesn't have to handle chroma vectors.*/
|
|
xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx);
|
|
ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy);
|
|
mvoffset0=mvoffset_base+(dx&xmask)+(offset_y[site]&ymask);
|
|
mvoffset1=mvoffset_base+(dx&~xmask)+(offset_y[site]&~ymask);
|
|
err=oc_satd16_halfpel(_enc,frag_buf_offs,fragis,
|
|
mvoffset0,mvoffset1,src,ref,ystride,_best_err);
|
|
if(err<_best_err){
|
|
_best_err=err;
|
|
best_site=site;
|
|
}
|
|
}
|
|
_vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site];
|
|
_vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site];
|
|
return _best_err;
|
|
}
|
|
|
|
void oc_mcenc_refine1mv(oc_enc_ctx *_enc,int _mbi,int _frame){
|
|
oc_mb_enc_info *embs;
|
|
int vec[2];
|
|
embs=_enc->mb_info;
|
|
vec[0]=OC_DIV2(embs[_mbi].analysis_mv[0][_frame][0]);
|
|
vec[1]=OC_DIV2(embs[_mbi].analysis_mv[0][_frame][1]);
|
|
embs[_mbi].satd[_frame]=oc_mcenc_ysatd_halfpel_mbrefine(_enc,
|
|
_mbi,vec,embs[_mbi].satd[_frame],_frame);
|
|
embs[_mbi].analysis_mv[0][_frame][0]=(signed char)vec[0];
|
|
embs[_mbi].analysis_mv[0][_frame][1]=(signed char)vec[1];
|
|
}
|
|
|
|
#if 0
|
|
static int oc_mcenc_ysad_halfpel_brefine(const oc_enc_ctx *_enc,
|
|
int _vec[2],const unsigned char *_src,const unsigned char *_ref,int _ystride,
|
|
int _offset_y[9],unsigned _best_err){
|
|
int mvoffset_base;
|
|
int best_site;
|
|
int sitei;
|
|
mvoffset_base=_vec[0]+_vec[1]*_ystride;
|
|
best_site=4;
|
|
for(sitei=0;sitei<8;sitei++){
|
|
unsigned err;
|
|
int site;
|
|
int xmask;
|
|
int ymask;
|
|
int dx;
|
|
int dy;
|
|
int mvoffset0;
|
|
int mvoffset1;
|
|
site=OC_SQUARE_SITES[0][sitei];
|
|
dx=OC_SQUARE_DX[site];
|
|
dy=OC_SQUARE_DY[site];
|
|
/*The following code SHOULD be equivalent to
|
|
oc_state_get_mv_offsets(&_mcenc->enc.state,&mvoffset0,&mvoffset1,
|
|
(_vec[0]<<1)+dx,(_vec[1]<<1)+dy,ref_ystride,0);
|
|
However, it should also be much faster, as it involves no multiplies and
|
|
doesn't have to handle chroma vectors.*/
|
|
xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx);
|
|
ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy);
|
|
mvoffset0=mvoffset_base+(dx&xmask)+(_offset_y[site]&ymask);
|
|
mvoffset1=mvoffset_base+(dx&~xmask)+(_offset_y[site]&~ymask);
|
|
err=oc_enc_frag_sad2_thresh(_enc,_src,
|
|
_ref+mvoffset0,_ref+mvoffset1,ystride,_best_err);
|
|
if(err<_best_err){
|
|
_best_err=err;
|
|
best_site=site;
|
|
}
|
|
}
|
|
_vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site];
|
|
_vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site];
|
|
return _best_err;
|
|
}
|
|
#endif
|
|
|
|
static unsigned oc_mcenc_ysatd_halfpel_brefine(const oc_enc_ctx *_enc,
|
|
int _vec[2],const unsigned char *_src,const unsigned char *_ref,int _ystride,
|
|
int _offset_y[9],unsigned _best_err){
|
|
int mvoffset_base;
|
|
int best_site;
|
|
int sitei;
|
|
mvoffset_base=_vec[0]+_vec[1]*_ystride;
|
|
best_site=4;
|
|
for(sitei=0;sitei<8;sitei++){
|
|
unsigned err;
|
|
int site;
|
|
int xmask;
|
|
int ymask;
|
|
int dx;
|
|
int dy;
|
|
int mvoffset0;
|
|
int mvoffset1;
|
|
site=OC_SQUARE_SITES[0][sitei];
|
|
dx=OC_SQUARE_DX[site];
|
|
dy=OC_SQUARE_DY[site];
|
|
/*The following code SHOULD be equivalent to
|
|
oc_state_get_mv_offsets(&_enc->state,&mvoffsets,0,
|
|
(_vec[0]<<1)+dx,(_vec[1]<<1)+dy);
|
|
However, it should also be much faster, as it involves no multiplies and
|
|
doesn't have to handle chroma vectors.*/
|
|
xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx);
|
|
ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy);
|
|
mvoffset0=mvoffset_base+(dx&xmask)+(_offset_y[site]&ymask);
|
|
mvoffset1=mvoffset_base+(dx&~xmask)+(_offset_y[site]&~ymask);
|
|
err=oc_enc_frag_satd2_thresh(_enc,_src,
|
|
_ref+mvoffset0,_ref+mvoffset1,_ystride,_best_err);
|
|
if(err<_best_err){
|
|
_best_err=err;
|
|
best_site=site;
|
|
}
|
|
}
|
|
_vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site];
|
|
_vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site];
|
|
return _best_err;
|
|
}
|
|
|
|
void oc_mcenc_refine4mv(oc_enc_ctx *_enc,int _mbi){
|
|
oc_mb_enc_info *embs;
|
|
const ptrdiff_t *frag_buf_offs;
|
|
const ptrdiff_t *fragis;
|
|
const unsigned char *src;
|
|
const unsigned char *ref;
|
|
int offset_y[9];
|
|
int ystride;
|
|
int bi;
|
|
ystride=_enc->state.ref_ystride[0];
|
|
frag_buf_offs=_enc->state.frag_buf_offs;
|
|
fragis=_enc->state.mb_maps[_mbi][0];
|
|
src=_enc->state.ref_frame_data[OC_FRAME_IO];
|
|
ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[OC_FRAME_PREV]];
|
|
offset_y[0]=offset_y[1]=offset_y[2]=-ystride;
|
|
offset_y[3]=offset_y[5]=0;
|
|
offset_y[6]=offset_y[7]=offset_y[8]=ystride;
|
|
embs=_enc->mb_info;
|
|
for(bi=0;bi<4;bi++){
|
|
ptrdiff_t frag_offs;
|
|
int vec[2];
|
|
frag_offs=frag_buf_offs[fragis[bi]];
|
|
vec[0]=OC_DIV2(embs[_mbi].block_mv[bi][0]);
|
|
vec[1]=OC_DIV2(embs[_mbi].block_mv[bi][1]);
|
|
embs[_mbi].block_satd[bi]=oc_mcenc_ysatd_halfpel_brefine(_enc,vec,
|
|
src+frag_offs,ref+frag_offs,ystride,offset_y,embs[_mbi].block_satd[bi]);
|
|
embs[_mbi].ref_mv[bi][0]=(signed char)vec[0];
|
|
embs[_mbi].ref_mv[bi][1]=(signed char)vec[1];
|
|
}
|
|
}
|