initial XvMC support
authorIvan Kalvachev <ikalvachev@gmail.com>
Sat, 26 Jul 2003 01:28:49 +0000 (01:28 +0000)
committerIvan Kalvachev <ikalvachev@gmail.com>
Sat, 26 Jul 2003 01:28:49 +0000 (01:28 +0000)
Originally committed as revision 2085 to svn://svn.ffmpeg.org/ffmpeg/trunk

libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/error_resilience.c
libavcodec/mpeg12.c
libavcodec/mpegvideo.c
libavcodec/xvmcvideo.c [new file with mode: 0644]

index 894c53a..49c2b62 100644 (file)
@@ -14,7 +14,7 @@ OBJS= common.o utils.o mem.o allcodecs.o \
       mpegvideo.o jrevdct.o jfdctfst.o jfdctint.o\
       mpegaudio.o ac3enc.o mjpeg.o resample.o dsputil.o \
       motion_est.o imgconvert.o imgresample.o \
-      mpeg12.o mpegaudiodec.o pcm.o simple_idct.o \
+      mpeg12.o xvmcvideo.o mpegaudiodec.o pcm.o simple_idct.o \
       ratecontrol.o adpcm.o eval.o dv.o error_resilience.o \
       fft.o mdct.o mace.o huffyuv.o cyuv.o opts.o raw.o h264.o golomb.o \
       vp3.o asv1.o 4xm.o cabac.o ffv1.o ra144.o ra288.o vcr1.o
index 43320e6..c364f22 100644 (file)
@@ -96,6 +96,9 @@ void avcodec_register_all(void)
 #endif
 #endif
     register_avcodec(&mpeg_decoder);
+#ifdef HAVE_XVMC
+    register_avcodec(&mpeg_xvmc_decoder);
+#endif
     register_avcodec(&dvvideo_decoder);
     register_avcodec(&dvaudio_decoder);
     register_avcodec(&mjpeg_decoder);
index 4ac92bf..fd7eb37 100644 (file)
@@ -894,6 +894,10 @@ void ff_er_frame_end(MpegEncContext *s){
     }else
         guess_mv(s);
 
+#ifdef HAVE_XVMC
+    /* the filters below are not XvMC compatible, skip them */
+    if(s->avctx->xvmc_acceleration) goto ec_clean;
+#endif
     /* fill DC for inter blocks */
     for(mb_y=0; mb_y<s->mb_height; mb_y++){
         for(mb_x=0; mb_x<s->mb_width; mb_x++){
@@ -979,6 +983,7 @@ void ff_er_frame_end(MpegEncContext *s){
         v_block_filter(s, s->current_picture.data[2], s->mb_width  , s->mb_height  , s->uvlinesize, 0);
     }
 
+ec_clean:
     /* clean a few tables */
     for(i=0; i<s->mb_num; i++){
         const int mb_xy= s->mb_index2xy[i];
index 62734b4..4f007ca 100644 (file)
@@ -67,6 +67,11 @@ static inline int mpeg2_decode_block_intra(MpegEncContext *s,
                                     int n);
 static int mpeg_decode_motion(MpegEncContext *s, int fcode, int pred);
 
+#ifdef HAVE_XVMC
+extern int XVMC_field_start(MpegEncContext *s, AVCodecContext *avctx);
+extern int XVMC_field_end(MpegEncContext *s);
+#endif
+
 #ifdef CONFIG_ENCODERS
 static uint8_t (*mv_penalty)[MAX_MV*2+1]= NULL;
 static uint8_t fcode_tab[MAX_MV*2+1];
@@ -1875,7 +1880,13 @@ static int mpeg_decode_slice(AVCodecContext *avctx,
                 } 
             }
       }
-    }
+#ifdef HAVE_XVMC
+// MPV_frame_start will call this function too,
+// but we need to call it on every field
+      if(s->avctx->xvmc_acceleration)
+         XVMC_field_start(s,avctx);
+#endif
+    }//fi(s->first_slice)
     s->first_slice = 0;
 
     init_get_bits(&s->gb, *buf, buf_size*8);
