view libmpcodecs/vd_libmpeg2.c @ 9076:92014b66ed3d

ability to disable the nonsense expand filter is a must! otherwise it's impossible to render subtitles earlier in the filter chain and then scale them down with a scale filter; huge subs will get rendered again on top!! (think dvd/vobsub where you can't just use smaller font size) if anyone has a better way to handle this, do it! (e.g. make it so that the first expand filter disabled osd for the rest of the filter chain)
author rfelker
date Fri, 24 Jan 2003 01:04:50 +0000
parents b465ba5897a3
children 16a7d1f4da38
line wrap: on
line source

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

#include "config.h"
#ifdef USE_LIBMPEG2

#include "mp_msg.h"

#include "vd_internal.h"

static vd_info_t info = 
{
	"MPEG 1/2 Video decoder v2.0",
	"libmpeg2",
	"A'rpi",
	"Aaron & Walken",
	"native"
};

LIBVD_EXTERN(libmpeg2)

#define USE_SIGJMP_TRICK

#ifdef USE_SIGJMP_TRICK
#include <signal.h>
#include <setjmp.h>
#endif

//#include "libmpdemux/parse_es.h"

#include "libvo/video_out.h"	// FIXME!!!

#include "libmpeg2/mpeg2.h"
#include "libmpeg2/mpeg2_internal.h"
#include "libmpeg2/mm_accel.h"

#include "../cpudetect.h"

mpeg2_config_t config;	// FIXME!!!
static picture_t *picture=NULL;	// exported from libmpeg2/decode.c

static int table_init_state=0;

// to set/get/query special features/parameters
static int control(sh_video_t *sh,int cmd,void* arg,...){
    return CONTROL_UNKNOWN;
}

static vo_frame_t frames[3];

// init driver
static int init(sh_video_t *sh){

    config.flags = 0;
if(gCpuCaps.hasMMX)
    config.flags |= MM_ACCEL_X86_MMX;
if(gCpuCaps.hasMMX2)
    config.flags |= MM_ACCEL_X86_MMXEXT;
if(gCpuCaps.has3DNow)
    config.flags |= MM_ACCEL_X86_3DNOW;
#ifdef HAVE_MLIB
    config.flags |= MM_ACCEL_MLIB;
#endif

    picture=malloc(sizeof(picture_t)); // !!! NEW HACK :) !!!
    memset(picture,0,sizeof(picture_t));
    header_state_init (picture);

    if(!table_init_state){
	idct_init ();
	motion_comp_init ();
	table_init_state=1;
    }

    picture->pp_options=divx_quality;
    
    memset(frames,0,3*sizeof(vo_frame_t));
    
    picture->forward_reference_frame=&frames[0];
    picture->backward_reference_frame=&frames[1];
    picture->temp_frame=&frames[2];
    picture->current_frame=NULL;
    
    // send seq header to the decoder:  *** HACK ***
//    mpeg2_decode_data(NULL,videobuffer,videobuffer+videobuf_len,0);
//    mpeg2_allocate_image_buffers (picture);
    return mpcodecs_config_vo(sh,sh->disp_w,sh->disp_h,IMGFMT_YV12);
}

// uninit driver
static void uninit(sh_video_t *sh){
//    mpeg2_free_image_buffers (picture);
}

static void draw_slice (vo_frame_t * frame, uint8_t ** src){
    int stride[3];
    int y=picture->slice<<4;

    stride[0]=picture->coded_picture_width;
    stride[1]=stride[2]=stride[0]/2;

    mpcodecs_draw_slice(frame->vo, src,
		stride, picture->display_picture_width,
		(y+16<=picture->display_picture_height) ? 16 :
		    picture->display_picture_height-y,
		0, y);
    
    ++picture->slice;
}

static int in_slice_flag=0; // FIXME! move to picture struct
static int drop_frame=0;    // FIXME! move to picture struct

