xvmcvideo.c 11.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * XVideo Motion Compensation
 * Copyright (c) 2003 Ivan Kalvachev
 *
 * This library 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 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

Ivan Kalvachev's avatar
Ivan Kalvachev committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#include <limits.h>

//avcodec include
#include "avcodec.h"
#include "dsputil.h"
#include "mpegvideo.h"

#undef NDEBUG
#include <assert.h>

#ifdef USE_FASTMEMCPY
#include "fastmemcpy.h"
#endif

#ifdef HAVE_XVMC
35 36 37 38 39 40 41

//X11 includes are in the xvmc_render.h
//by replacing it with none-X one
//XvMC emulation could be performed

#include "xvmc_render.h"

Ivan Kalvachev's avatar
Ivan Kalvachev committed
42 43
//#include "xvmc_debug.h"

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
//set s->block
inline void XVMC_init_block(MpegEncContext *s){
xvmc_render_state_t * render;
    render = (xvmc_render_state_t*)s->current_picture.data[2];
    assert(render != NULL);
    if( (render == NULL) || (render->magic != MP_XVMC_RENDER_MAGIC) ){
        assert(0);
        return;//make sure that this is render packet
    }
    s->block =(DCTELEM *)(render->data_blocks+(render->next_free_data_block_num)*64);
}

void XVMC_pack_pblocks(MpegEncContext *s, int cbp){
int i,j;

    j=0;
60 61
    for(i=0;i<6;i++){
        if(cbp & (1<<(5-i)) ){
62 63 64 65 66 67
           s->pblocks[i] = (short *)(&s->block[(j++)]);
        }else{
           s->pblocks[i] = NULL;
        }
//        printf("s->pblocks[%d]=%p ,s->block=%p cbp=%d\n",i,s->pblocks[i],s->block,cbp);
    }
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
    if (s->chroma_format >= 2){
        if (s->chroma_format == 2){//CHROMA_422
            for(i=6;i<8;i++){
                if(cbp & (1<<(6+7-i)) ){
                    s->pblocks[i] = (short *)(&s->block[(j++)]);
                }else{
                    s->pblocks[i] = NULL;
                }
            }
        }else{//CHROMA_444
            for(i=6; i<12; i++){
                if(cbp & (1<<(6+11-i)) ){
                    s->pblocks[i] = (short *)(&s->block[(j++)]);
                }else{
                    s->pblocks[i] = NULL;
                }
            }
	}
    }
87 88
}

89
static int calc_cbp(MpegEncContext *s){
Ivan Kalvachev's avatar
Ivan Kalvachev committed
90 91
/* compute cbp */
int  i,cbp = 0;
92
    for(i=0; i<4; i++) {
Ivan Kalvachev's avatar
Ivan Kalvachev committed
93 94 95
        if(s->block_last_index[i] >= 0)
            cbp |= 1 << (5 - i);
    }
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    if(s->flags & CODEC_FLAG_GRAY)
         return cbp; //4 block for grayscale one done


    for(i=4; i<6; i++) {
        if(s->block_last_index[i] >= 0)
            cbp |= 1 << (5 - i);
    }
    if(s->chroma_format <  2) return cbp;


    if(s->chroma_format == 2){/*CHROMA_422*/
        for(i=6; i<8; i++) {
            if(s->block_last_index[i] >= 0)
                cbp |= 1 << (6+7 - i);
        }
    }else{/*CHROMA_444*/
        for(i=6; i<12; i++) {
            if(s->block_last_index[i] >= 0)
                cbp |= 1 << (6+11 - i);
        }
    }
Ivan Kalvachev's avatar
Ivan Kalvachev committed
118 119 120 121 122 123 124 125 126 127 128
    return cbp;
}



//these functions should be called on every new field or/and frame
//They should be safe if they are called few times for same field!
int XVMC_field_start(MpegEncContext*s, AVCodecContext *avctx){
xvmc_render_state_t * render,* last, * next;

    assert(avctx != NULL);
129

Ivan Kalvachev's avatar
Ivan Kalvachev committed
130 131
    render = (xvmc_render_state_t*)s->current_picture.data[2];
    assert(render != NULL);
132 133
    if( (render == NULL) || (render->magic != MP_XVMC_RENDER_MAGIC) )
        return -1;//make sure that this is render packet
Ivan Kalvachev's avatar
Ivan Kalvachev committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

    render->picture_structure = s->picture_structure;
    render->flags = (s->first_field)? 0: XVMC_SECOND_FIELD;

//make sure that all data is drawn by XVMC_end_frame
    assert(render->filled_mv_blocks_num==0);

    render->p_future_surface = NULL;
    render->p_past_surface = NULL;

    switch(s->pict_type){
        case  I_TYPE:
            return 0;// no prediction from other frames
        case  B_TYPE:
            next = (xvmc_render_state_t*)s->next_picture.data[2];
            assert(next!=NULL);
            assert(next->state & MP_XVMC_STATE_PREDICTION);
151 152
            if(next == NULL) return -1;
            if(next->magic != MP_XVMC_RENDER_MAGIC) return -1;
Ivan Kalvachev's avatar
Ivan Kalvachev committed
153 154 155 156 157 158
            render->p_future_surface = next->p_surface;
            //no return here, going to set forward prediction
        case  P_TYPE:
            last = (xvmc_render_state_t*)s->last_picture.data[2];
            if(last == NULL)// && !s->first_field)
                last = render;//predict second field from the first
159
            if(last->magic != MP_XVMC_RENDER_MAGIC) return -1;
Ivan Kalvachev's avatar
Ivan Kalvachev committed
160 161 162
            assert(last->state & MP_XVMC_STATE_PREDICTION);
            render->p_past_surface = last->p_surface;
            return 0;
163
    }
Ivan Kalvachev's avatar
Ivan Kalvachev committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

return -1;
}