@@ -2020,6 +2031,10 @@ static int slice_end(AVCodecContext *avctx, AVFrame *pict)
     if (!s1->mpeg_enc_ctx_allocated)
         return 0;
 
+#ifdef HAVE_XVMC
+    if(s->avctx->xvmc_acceleration)
+        XVMC_field_end(s);
+#endif
     /* end of slice reached */
     if (/*s->mb_y<<field_pic == s->mb_height &&*/ !s->first_field) {
         /* end of image */
@@ -2103,6 +2118,11 @@ static int mpeg1_decode_sequence(AVCodecContext *avctx,
             );
         avctx->bit_rate = s->bit_rate;
         
+        //get_format() or set_video(width,height,aspect,pix_fmt);
+        //until then pix_fmt may be changed right after codec init
+        if( avctx->pix_fmt == PIX_FMT_XVMC_MPEG2_IDCT )
+            avctx->idct_algo = FF_IDCT_SIMPLE;
+
         if (MPV_common_init(s) < 0)
             return -1;
         s1->mpeg_enc_ctx_allocated = 1;
@@ -2181,6 +2201,11 @@ static int vcr2_init_sequence(AVCodecContext *avctx)
     avctx->has_b_frames= 0; //true?
     s->low_delay= 1;
     s->avctx = avctx;
+
+    //get_format() or set_video(width,height,aspect,pix_fmt);
+    //until then pix_fmt may be changed right after codec init
+    if( avctx->pix_fmt == PIX_FMT_XVMC_MPEG2_IDCT )
+        avctx->idct_algo = FF_IDCT_SIMPLE;
     
     if (MPV_common_init(s) < 0)
         return -1;
@@ -2414,3 +2439,35 @@ AVCodec mpeg_decoder = {
     CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_DR1 | CODEC_CAP_TRUNCATED,
     .flush= ff_mpeg_flush,
 };
+
+#ifdef HAVE_XVMC
+static int mpeg_mc_decode_init(AVCodecContext *avctx){
+    Mpeg1Context *s;
+
+    if( !(avctx->slice_flags & SLICE_FLAG_CODED_ORDER) )
+        return -1;
+    if( !(avctx->slice_flags & SLICE_FLAG_ALLOW_FIELD) )
+        dprintf("mpeg12.c: XvMC decoder will work better if SLICE_FLAG_ALLOW_FIELD is set\n");
+
+    mpeg_decode_init(avctx);
+    s = avctx->priv_data;
+
+    avctx->pix_fmt = PIX_FMT_XVMC_MPEG2_IDCT;
+    avctx->xvmc_acceleration = 1;
+
+    return 0;
+}
+
+AVCodec mpeg_xvmc_decoder = {
+    "mpegvideo_xvmc",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_MPEG2VIDEO_XVMC,
+    sizeof(Mpeg1Context),
+    mpeg_mc_decode_init,
+    NULL,
+    mpeg_decode_end,
+    mpeg_decode_frame,
+    CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_DR1 | CODEC_CAP_TRUNCATED,
+};
+
+#endif
index 3c11646..737cd0d 100644 (file)
@@ -52,6 +52,12 @@ static int dct_quantize_c(MpegEncContext *s, DCTELEM *block, int n, int qscale,
 static int dct_quantize_trellis_c(MpegEncContext *s, DCTELEM *block, int n, int qscale, int *overflow);
 #endif //CONFIG_ENCODERS
 
+#ifdef HAVE_XVMC
+extern int  XVMC_field_start(MpegEncContext*s, AVCodecContext *avctx);
+extern void XVMC_field_end(MpegEncContext *s);
+extern void XVMC_decode_mb(MpegEncContext *s, DCTELEM block[6][64]);
+#endif
+
 void (*draw_edges)(uint8_t *buf, int wrap, int width, int height, int w)= draw_edges_c;
 
 
@@ -1028,6 +1034,10 @@ alloc:
     }else 
         s->dct_unquantize = s->dct_unquantize_mpeg1;
 
+#ifdef HAVE_XVMC
+    if(s->avctx->xvmc_acceleration)
+        return XVMC_field_start(s, avctx);
+#endif
     return 0;
 }
 
@@ -1036,6 +1046,12 @@ void MPV_frame_end(MpegEncContext *s)
 {
     int i;
     /* draw edge for correct motion prediction if outside */
+#ifdef HAVE_XVMC
+//just to make sure that all data is rendered.
+    if(s->avctx->xvmc_acceleration){
+        XVMC_field_end(s);
+    }else
+#endif
     if(s->codec_id!=CODEC_ID_SVQ1 && s->codec_id != CODEC_ID_MPEG1VIDEO){
         if (s->pict_type != B_TYPE && !s->intra_only && !(s->flags&CODEC_FLAG_EMU_EDGE)) {
             draw_edges(s->current_picture.data[0], s->linesize  , s->h_edge_pos   , s->v_edge_pos   , EDGE_WIDTH  );
@@ -2382,6 +2398,12 @@ void MPV_decode_mb(MpegEncContext *s, DCTELEM block[6][64])
 {
     int mb_x, mb_y;
     const int mb_xy = s->mb_y * s->mb_stride + s->mb_x;
+#ifdef HAVE_XVMC
+    if(s->avctx->xvmc_acceleration){
+        XVMC_decode_mb(s,block);
+        return;
+    }
+#endif
 
     mb_x = s->mb_x;
     mb_y = s->mb_y;
diff --git a/libavcodec/xvmcvideo.c b/libavcodec/xvmcvideo.c
new file mode 100644 (file)
index 0000000..d6aaee4
--- /dev/null
@@ -0,0 +1,267 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <limits.h>
+
+//X11 include
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+#include <X11/extensions/XvMClib.h>
+
+#include "xvmc_render.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
+//#include "xvmc_debug.h"
+
+static int calc_cbp(MpegEncContext *s, int blocknum){
+/* compute cbp */
+// for I420 bit_offset=5
+int  i,cbp = 0;
+    for(i=0; i<blocknum; i++) {
+        if(s->block_last_index[i] >= 0)
+            cbp |= 1 << (5 - i);
+    }
+    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);
+    render = (xvmc_render_state_t*)s->current_picture.data[2];
+    assert(render != NULL);
+
+    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);
+            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
+            assert(last->state & MP_XVMC_STATE_PREDICTION);
+            render->p_past_surface = last->p_surface;
+            return 0;
+     }
+
+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);
+    }
+}
+
+void XVMC_decode_mb(MpegEncContext *s, DCTELEM block[6][64]){
+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){
+        fprintf(stderr,"XVMC doesn't support encoding!!!\n");
+        assert(0);
+        return;
+    }
+
+   //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);
+    //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;
+    blocks_per_mb = 6;
+/*
+    switch( s->chroma_format){
+        case CHROMA_422:
+            blocks_per_mb = 8;
+            break;
+        case CHROMA_444:
+            blocks_per_mb = 12;
+            break;
+    }
+*/
+    if(s->flags & CODEC_FLAG_GRAY){
+        if(s->mb_intra){//intra frames are alwasy full chroma block
+            memset(block[4],0,sizeof(short)*8*8);//so we need to clear them
+            memset(block[5],0,sizeof(short)*8*8);
+            if(!render->unsigned_intra)
+                block[4][0] = block[5][0] = 1<<10;
+        }
+        else
+            blocks_per_mb = 4;//Luminance blocks only
+    };
+    cbp = calc_cbp(s,blocks_per_mb);
+    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)) )
+                block[i][0]-=1<<10;
+            if(!render->idct){
+                s->dsp.idct(block[i]);
+                //!!TODO!clip!!!
+            }
+//TODO:avoid block copy by modifying s->block pointer
+            memcpy(&render->data_blocks[(render->next_free_data_block_num++)*64],
+                    block[i],sizeof(short)*8*8);
+        }
+    }
+    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