static mp_image_t* parse_chunk (sh_video_t* sh, int code, uint8_t * buffer, int framedrop){
    mp_image_t* mpi=NULL;

//    stats_header (code, buffer);

    if (in_slice_flag && ((!code) || (code >= 0xb0))) {
	// ok, we've completed decoding a frame/field!
	in_slice_flag = 0;
	mpi=picture->display_frame->mpi;
	if(picture->picture_structure!=FRAME_PICTURE && !picture->second_field)
	    mpi=NULL; // we don't draw first fields!
    }

    switch (code) {
    case 0x00:	/* picture_start_code */
	if (header_process_picture_header (picture, buffer)) {
	    printf ("bad picture header\n");
	}
	drop_frame = framedrop && (picture->picture_coding_type == B_TYPE);
	drop_frame |= framedrop>=2; // hard drop
	break;

    case 0xb3:	/* sequence_header_code */
	if (header_process_sequence_header (picture, buffer)) {
	    printf ("bad sequence header\n");
	}
	break;

    case 0xb5:	/* extension_start_code */
	if (header_process_extension (picture, buffer)) {
	    printf ("bad extension\n");
	}
	break;

    default:
	if (code >= 0xb0)  break;

	if (!in_slice_flag) {
	    in_slice_flag = 1;

	    // set current_frame pointer:
	    if (!picture->second_field){
		mp_image_t* mpi;
		int flags;
		if (picture->picture_coding_type == B_TYPE){
		    flags=(!framedrop && vd_use_slices && 
			picture->picture_structure==FRAME_PICTURE) ?
			    MP_IMGFLAG_DRAW_CALLBACK:0;
		    picture->display_frame=
		    picture->current_frame = picture->temp_frame;
		} else {
		    flags=MP_IMGFLAG_PRESERVE|MP_IMGFLAG_READABLE;
		    picture->current_frame = picture->forward_reference_frame;
		    picture->display_frame=
		    picture->forward_reference_frame = picture->backward_reference_frame;
		    picture->backward_reference_frame = picture->current_frame;
		}
		mpi=mpcodecs_get_image(sh,MP_IMGTYPE_IPB, flags,
			picture->coded_picture_width,
			picture->coded_picture_height);
		// 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 :)
		    picture->current_frame->copy=draw_slice;
		} else
		    picture->current_frame->copy=NULL;
		// let's, setup pointers!
		picture->current_frame->base[0]=mpi->planes[0];
		picture->current_frame->base[1]=mpi->planes[1];
		picture->current_frame->base[2]=mpi->planes[2];
		picture->current_frame->mpi=mpi;	// tricky!
#if 1
		if(!picture->forward_reference_frame->base[0]){
		    // workaround for sig11
		    picture->forward_reference_frame->base[0]=mpi->planes[0];
		    picture->forward_reference_frame->base[1]=mpi->planes[1];
		    picture->forward_reference_frame->base[2]=mpi->planes[2];
		}
		if(!picture->backward_reference_frame->base[0]){
		    // workaround for sig11
		    picture->backward_reference_frame->base[0]=mpi->planes[0];
		    picture->backward_reference_frame->base[1]=mpi->planes[1];
		    picture->backward_reference_frame->base[2]=mpi->planes[2];
		}
#endif
#ifdef MPEG12_POSTPROC
		mpi->qscale=&picture->current_frame->quant_store[1][1];
		mpi->qstride=(MPEG2_MBC+1);
#endif
		mp_msg(MSGT_DECVIDEO,MSGL_DBG2,"mpeg2: [%c] %p  %s  \n",
		    (picture->picture_coding_type == B_TYPE) ? 'B':'P',
		    mpi, (mpi->flags&MP_IMGFLAG_DIRECT)?"DR!":"");
	    }

	    picture->current_frame->vo=sh;
	    picture->slice=0;

	}

	if (!drop_frame) {
	    slice_process (picture, code, buffer);
#ifdef ARCH_X86
	    if (config.flags & MM_ACCEL_X86_MMX) __asm__ __volatile__ ("emms");
#endif
	}

    }
    return mpi;
}

#ifdef USE_SIGJMP_TRICK

static jmp_buf mpeg2_jmp_buf;

static void mpeg2_sighandler(int sig){
    longjmp(mpeg2_jmp_buf,1);
}
#endif

// decode a frame
static mp_image_t* decode(sh_video_t *sh,void* data,int len,int flags){
    static uint32_t code;
    static uint8_t* pos;
    static uint8_t* current;
    uint8_t* end=data+len;
    static mp_image_t* mpi;
    mp_image_t* ret=NULL;
    int framedrop=flags&3;
    void* old_sigh;

    // Note: static is REQUIRED because of longjmp() may destroy stack!
    pos=NULL;
    current=data;
    mpi=NULL;

#ifdef USE_SIGJMP_TRICK
    old_sigh=signal(SIGSEGV,mpeg2_sighandler);
#endif

while(current<end){
  // FIND NEXT HEAD:
  static unsigned int head;
  static uint8_t c;
  head=-1;
  //--------------------
  while(current<end){
      c=current[0];
      ++current;
      head<<=8;
      if(head==0x100) break; // synced
      head|=c;
  }
  //--------------------
  if(pos){
#ifdef USE_SIGJMP_TRICK
    if(setjmp(mpeg2_jmp_buf)){
#ifdef ARCH_X86
	if (config.flags & MM_ACCEL_X86_MMX) __asm__ __volatile__ ("emms");
#endif
	printf("@@@ libmpeg2 returned from sig11... (bad file?) @@@\n");
    } else
#endif
    {
	ret=parse_chunk(sh, code&0xFF, pos, framedrop);
	if(ret) mpi=ret;
    }
  }
  //--------------------
  pos=current;code=head|c;
}

#ifdef USE_SIGJMP_TRICK
    signal(SIGSEGV,old_sigh); // restore sighandler
#endif

//    if(code==0x1FF){
	ret=parse_chunk(sh, 0xFF, NULL, framedrop); // send 'end of frame'
	if(ret) mpi=ret;
//    }

    if(mpi){
        static int last_non_b_type= 0;

	if(picture->picture_coding_type == B_TYPE)
	    mpi->pict_type= B_TYPE;
	else{
	    mpi->pict_type= last_non_b_type;
	    last_non_b_type= picture->picture_coding_type;
	}
    }

    return mpi;
}
#endif