void XVMC_field_end(MpegEncContext *s){
xvmc_render_state_t * render;
    render = (xvmc_render_state_t*)s->current_picture.data[2];
    assert(render != NULL);

    if(render->filled_mv_blocks_num > 0){
//        printf("xvmcvideo.c: rendering %d left blocks after last slice!!!\n",render->filled_mv_blocks_num );
        ff_draw_horiz_band(s,0,0);
    }
}

179
void XVMC_decode_mb(MpegEncContext *s){
Ivan Kalvachev's avatar
Ivan Kalvachev committed
180 181 182 183 184 185 186 187
XvMCMacroBlock * mv_block;
xvmc_render_state_t * render;
int i,cbp,blocks_per_mb;

const int mb_xy = s->mb_y * s->mb_stride + s->mb_x;


    if(s->encoding){
188
        av_log(s->avctx, AV_LOG_ERROR, "XVMC doesn't support encoding!!!\n");
Alex Beregszaszi's avatar
Alex Beregszaszi committed
189
	av_abort();
Ivan Kalvachev's avatar
Ivan Kalvachev committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
    }

   //from MPV_decode_mb(),
    /* update DC predictors for P macroblocks */
    if (!s->mb_intra) {
        s->last_dc[0] =
        s->last_dc[1] =
        s->last_dc[2] =  128 << s->intra_dc_precision;
    }

   //MC doesn't skip blocks
    s->mb_skiped = 0;


   // do I need to export quant when I could not perform postprocessing?
   // anyway, it doesn't hurrt
    s->current_picture.qscale_table[mb_xy] = s->qscale;

//START OF XVMC specific code
    render = (xvmc_render_state_t*)s->current_picture.data[2];
    assert(render!=NULL);
    assert(render->magic==MP_XVMC_RENDER_MAGIC);
    assert(render->mv_blocks);
213

Ivan Kalvachev's avatar
Ivan Kalvachev committed
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
    //take the next free macroblock
    mv_block = &render->mv_blocks[render->start_mv_blocks_num + 
                                   render->filled_mv_blocks_num ];

// memset(mv_block,0,sizeof(XvMCMacroBlock));

    mv_block->x = s->mb_x;
    mv_block->y = s->mb_y;
    mv_block->dct_type = s->interlaced_dct;//XVMC_DCT_TYPE_FRAME/FIELD;
//    mv_block->motion_type = 0;  //zero to silense warnings
    if(s->mb_intra){
        mv_block->macroblock_type = XVMC_MB_TYPE_INTRA;//no MC, all done
    }else{
        mv_block->macroblock_type = XVMC_MB_TYPE_PATTERN;

        if(s->mv_dir & MV_DIR_FORWARD){
            mv_block->macroblock_type|= XVMC_MB_TYPE_MOTION_FORWARD;
            //pmv[n][dir][xy]=mv[dir][n][xy]
            mv_block->PMV[0][0][0] = s->mv[0][0][0];
            mv_block->PMV[0][0][1] = s->mv[0][0][1];
            mv_block->PMV[1][0][0] = s->mv[0][1][0];
            mv_block->PMV[1][0][1] = s->mv[0][1][1];
        }
        if(s->mv_dir & MV_DIR_BACKWARD){
            mv_block->macroblock_type|=XVMC_MB_TYPE_MOTION_BACKWARD;
            mv_block->PMV[0][1][0] = s->mv[1][0][0];
            mv_block->PMV[0][1][1] = s->mv[1][0][1];
            mv_block->PMV[1][1][0] = s->mv[1][1][0];
            mv_block->PMV[1][1][1] = s->mv[1][1][1];
        }

        switch(s->mv_type){
            case  MV_TYPE_16X16:
                mv_block->motion_type = XVMC_PREDICTION_FRAME;
                break;
            case  MV_TYPE_16X8:
                mv_block->motion_type = XVMC_PREDICTION_16x8;
                break;
            case  MV_TYPE_FIELD:
                mv_block->motion_type = XVMC_PREDICTION_FIELD;
                if(s->picture_structure == PICT_FRAME){
                    mv_block->PMV[0][0][1]<<=1;
                    mv_block->PMV[1][0][1]<<=1;
                    mv_block->PMV[0][1][1]<<=1;
                    mv_block->PMV[1][1][1]<<=1;
                }
                break;
            case  MV_TYPE_DMV:
                mv_block->motion_type = XVMC_PREDICTION_DUAL_PRIME;
                if(s->picture_structure == PICT_FRAME){

                    mv_block->PMV[0][0][0] = s->mv[0][0][0];//top from top
                    mv_block->PMV[0][0][1] = s->mv[0][0][1]<<1;

                    mv_block->PMV[0][1][0] = s->mv[0][0][0];//bottom from bottom
                    mv_block->PMV[0][1][1] = s->mv[0][0][1]<<1;

                    mv_block->PMV[1][0][0] = s->mv[0][2][0];//dmv00, top from bottom
                    mv_block->PMV[1][0][1] = s->mv[0][2][1]<<1;//dmv01

                    mv_block->PMV[1][1][0] = s->mv[0][3][0];//dmv10, bottom from top
                    mv_block->PMV[1][1][1] = s->mv[0][3][1]<<1;//dmv11

                }else{
                    mv_block->PMV[0][1][0] = s->mv[0][2][0];//dmv00
                    mv_block->PMV[0][1][1] = s->mv[0][2][1];//dmv01
                }
                break;
            default:
                assert(0);
        }

        mv_block->motion_vertical_field_select = 0;

//set correct field referenses
        if(s->mv_type == MV_TYPE_FIELD || s->mv_type == MV_TYPE_16X8){
            if( s->field_select[0][0] ) mv_block->motion_vertical_field_select|=1;
            if( s->field_select[1][0] ) mv_block->motion_vertical_field_select|=2;
            if( s->field_select[0][1] ) mv_block->motion_vertical_field_select|=4;
            if( s->field_select[1][1] ) mv_block->motion_vertical_field_select|=8;
        }
    }//!intra
//time to handle data blocks;
    mv_block->index = render->next_free_data_block_num;
298

Ivan Kalvachev's avatar
Ivan Kalvachev committed
299
    blocks_per_mb = 6;
300
    if( s->chroma_format >= 2){
Ivan Kalvachev's avatar
Ivan Kalvachev committed
301
        blocks_per_mb = 4 + (1 << (s->chroma_format));
Ivan Kalvachev's avatar
Ivan Kalvachev committed
302
    }
303

Ivan Kalvachev's avatar
Ivan Kalvachev committed
304 305
    if(s->flags & CODEC_FLAG_GRAY){
        if(s->mb_intra){//intra frames are alwasy full chroma block
306 307 308 309 310 311
            for(i=4; i<blocks_per_mb; i++){
                memset(s->pblocks[i],0,sizeof(short)*8*8);//so we need to clear them
                if(!render->unsigned_intra)
                    s->pblocks[i][0] = 1<<10;
            }
        }else
Ivan Kalvachev's avatar
Ivan Kalvachev committed
312
            blocks_per_mb = 4;//Luminance blocks only
313
    }
314
    cbp = calc_cbp(s);
Ivan Kalvachev's avatar
Ivan Kalvachev committed
315 316 317 318 319 320 321 322
    mv_block->coded_block_pattern = cbp;
    if(cbp == 0)
        mv_block->macroblock_type &= ~XVMC_MB_TYPE_PATTERN;

    for(i=0; i<blocks_per_mb; i++){
        if(s->block_last_index[i] >= 0){
            // i do not have unsigned_intra MOCO to test, hope it is OK
            if( (s->mb_intra) && ( render->idct || (!render->idct && !render->unsigned_intra)) )
323
                s->pblocks[i][0]-=1<<10;
Ivan Kalvachev's avatar
Ivan Kalvachev committed
324
            if(!render->idct){
325
                s->dsp.idct(s->pblocks[i]);
Ivan Kalvachev's avatar
Ivan Kalvachev committed
326 327
                //!!TODO!clip!!!
            }
328 329 330 331 332 333 334 335 336 337 338 339 340
//copy blocks only if the codec doesn't support pblocks reordering
            if(s->avctx->xvmc_acceleration == 1){
                memcpy(&render->data_blocks[(render->next_free_data_block_num)*64],
                        s->pblocks[i],sizeof(short)*8*8);
            }else{
/*              if(s->pblocks[i] != &render->data_blocks[
                        (render->next_free_data_block_num)*64]){
                   printf("ERROR mb(%d,%d) s->pblocks[i]=%p data_block[]=%p\n",
                   s->mb_x,s->mb_y, s->pblocks[i], 
                   &render->data_blocks[(render->next_free_data_block_num)*64]);
                }*/
            }
            render->next_free_data_block_num++;
Ivan Kalvachev's avatar
Ivan Kalvachev committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        }
    }
    render->filled_mv_blocks_num++;

    assert(render->filled_mv_blocks_num <= render->total_number_of_mv_blocks);
    assert(render->next_free_data_block_num <= render->total_number_of_data_blocks);


    if(render->filled_mv_blocks_num >= render->total_number_of_mv_blocks)
        ff_draw_horiz_band(s,0,0);

// DumpRenderInfo(render);
// DumpMBlockInfo(mv_block);

}

#endif