📄 motion_est.c
字号:
/*
* Motion estimation
* Copyright (c) 2000,2001 Fabrice Bellard.
* Copyright (c) 2002-2004 Michael Niedermayer
*
* new motion estimation (X1/EPZS) by Michael Niedermayer <michaelni@gmx.at>
*
* 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 motion_est.c
* Motion estimation.
*/
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "avcodec.h"
#include "dsputil.h"
#include "mpegvideo.h"
#undef NDEBUG
#include <assert.h>
#define SQ(a) ((a)*(a))
#define P_LEFT P[1]
#define P_TOP P[2]
#define P_TOPRIGHT P[3]
#define P_MEDIAN P[4]
#define P_MV1 P[9]
static inline int sad_hpel_motion_search(MpegEncContext * s,
int *mx_ptr, int *my_ptr, int dmin,
int src_index, int ref_index,
int size, int h);
static inline int update_map_generation(MotionEstContext *c)
{
c->map_generation+= 1<<(ME_MAP_MV_BITS*2);
if(c->map_generation==0){
c->map_generation= 1<<(ME_MAP_MV_BITS*2);
memset(c->map, 0, sizeof(uint32_t)*ME_MAP_SIZE);
}
return c->map_generation;
}
/* shape adaptive search stuff */
typedef struct Minima{
int height;
int x, y;
int checked;
}Minima;
static int minima_cmp(const void *a, const void *b){
const Minima *da = (const Minima *) a;
const Minima *db = (const Minima *) b;
return da->height - db->height;
}
#define FLAG_QPEL 1 //must be 1
#define FLAG_CHROMA 2
#define FLAG_DIRECT 4
static inline void init_ref(MotionEstContext *c, uint8_t *src[3], uint8_t *ref[3], uint8_t *ref2[3], int x, int y, int ref_index){
const int offset[3]= {
y*c-> stride + x,
((y*c->uvstride + x)>>1),
((y*c->uvstride + x)>>1),
};
int i;
for(i=0; i<3; i++){
c->src[0][i]= src [i] + offset[i];
c->ref[0][i]= ref [i] + offset[i];
}
if(ref_index){
for(i=0; i<3; i++){
c->ref[ref_index][i]= ref2[i] + offset[i];
}
}
}
static int get_flags(MotionEstContext *c, int direct, int chroma){
return ((c->avctx->flags&CODEC_FLAG_QPEL) ? FLAG_QPEL : 0)
+ (direct ? FLAG_DIRECT : 0)
+ (chroma ? FLAG_CHROMA : 0);
}
/*! \brief compares a block (either a full macroblock or a partition thereof)
against a proposed motion-compensated prediction of that block
*/
static av_always_inline int cmp(MpegEncContext *s, const int x, const int y, const int subx, const int suby,
const int size, const int h, int ref_index, int src_index,
me_cmp_func cmp_func, me_cmp_func chroma_cmp_func, const int flags){
MotionEstContext * const c= &s->me;
const int stride= c->stride;
const int uvstride= c->uvstride;
const int qpel= flags&FLAG_QPEL;
const int chroma= flags&FLAG_CHROMA;
const int dxy= subx + (suby<<(1+qpel)); //FIXME log2_subpel?
const int hx= subx + (x<<(1+qpel));
const int hy= suby + (y<<(1+qpel));
uint8_t * const * const ref= c->ref[ref_index];
uint8_t * const * const src= c->src[src_index];
int d;
//FIXME check chroma 4mv, (no crashes ...)
if(flags&FLAG_DIRECT){
assert(x >= c->xmin && hx <= c->xmax<<(qpel+1) && y >= c->ymin && hy <= c->ymax<<(qpel+1));
if(x >= c->xmin && hx <= c->xmax<<(qpel+1) && y >= c->ymin && hy <= c->ymax<<(qpel+1)){
const int time_pp= s->pp_time;
const int time_pb= s->pb_time;
const int mask= 2*qpel+1;
if(s->mv_type==MV_TYPE_8X8){
int i;
for(i=0; i<4; i++){
int fx = c->direct_basis_mv[i][0] + hx;
int fy = c->direct_basis_mv[i][1] + hy;
int bx = hx ? fx - c->co_located_mv[i][0] : c->co_located_mv[i][0]*(time_pb - time_pp)/time_pp + ((i &1)<<(qpel+4));
int by = hy ? fy - c->co_located_mv[i][1] : c->co_located_mv[i][1]*(time_pb - time_pp)/time_pp + ((i>>1)<<(qpel+4));
int fxy= (fx&mask) + ((fy&mask)<<(qpel+1));
int bxy= (bx&mask) + ((by&mask)<<(qpel+1));
uint8_t *dst= c->temp + 8*(i&1) + 8*stride*(i>>1);
if(qpel){
c->qpel_put[1][fxy](dst, ref[0] + (fx>>2) + (fy>>2)*stride, stride);
c->qpel_avg[1][bxy](dst, ref[8] + (bx>>2) + (by>>2)*stride, stride);
}else{
c->hpel_put[1][fxy](dst, ref[0] + (fx>>1) + (fy>>1)*stride, stride, 8);
c->hpel_avg[1][bxy](dst, ref[8] + (bx>>1) + (by>>1)*stride, stride, 8);
}
}
}else{
int fx = c->direct_basis_mv[0][0] + hx;
int fy = c->direct_basis_mv[0][1] + hy;
int bx = hx ? fx - c->co_located_mv[0][0] : (c->co_located_mv[0][0]*(time_pb - time_pp)/time_pp);
int by = hy ? fy - c->co_located_mv[0][1] : (c->co_located_mv[0][1]*(time_pb - time_pp)/time_pp);
int fxy= (fx&mask) + ((fy&mask)<<(qpel+1));
int bxy= (bx&mask) + ((by&mask)<<(qpel+1));
if(qpel){
c->qpel_put[1][fxy](c->temp , ref[0] + (fx>>2) + (fy>>2)*stride , stride);
c->qpel_put[1][fxy](c->temp + 8 , ref[0] + (fx>>2) + (fy>>2)*stride + 8 , stride);
c->qpel_put[1][fxy](c->temp + 8*stride, ref[0] + (fx>>2) + (fy>>2)*stride + 8*stride, stride);
c->qpel_put[1][fxy](c->temp + 8 + 8*stride, ref[0] + (fx>>2) + (fy>>2)*stride + 8 + 8*stride, stride);
c->qpel_avg[1][bxy](c->temp , ref[8] + (bx>>2) + (by>>2)*stride , stride);
c->qpel_avg[1][bxy](c->temp + 8 , ref[8] + (bx>>2) + (by>>2)*stride + 8 , stride);
c->qpel_avg[1][bxy](c->temp + 8*stride, ref[8] + (bx>>2) + (by>>2)*stride + 8*stride, stride);
c->qpel_avg[1][bxy](c->temp + 8 + 8*stride, ref[8] + (bx>>2) + (by>>2)*stride + 8 + 8*stride, stride);
}else{
assert((fx>>1) + 16*s->mb_x >= -16);
assert((fy>>1) + 16*s->mb_y >= -16);
assert((fx>>1) + 16*s->mb_x <= s->width);
assert((fy>>1) + 16*s->mb_y <= s->height);
assert((bx>>1) + 16*s->mb_x >= -16);
assert((by>>1) + 16*s->mb_y >= -16);
assert((bx>>1) + 16*s->mb_x <= s->width);
assert((by>>1) + 16*s->mb_y <= s->height);
c->hpel_put[0][fxy](c->temp, ref[0] + (fx>>1) + (fy>>1)*stride, stride, 16);
c->hpel_avg[0][bxy](c->temp, ref[8] + (bx>>1) + (by>>1)*stride, stride, 16);
}
}
d = cmp_func(s, c->temp, src[0], stride, 16);
}else
d= 256*256*256*32;
}else{
int uvdxy; /* no, it might not be used uninitialized */
if(dxy){
if(qpel){
c->qpel_put[size][dxy](c->temp, ref[0] + x + y*stride, stride); //FIXME prototype (add h)
if(chroma){
int cx= hx/2;
int cy= hy/2;
cx= (cx>>1)|(cx&1);
cy= (cy>>1)|(cy&1);
uvdxy= (cx&1) + 2*(cy&1);
//FIXME x/y wrong, but mpeg4 qpel is sick anyway, we should drop as much of it as possible in favor for h264
}
}else{
c->hpel_put[size][dxy](c->temp, ref[0] + x + y*stride, stride, h);
if(chroma)
uvdxy= dxy | (x&1) | (2*(y&1));
}
d = cmp_func(s, c->temp, src[0], stride, h);
}else{
d = cmp_func(s, src[0], ref[0] + x + y*stride, stride, h);
if(chroma)
uvdxy= (x&1) + 2*(y&1);
}
if(chroma){
uint8_t * const uvtemp= c->temp + 16*stride;
c->hpel_put[size+1][uvdxy](uvtemp , ref[1] + (x>>1) + (y>>1)*uvstride, uvstride, h>>1);
c->hpel_put[size+1][uvdxy](uvtemp+8, ref[2] + (x>>1) + (y>>1)*uvstride, uvstride, h>>1);
d += chroma_cmp_func(s, uvtemp , src[1], uvstride, h>>1);
d += chroma_cmp_func(s, uvtemp+8, src[2], uvstride, h>>1);
}
}
#if 0
if(full_pel){
const int index= (((y)<<ME_MAP_SHIFT) + (x))&(ME_MAP_SIZE-1);
score_map[index]= d;
}
d += (c->mv_penalty[hx - c->pred_x] + c->mv_penalty[hy - c->pred_y])*c->penalty_factor;
#endif
return d;
}
#include "motion_est_template.c"
static int zero_cmp(void *s, uint8_t *a, uint8_t *b, int stride, int h){
return 0;
}
static void zero_hpel(uint8_t *a, const uint8_t *b, int stride, int h){
}
void ff_init_me(MpegEncContext *s){
MotionEstContext * const c= &s->me;
int cache_size= FFMIN(ME_MAP_SIZE>>ME_MAP_SHIFT, 1<<ME_MAP_SHIFT);
int dia_size= FFMAX(FFABS(s->avctx->dia_size)&255, FFABS(s->avctx->pre_dia_size)&255);
c->avctx= s->avctx;
if(cache_size < 2*dia_size && !c->stride){
av_log(s->avctx, AV_LOG_INFO, "ME_MAP size may be a little small for the selected diamond size\n");
}
ff_set_cmp(&s->dsp, s->dsp.me_pre_cmp, c->avctx->me_pre_cmp);
ff_set_cmp(&s->dsp, s->dsp.me_cmp, c->avctx->me_cmp);
ff_set_cmp(&s->dsp, s->dsp.me_sub_cmp, c->avctx->me_sub_cmp);
ff_set_cmp(&s->dsp, s->dsp.mb_cmp, c->avctx->mb_cmp);
c->flags = get_flags(c, 0, c->avctx->me_cmp &FF_CMP_CHROMA);
c->sub_flags= get_flags(c, 0, c->avctx->me_sub_cmp&FF_CMP_CHROMA);
c->mb_flags = get_flags(c, 0, c->avctx->mb_cmp &FF_CMP_CHROMA);
/*FIXME s->no_rounding b_type*/
if(s->flags&CODEC_FLAG_QPEL){
c->sub_motion_search= qpel_motion_search;
c->qpel_avg= s->dsp.avg_qpel_pixels_tab;
if(s->no_rounding) c->qpel_put= s->dsp.put_no_rnd_qpel_pixels_tab;
else c->qpel_put= s->dsp.put_qpel_pixels_tab;
}else{
if(c->avctx->me_sub_cmp&FF_CMP_CHROMA)
c->sub_motion_search= hpel_motion_search;
else if( c->avctx->me_sub_cmp == FF_CMP_SAD
&& c->avctx-> me_cmp == FF_CMP_SAD
&& c->avctx-> mb_cmp == FF_CMP_SAD)
c->sub_motion_search= sad_hpel_motion_search; // 2050 vs. 2450 cycles
else
c->sub_motion_search= hpel_motion_search;
}
c->hpel_avg= s->dsp.avg_pixels_tab;
if(s->no_rounding) c->hpel_put= s->dsp.put_no_rnd_pixels_tab;
else c->hpel_put= s->dsp.put_pixels_tab;
if(s->linesize){
c->stride = s->linesize;
c->uvstride= s->uvlinesize;
}else{
c->stride = 16*s->mb_width + 32;
c->uvstride= 8*s->mb_width + 16;
}
/* 8x8 fullpel search would need a 4x4 chroma compare, which we do
* not have yet, and even if we had, the motion estimation code
* does not expect it. */
if(s->codec_id != CODEC_ID_SNOW){
if((c->avctx->me_cmp&FF_CMP_CHROMA)/* && !s->dsp.me_cmp[2]*/){
s->dsp.me_cmp[2]= zero_cmp;
}
if((c->avctx->me_sub_cmp&FF_CMP_CHROMA) && !s->dsp.me_sub_cmp[2]){
s->dsp.me_sub_cmp[2]= zero_cmp;
}
c->hpel_put[2][0]= c->hpel_put[2][1]=
c->hpel_put[2][2]= c->hpel_put[2][3]= zero_hpel;
}
if(s->codec_id == CODEC_ID_H261){
c->sub_motion_search= no_sub_motion_search;
}
c->temp= c->scratchpad;
}
#if 0
static int pix_dev(uint8_t * pix, int line_size, int mean)
{
int s, i, j;
s = 0;
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j += 8) {
s += FFABS(pix[0]-mean);
s += FFABS(pix[1]-mean);
s += FFABS(pix[2]-mean);
s += FFABS(pix[3]-mean);
s += FFABS(pix[4]-mean);
s += FFABS(pix[5]-mean);
s += FFABS(pix[6]-mean);
s += FFABS(pix[7]-mean);
pix += 8;
}
pix += line_size - 16;
}
return s;
}
#endif
static inline void no_motion_search(MpegEncContext * s,
int *mx_ptr, int *my_ptr)
{
*mx_ptr = 16 * s->mb_x;
*my_ptr = 16 * s->mb_y;
}
#if 0 /* the use of these functions is inside #if 0 */
static int full_motion_search(MpegEncContext * s,
int *mx_ptr, int *my_ptr, int range,
int xmin, int ymin, int xmax, int ymax, uint8_t *ref_picture)
{
int x1, y1, x2, y2, xx, yy, x, y;
int mx, my, dmin, d;
uint8_t *pix;
xx = 16 * s->mb_x;
yy = 16 * s->mb_y;
x1 = xx - range + 1; /* we loose one pixel to avoid boundary pb with half pixel pred */
if (x1 < xmin)
x1 = xmin;
x2 = xx + range - 1;
if (x2 > xmax)
x2 = xmax;
y1 = yy - range + 1;
if (y1 < ymin)
y1 = ymin;
y2 = yy + range - 1;
if (y2 > ymax)
y2 = ymax;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -