view libmpcodecs/vd_ffmpeg.c @ 7256:ec6dd0a29d93

Add the code that chooses a good X11 truecolor visual to the vo_x11 config() function. This is useful for framebuffers on Sun hardware, where we have multiple truecolor visuals of different depths available, and the root window typically runs at depth 8, yet there are 24 bit true color visuals available as well.
author jkeil
date Tue, 03 Sep 2002 18:10:42 +0000
parents e3ecccc7e505
children e805ef05536c
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

#include "config.h"
#include "mp_msg.h"
#include "help_mp.h"

#ifdef USE_LIBAVCODEC

#include "bswap.h"

#include "vd_internal.h"

static vd_info_t info = {
	"FFmpeg's libavcodec codec family",
	"ffmpeg",
	"A'rpi",
	"http://ffmpeg.sf.net",
	"native codecs"
};

LIBVD_EXTERN(ffmpeg)

#include "../postproc/rgb2rgb.h"


#ifdef USE_LIBAVCODEC_SO
#include <ffmpeg/avcodec.h>
#else
#include "libavcodec/avcodec.h"
#endif

int avcodec_inited=0;

#ifdef FF_POSTPROCESS
int quant_store[MBR+1][MBC+1];
#endif

typedef struct {
    AVCodecContext *avctx;
    int last_aspect;
    int do_slices;
    int do_dr1;
    int vo_inited;
    int convert;
    int best_csp;
} vd_ffmpeg_ctx;

//#ifdef FF_POSTPROCESS
//unsigned int lavc_pp=0;
//#endif

#include "cfgparser.h"

static void get_buffer(struct AVCodecContext *avctx, int width, int height, int pict_type);

static int lavc_param_workaround_bugs=0;
static int lavc_param_error_resilience=-1;
static int lavc_param_gray=0;
static int lavc_param_vstats=0;

struct config lavc_decode_opts_conf[]={
#if LIBAVCODEC_BUILD >= 4611
	{"bug", &lavc_param_workaround_bugs, CONF_TYPE_INT, CONF_RANGE, 0, 99, NULL},
	{"ver", &lavc_param_error_resilience, CONF_TYPE_INT, CONF_RANGE, -1, 99, NULL},
#endif
#if LIBAVCODEC_BUILD >= 4614
	{"gray", &lavc_param_gray, CONF_TYPE_FLAG, 0, 0, CODEC_FLAG_PART, NULL},
#endif
	{"vstats", &lavc_param_vstats, CONF_TYPE_FLAG, 0, 0, 1, NULL},
	{NULL, NULL, 0, 0, 0, 0, NULL}
};

// to set/get/query special features/parameters
static int control(sh_video_t *sh,int cmd,void* arg,...){
    vd_ffmpeg_ctx *ctx = sh->context;
    AVCodecContext *avctx = ctx->avctx;
    switch(cmd){
    case VDCTRL_QUERY_FORMAT:
	if( (*((int*)arg)) == ctx->best_csp ) return CONTROL_TRUE;//supported
	// possible conversions:
	switch( (*((int*)arg)) ){
        case IMGFMT_YV12:
        case IMGFMT_IYUV:
        case IMGFMT_I420:
	    // "converted" using pointer/stride modification
	    if(avctx->pix_fmt==PIX_FMT_YUV420P) return CONTROL_TRUE;// u/v swap
	    if(avctx->pix_fmt==PIX_FMT_YUV422P) return CONTROL_TRUE;// half stride
	    break;
#if 1
        case IMGFMT_YUY2:
	    // converted using yuv422ptoyuy2()
	    if(avctx->pix_fmt==PIX_FMT_YUV422P) return CONTROL_TRUE;
	    break;
	}
#endif
        return CONTROL_FALSE;
    }
    return CONTROL_UNKNOWN;
}

// init driver
static int init(sh_video_t *sh){
    AVCodecContext *avctx;
    vd_ffmpeg_ctx *ctx;
    AVCodec *lavc_codec;

    if(!avcodec_inited){
      avcodec_init();
      avcodec_register_all();
      avcodec_inited=1;
    }

    ctx = sh->context = malloc(sizeof(vd_ffmpeg_ctx));
    if (!ctx)
	return(0);
    memset(ctx, 0, sizeof(vd_ffmpeg_ctx));
    
    lavc_codec = (AVCodec *)avcodec_find_decoder_by_name(sh->codec->dll);
    if(!lavc_codec){
	mp_msg(MSGT_DECVIDEO,MSGL_ERR,MSGTR_MissingLAVCcodec,sh->codec->dll);
	return 0;
    }

    if(vd_use_slices && lavc_codec->capabilities&CODEC_CAP_DRAW_HORIZ_BAND)
	ctx->do_slices=1;
 
#if LIBAVCODEC_BUILD > 4615
    if(lavc_codec->capabilities&CODEC_CAP_DR1)
	ctx->do_dr1=1;
#endif
            
    ctx->avctx = malloc(sizeof(AVCodecContext));
    memset(ctx->avctx, 0, sizeof(AVCodecContext));
    avctx = ctx->avctx;

#if LIBAVCODEC_BUILD > 4615
    if(ctx->do_dr1){
        avctx->flags|= CODEC_FLAG_EMU_EDGE | CODEC_FLAG_DR1; 
        avctx->get_buffer_callback= get_buffer;
    }
#endif
    
    avctx->width = sh->disp_w;
    avctx->height= sh->disp_h;
#if LIBAVCODEC_BUILD >= 4611
    avctx->workaround_bugs= lavc_param_workaround_bugs;
    avctx->error_resilience= lavc_param_error_resilience;
#endif
#if LIBAVCODEC_BUILD >= 4614
    if(lavc_param_gray) avctx->flags|= CODEC_FLAG_GRAY;
#endif
    
    mp_dbg(MSGT_DECVIDEO,MSGL_DBG2,"libavcodec.size: %d x %d\n",avctx->width,avctx->height);
    if (sh->format == mmioFOURCC('R', 'V', '1', '3'))
	avctx->sub_id = 3;
#if LIBAVCODEC_BUILD >= 4605
    /* AVRn stores huffman table in AVI header */
    /* Pegasus MJPEG stores it also in AVI header, but it uses the common
       MJPG fourcc :( */
    if (sh->bih && (sh->bih->biSize != sizeof(BITMAPINFOHEADER)) &&
	(sh->format == mmioFOURCC('A','V','R','n') ||
	sh->format == mmioFOURCC('M','J','P','G')))
    {
	avctx->flags |= CODEC_FLAG_EXTERN_HUFF;
	avctx->extradata_size = sh->bih->biSize-sizeof(BITMAPINFOHEADER);
	avctx->extradata = malloc(avctx->extradata_size);
	memcpy(avctx->extradata, sh->bih+sizeof(BITMAPINFOHEADER),
	    avctx->extradata_size);

#if 0
	{
	    int x;
	    uint8_t *p = avctx->extradata;
	    
	    for (x=0; x<avctx->extradata_size; x++)
		printf("[%x] ", p[x]);
	    printf("\n");
	}
#endif
    }
#endif
    if(   sh->format == mmioFOURCC('R', 'V', '1', '0')
       || sh->format == mmioFOURCC('R', 'V', '1', '3')){
        unsigned int* extrahdr=(unsigned int*)(sh->bih+1);
        avctx->extradata_size= 8;
	avctx->extradata = malloc(avctx->extradata_size);
        ((uint32_t*)avctx->extradata)[0] = extrahdr[0];
        ((uint32_t*)avctx->extradata)[1] = extrahdr[1];
//        printf("%X %X %d %d\n", extrahdr[0], extrahdr[1]);
    }

    /* open it */
    if (avcodec_open(avctx, lavc_codec) < 0) {
        mp_msg(MSGT_DECVIDEO,MSGL_ERR, MSGTR_CantOpenCodec);
        return 0;
    }
    mp_msg(MSGT_DECVIDEO,MSGL_V,"INFO: libavcodec init OK!\n");
    ctx->last_aspect=-3;
    return 1; //mpcodecs_config_vo(sh,sh->disp_w,sh->disp_h,IMGFMT_YV12);
}

// uninit driver
static void uninit(sh_video_t *sh){
    vd_ffmpeg_ctx *ctx = sh->context;
    AVCodecContext *avctx = ctx->avctx;

    if (avcodec_close(avctx) < 0)
    	    mp_msg(MSGT_DECVIDEO,MSGL_ERR, MSGTR_CantCloseCodec);

#if LIBAVCODEC_BUILD >= 4605
    if (avctx->extradata_size)
	free(avctx->extradata);
#endif

    if (avctx)
	free(avctx);
    if (ctx)
	free(ctx);
}

static void draw_slice(struct AVCodecContext *s,
                	UINT8 **src, int linesize,
                	int y, int width, int height){
    sh_video_t * sh = s->opaque;
    int stride[3];
    int start=0, i;
    int skip_stride= (s->width+15)>>4;
#if LIBAVCODEC_BUILD > 4615
    UINT8 *skip= &s->mbskip_table[(y>>4)*skip_stride];
    int threshold= s->pict_type==B_TYPE ? -99 : s->dr_ip_buffer_count;
#endif

    stride[0]=linesize;
#if LIBAVCODEC_BUILD > 4615
    if(s->dr_uvstride)
        stride[1]=stride[2]= s->dr_uvstride;
    else
#endif
        stride[1]=stride[2]=stride[0]/2;
#if 0
    if(s->pict_type!=B_TYPE){
        for(i=0; i*16<width+16; i++){ 
            if(i*16>=width || skip[i]>=threshold){
                if(start==i) start++;
                else{
                    UINT8 *src2[3]= {src[0] + start*16, 
                                     src[1] + start*8, 
                                     src[2] + start*8};
//printf("%2d-%2d x %d\n", start, i, y);
                    mpcodecs_draw_slice (sh,src2, stride, (i-start)*16, height, start*16, y);
                    start= i+1;
                }
            }       
        }
    }else
#endif
        mpcodecs_draw_slice (sh,src, stride, width, height, 0, y);
}

static int init_vo(sh_video_t *sh){
    vd_ffmpeg_ctx *ctx = sh->context;
    AVCodecContext *avctx = ctx->avctx;

    if (avctx->aspect_ratio_info != ctx->last_aspect ||
	avctx->width != sh->disp_w ||
	avctx->height != sh->disp_h ||
	!ctx->vo_inited)
    {
	ctx->last_aspect = avctx->aspect_ratio_info;
	switch(avctx->aspect_ratio_info)
	{
	    case FF_ASPECT_4_3_625:
	    case FF_ASPECT_4_3_525:
		sh->aspect = 4.0/3.0;
		break;
	    case FF_ASPECT_16_9_625:
	    case FF_ASPECT_16_9_525:
		sh->aspect = 16.0/9.0;
		break;
	    case FF_ASPECT_SQUARE:
		sh->aspect = 0.0;
		break;
	}
	sh->disp_w = avctx->width;
	sh->disp_h = avctx->height;
	ctx->vo_inited=1;
	switch(avctx->pix_fmt){
#if LIBAVCODEC_BUILD >= 4615
	case PIX_FMT_YUV410P: ctx->best_csp=IMGFMT_YVU9;break; //svq1
#endif
	case PIX_FMT_YUV420P: ctx->best_csp=IMGFMT_YV12;break; //mpegs
	case PIX_FMT_YUV422P: ctx->best_csp=IMGFMT_422P;break; //mjpeg
	case PIX_FMT_YUV444P: ctx->best_csp=IMGFMT_444P;break; //???
	case PIX_FMT_YUV422:  ctx->best_csp=IMGFMT_YUY2;break; //???
	default:
	    ctx->best_csp=0;
	}
    	if (!mpcodecs_config_vo(sh,sh->disp_w,sh->disp_h, ctx->best_csp))
    		return -1;
	ctx->convert=(sh->codec->outfmt[sh->outfmtidx]==IMGFMT_YUY2
	    && ctx->best_csp!=IMGFMT_YUY2); // yuv422p->yuy2 conversion
    }
    return 0;
}

#if LIBAVCODEC_BUILD > 4615
static void get_buffer(struct AVCodecContext *avctx, int width, int height, int pict_type){
    sh_video_t * sh = avctx->opaque;
    vd_ffmpeg_ctx *ctx = sh->context;
    mp_image_t* mpi=NULL;
    int flags= MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE;
    int type= MP_IMGTYPE_IPB;
    int align=15;

    if(avctx->pix_fmt == PIX_FMT_YUV410P)
        align=63; //yes seriously, its really needed (16x16 chroma blocks in SVQ1 -> 64x64)

    if(init_vo(sh)<0){
        printf("init_vo failed\n");
        return;
    }

    if(pict_type==B_TYPE)
        flags|=(!avctx->hurry_up && ctx->do_slices) ?
                MP_IMGFLAG_DRAW_CALLBACK:0;
    else
        flags|= MP_IMGFLAG_PRESERVE|MP_IMGFLAG_READABLE
                | (ctx->do_slices ? MP_IMGFLAG_DRAW_CALLBACK : 0);

#if LIBAVCODEC_BUILD > 4616
    if(avctx->has_b_frames){
        type= MP_IMGTYPE_IPB;
    }else{
        type= MP_IMGTYPE_IP;
    }
#endif
    mp_msg(MSGT_DECVIDEO,MSGL_DBG2, type== MP_IMGTYPE_IPB ? "using IPB\n" : "using IP\n");

    mpi= mpcodecs_get_image(sh,type, flags,
			(width+align)&(~align), (height+align)&(~align));

    // ok, lets see what did we get:
    if(  mpi->flags&MP_IMGFLAG_DRAW_CALLBACK &&
       !(mpi->flags&MP_IMGFLAG_DIRECT)){
	// nice, filter/vo likes draw_callback :)
	avctx->draw_horiz_band= draw_slice;
    } else
	avctx->draw_horiz_band= NULL;
    avctx->dr_buffer[0]= mpi->planes[0];
    avctx->dr_buffer[1]= mpi->planes[1];
    avctx->dr_buffer[2]= mpi->planes[2];
    
    if(avctx->dr_stride && avctx->dr_stride !=mpi->stride[0]){
        mp_msg(MSGT_DECVIDEO,MSGL_ERR, "Error: stride changed\n");
    }
    
    if(avctx->dr_uvstride && avctx->dr_uvstride !=mpi->stride[1]){
        mp_msg(MSGT_DECVIDEO,MSGL_ERR, "Error: uvstride changed\n");
    }
    
    assert(mpi->width  >= ((width +align)&(~align)));   
    assert(mpi->height >= ((height+align)&(~align)));
    assert(mpi->stride[0] >= mpi->width);
    if(mpi->imgfmt==IMGFMT_I420 || mpi->imgfmt==IMGFMT_YV12 || mpi->imgfmt==IMGFMT_IYUV){
        const int y_size= mpi->stride[0] * mpi->height;
        const int c_size= mpi->stride[1] * mpi->chroma_height;
        
        assert(mpi->planes[0] > mpi->planes[1] || mpi->planes[0] + y_size <= mpi->planes[1]);
        assert(mpi->planes[0] > mpi->planes[2] || mpi->planes[0] + y_size <= mpi->planes[2]);
        assert(mpi->planes[1] > mpi->planes[0] || mpi->planes[1] + c_size <= mpi->planes[0]);
        assert(mpi->planes[1] > mpi->planes[2] || mpi->planes[1] + c_size <= mpi->planes[2]);
        assert(mpi->planes[2] > mpi->planes[0] || mpi->planes[2] + c_size <= mpi->planes[0]);
        assert(mpi->planes[2] > mpi->planes[1] || mpi->planes[2] + c_size <= mpi->planes[1]);
    }

    avctx->dr_stride   = mpi->stride[0];
    avctx->dr_uvstride = mpi->stride[1];

    avctx->dr_opaque_frame = mpi;
    avctx->dr_ip_buffer_count=2; //FIXME
//printf("%X\n", (int)mpi->planes[0]);
#if 0
if(mpi->flags&MP_IMGFLAG_DIRECT)
    printf("D");
else if(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)
    printf("S");
else
    printf(".");
#endif
}
#endif

// decode a frame
static mp_image_t* decode(sh_video_t *sh,void* data,int len,int flags){
    int got_picture=0;
    int ret;
    AVPicture lavc_picture;
    vd_ffmpeg_ctx *ctx = sh->context;
    AVCodecContext *avctx = ctx->avctx;
    mp_image_t* mpi=NULL;
    int dr1= ctx->do_dr1;

    if(len<=0) return NULL; // skipped frame

    avctx->draw_horiz_band=NULL;
    avctx->opaque=sh;
    if(ctx->vo_inited && !ctx->convert && !(flags&3) && !dr1){
	mpi=mpcodecs_get_image(sh, MP_IMGTYPE_EXPORT, MP_IMGFLAG_PRESERVE |
	    (ctx->do_slices?MP_IMGFLAG_DRAW_CALLBACK:0),
	    sh->disp_w, sh->disp_h);
	if(mpi && mpi->flags&MP_IMGFLAG_DRAW_CALLBACK){
	    // vd core likes slices!
	    avctx->draw_horiz_band=draw_slice;
	}
    }

#if LIBAVCODEC_BUILD > 4603
    avctx->hurry_up=(flags&3)?((flags&2)?2:1):0;
#endif

    ret = avcodec_decode_video(avctx, &lavc_picture,
	     &got_picture, data, len);
    if(ret<0) mp_msg(MSGT_DECVIDEO,MSGL_WARN, "Error while decoding frame!\n");

//-- vstats generation
    while(lavc_param_vstats){ // always one time loop
        static FILE *fvstats=NULL;
        char filename[20];
        static long long int all_len=0;
        static int frame_number=0;
        static double all_frametime=0.0;

        if(!fvstats) {
            time_t today2;
            struct tm *today;
            today2 = time(NULL);
            today = localtime(&today2);
            sprintf(filename, "vstats_%02d%02d%02d.log", today->tm_hour,
                today->tm_min, today->tm_sec);
            fvstats = fopen(filename,"w");
            if(!fvstats) {
                perror("fopen");
                lavc_param_vstats=0; // disable block
                break;
                /*exit(1);*/
            }
        }

        all_len+=len;
        all_frametime+=sh->frametime;
        fprintf(fvstats, "frame= %5d q= %2d f_size= %6d s_size= %8.0fkB ",
            ++frame_number, avctx->quality, len, (double)all_len/1024);
        fprintf(fvstats, "time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ",
           all_frametime, (double)(len*8)/sh->frametime/1000.0,
           (double)(all_len*8)/all_frametime/1000.0);
        fprintf(fvstats, "type= %c\n", sh->ds->flags&1 ? 'I' : 'P');
        break;
    }
//--

    if(!got_picture) return NULL;	// skipped image

    if(init_vo(sh)<0) return NULL;

#if LIBAVCODEC_BUILD > 4615
    if(dr1 && avctx->dr_opaque_frame){
        mpi= (mp_image_t*)avctx->dr_opaque_frame;
    }
#endif
        
    if(!mpi && ctx->convert){
	// do yuv422p -> yuy2 conversion:
        mpi=mpcodecs_get_image(sh, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE,
	    avctx->width, avctx->height);
	if(!mpi) return NULL;
	yuv422ptoyuy2(lavc_picture.data[0],lavc_picture.data[1],lavc_picture.data[2],
	    mpi->planes[0],avctx->width,avctx->height,
	    lavc_picture.linesize[0],lavc_picture.linesize[1],mpi->stride[0]);
	return mpi;
    }
    
    if(!mpi)
    mpi=mpcodecs_get_image(sh, MP_IMGTYPE_EXPORT, MP_IMGFLAG_PRESERVE,
	avctx->width, avctx->height);
    if(!mpi){	// temporary!
	printf("couldn't allocate image for codec\n");
	return NULL;
    }
    
    if(!dr1){
        mpi->planes[0]=lavc_picture.data[0];
        mpi->planes[1]=lavc_picture.data[1];
        mpi->planes[2]=lavc_picture.data[2];
        mpi->stride[0]=lavc_picture.linesize[0];
        mpi->stride[1]=lavc_picture.linesize[1];
        mpi->stride[2]=lavc_picture.linesize[2];
    }

    if(avctx->pix_fmt==PIX_FMT_YUV422P && mpi->chroma_y_shift==1){
	// we have 422p but user wants 420p
	mpi->stride[1]*=2;
	mpi->stride[2]*=2;
    }
    
/* to comfirm with newer lavc style */
#if !defined(FF_POSTPROCESS) && (LIBAVCODEC_BUILD > 4612)
    mpi->qscale=avctx->quant_store;
#if LIBAVCODEC_BUILD > 4613
    mpi->qstride=avctx->qstride;
#else
    mpi->qstride=MBC+1;
#endif
#elif defined(FF_POSTPROCESS)
    mpi->qscale=&quant_store[0][0];
    mpi->qstride=MBC+1;
#endif
    
    return mpi;
}

#endif