view libvo/vo_dxr2.c @ 37158:08bbd1e9036d

vd_ffmpeg: Rewrite ticket reference in comment Omit the issue tracking software's name. Despite the migration from Bugzilla to Trac we were able to keep the ticket numbers.
author al
date Fri, 15 Aug 2014 22:27:52 +0000
parents 05efbb5ab47e
children
line wrap: on
line source

/*
 * This file is part of MPlayer.
 *
 * MPlayer is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * MPlayer 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "fastmemcpy.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>

#include "config.h"
#include "aspect.h"
#include "video_out.h"
#define NO_DRAW_SLICE
#include "video_out_internal.h"
#include "mp_msg.h"
#include "m_option.h"
#include "sub/sub.h"
#include "path.h"
#include "libmpcodecs/vd.h"
#include "libmpdemux/mpeg_packetizer.h"
#include "x11_common.h"
#include "libvo/vo_dxr2.h"

#include <dxr2ioctl.h>


int dxr2_fd = -1;

static int movie_w,movie_h;
static int playing = 0;

// vo device used to blank the screen for the overlay init
static const vo_functions_t* sub_vo = NULL;

static uint8_t* sub_img = NULL;
static int sub_x,sub_y,sub_w,sub_h;
static int sub_x_off,sub_y_off;
static int sub_config_count;
static int dxr2_aspect;
static int sub_vo_win = 0;

static int use_ol = 1;
static int ol_ratio = 1000;
static char *norm = NULL;
static char *ucode = NULL;
static int ar_mode = DXR2_ASPECTRATIOMODE_LETTERBOX;
static int mv_mode = DXR2_MACROVISION_OFF;
static int _75ire_mode = DXR2_75IRE_OFF;
static int bw_mode = DXR2_BLACKWHITE_OFF;
static int interlaced_mode = DXR2_INTERLACED_ON;
static int pixel_mode = DXR2_PIXEL_CCIR601;
static int iec958_mode = DXR2_IEC958_DECODED;
static int mute_mode = DXR2_AUDIO_MUTE_OFF;
static int ignore_cache = 0;
static int update_cache = 0;
static int olw_cor = 0, olh_cor = 0,olx_cor = 0, oly_cor = 0;
static int ol_osd = 0;
static int ck_rmin = 0x40;
static int ck_rmax = 0xFF;
static int ck_r = 0xFF;
static int ck_gmin = 0x00;
static int ck_gmax = 0x20;
static int ck_g = 0;
static int ck_bmin = 0x40;
static int ck_bmax = 0xFF;
static int ck_b = 0xFF;
static int cr_left = 0, cr_right = 0;
static int cr_top = 55, cr_bot = 300;

const m_option_t dxr2_opts[] = {
  { "overlay", &use_ol, CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "nooverlay", &use_ol, CONF_TYPE_FLAG, 0, 1, 0, NULL},
  { "overlay-ratio", &ol_ratio, CONF_TYPE_INT, CONF_RANGE, 1, 2500, NULL },
  { "ucode", &ucode, CONF_TYPE_STRING,0, 0, 0, NULL},

  { "norm", &norm, CONF_TYPE_STRING,0, 0, 0, NULL},

  { "ar-mode",&ar_mode,  CONF_TYPE_INT, CONF_RANGE,0,2,NULL },

  { "macrovision",&mv_mode,CONF_TYPE_INT,CONF_RANGE,0,3, NULL },

  { "75ire",&_75ire_mode,CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "no75ire",&_75ire_mode,CONF_TYPE_FLAG, 0, 1, 0, NULL},

  { "bw",&bw_mode,CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "color",&bw_mode,CONF_TYPE_FLAG, 0, 1, 0, NULL},

  { "interlaced",&interlaced_mode,CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "nointerlaced",&interlaced_mode,CONF_TYPE_FLAG, 0, 1, 0, NULL},

  { "square-pixel",&pixel_mode,CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "ccir601-pixel",&pixel_mode,CONF_TYPE_FLAG, 0, 1, 0, NULL},

  { "iec958-encoded",&iec958_mode,CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "iec958-decoded",&iec958_mode,CONF_TYPE_FLAG, 0, 1, 0, NULL},

  { "mute", &mute_mode,CONF_TYPE_FLAG, 0, 1, 0, NULL},
  { "nomute",&mute_mode,CONF_TYPE_FLAG, 0, 0, 1, NULL},

  { "ignore-cache",&ignore_cache,CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "update-cache",&update_cache,CONF_TYPE_FLAG, 0, 0, 1, NULL},

  { "olh-cor", &olh_cor, CONF_TYPE_INT, CONF_RANGE, -20, 20, NULL},
  { "olw-cor", &olw_cor, CONF_TYPE_INT, CONF_RANGE, -20, 20, NULL},
  { "olx-cor", &olx_cor, CONF_TYPE_INT, CONF_RANGE, -20, 20, NULL},
  { "oly-cor", &oly_cor, CONF_TYPE_INT, CONF_RANGE, -20, 20, NULL},

  { "ol-osd", &ol_osd, CONF_TYPE_FLAG, 0, 0, 1, NULL},
  { "nool-osd", &ol_osd, CONF_TYPE_FLAG, 0, 1, 0, NULL},

  { "ck-rmin", &ck_rmin, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-rmax", &ck_rmax, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-r", &ck_r, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-gmin", &ck_gmin, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-gmax", &ck_gmax, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-g", &ck_g, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-bmin", &ck_bmin, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-bmax", &ck_bmax, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "ck-b", &ck_b, CONF_TYPE_INT, CONF_RANGE, 0, 0xFF, NULL},
  { "cr-left", &cr_left, CONF_TYPE_INT, CONF_RANGE, 0, 500, NULL},
  { "cr-right", &cr_right, CONF_TYPE_INT, CONF_RANGE, 0, 500, NULL},
  { "cr-top", &cr_top, CONF_TYPE_INT, CONF_RANGE, 0, 500, NULL},
  { "cr-bot", &cr_bot, CONF_TYPE_INT, CONF_RANGE, 0, 500, NULL},

  { NULL,NULL, 0, 0, 0, 0, NULL}
};

static const vo_info_t info = {
  "DXR2 video out",
  "dxr2",
  "Alban Bedel <albeu@free.fr> and Tobias Diedrich <ranma@gmx.at>",
  ""
};

const LIBVO_EXTERN (dxr2)

static char *ucodesearchpath[] = {
  "/usr/local/lib/dxr2/dvd12.ux",
  "/usr/lib/dxr2/dvd12.ux",
  "/usr/src/dvd12.ux",
  NULL,
};

#define BUF_SIZE	2048

static unsigned char dxr2buf[BUF_SIZE];
static unsigned int  dxr2bufpos = 0;

int write_dxr2(const unsigned char *data, int len)
{
  int w = 0;

  if (dxr2_fd < 0)
  {
    mp_msg (MSGT_VO, MSGL_ERR, "DXR2 fd is not valid\n");
    return 0;
  }

  while (len>0) if ((dxr2bufpos+len) <= BUF_SIZE) {
    fast_memcpy(dxr2buf+dxr2bufpos, data, len);
    dxr2bufpos+=len;
    len=0;
  } else {
    int copylen=BUF_SIZE-dxr2bufpos;
    if(copylen > 0) {
      fast_memcpy(dxr2buf+dxr2bufpos, data, copylen);
      dxr2bufpos += copylen;
      data+=copylen;
      len-=copylen;
    }
    w += write(dxr2_fd, dxr2buf, BUF_SIZE);
    if(w < 0) {
      mp_msg(MSGT_VO,MSGL_WARN,"DXR2 : write failed : %s \n",strerror(errno));
      dxr2bufpos = 0;
      break;
    }
    dxr2bufpos -= w;
    if(dxr2bufpos)
      memmove(dxr2buf,dxr2buf + w,dxr2bufpos);
  }

  return w;
}

static void flush_dxr2(void)
{
  int w;
  while (dxr2bufpos) {
    w = write(dxr2_fd, dxr2buf, dxr2bufpos);
    if(w < 0) {
      mp_msg(MSGT_VO,MSGL_WARN,"DXR2 : write failed %s \n",strerror(errno));
      dxr2bufpos = 0;
      break;
    }
    dxr2bufpos -= w;
  }
}

#define PACK_MAX_SIZE 2048

static unsigned char pack[PACK_MAX_SIZE];

static unsigned char mpg_eof[]={
  0x00, 0x00, 0x01, 0xb9
};

static void dxr2_send_eof(void)
{
  write_dxr2(mpg_eof, sizeof(mpg_eof));
}

static void dxr2_send_sub_packet(unsigned char* data,int len,int id,unsigned int timestamp) {
  int ptslen=5;

  if(dxr2_fd < 0) {
    mp_msg(MSGT_VO,MSGL_ERR,"DXR2 fd is not valid\n");
    return;
  }

  if (((int) timestamp)<0)
    timestamp=0;

  mp_msg(MSGT_VO,MSGL_DBG2,"dxr2_send_sub_packet(timestamp=%d)\n", timestamp);
  // startcode:
  pack[0]=pack[1]=0;pack[2]=0x01;

  // stream id
  pack[3]=0xBD;

  while(len>=4){
    int payload_size= PACK_MAX_SIZE-(7+ptslen+3);
    if(payload_size>len) payload_size= len;

    pack[4]=(3+ptslen+1+payload_size)>>8;
    pack[5]=(3+ptslen+1+payload_size)&255;

    pack[6]=0x81;
    if(ptslen){
      int x;
      pack[7]=0x80;
      pack[8]=ptslen;
      // presentation time stamp:
      x=(0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1;
      pack[9]=x;
      x=((((timestamp >> 15) & 0x7fff) << 1) | 1);
      pack[10]=x>>8; pack[11]=x&255;
      x=((((timestamp) & 0x7fff) << 1) | 1);
      pack[12]=x>>8; pack[13]=x&255;
    } else {
      pack[7]=0x00;
      pack[8]=0x00;
    }
    pack[ptslen+9] = id;

    write_dxr2(pack,7+ptslen+3);
    write_dxr2(data,payload_size);
    len -= payload_size;
    data += payload_size;
    ptslen = 0;
  }
}

static int dxr2_set_vga_params(dxr2_vgaParams_t* vga,int detect) {
  // Init the overlay, don't ask me how it work ;-)
  dxr2_sixArg_t oc;
  dxr2_oneArg_t om;
  dxr2_twoArg_t win;
  dxr2_fourArg_t crop;

  crop.arg1= cr_left;
  crop.arg2= cr_right;
  crop.arg3 = cr_top;
  crop.arg4 = cr_bot;
  ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_CROPPING, &crop);

  oc.arg1 = 0x40;
  oc.arg2 = 0xff;
  oc.arg3 = 0x40;
  oc.arg4 = 0xff;
  oc.arg5 = 0x40;
  oc.arg6 = 0xff;
  ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_COLOUR, &oc);

  om.arg = ol_ratio;
  ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_RATIO,&om);

  win.arg1 = 100;
  win.arg2 = 3;
  ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_POSITION,&win);

  win.arg1 = vo_screenwidth;
  win.arg2 = vo_screenheight;
  ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_DIMENSION,&win);

  om.arg = 0;
  ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_IN_DELAY,&om);

  if(detect) {
    // First we need a white screen
    uint8_t* img = malloc(vo_screenwidth*vo_screenheight*3);
    uint8_t* src[] = { img, NULL, NULL };
    int stride[] = { vo_screenwidth * 3 , 0, 0 };
    int cc = vo_config_count;

    memset(img,255,vo_screenwidth*vo_screenheight*3);
    vo_config_count = sub_config_count;
    if(sub_vo->config(vo_screenwidth,vo_screenheight,vo_screenwidth,vo_screenheight,
		    VOFLAG_FULLSCREEN ,"DXR2 sub vo",IMGFMT_BGR24) != 0) {
      mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] sub vo config failed => No overlay\n");
      sub_vo->uninit();
      sub_vo = NULL;
      use_ol = 0;
      vo_config_count = cc;
      return 0;
    }
    sub_vo->draw_slice(src,stride,vo_screenwidth,vo_screenheight,0,0);
    sub_vo->flip_page();
    free(img);
    sub_config_count++;
    vo_config_count = cc;

    om.arg = DXR2_OVERLAY_WINDOW_COLOUR_KEY;
    ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_MODE,&om);

    vga->xScreen = vo_screenwidth;
    vga->yScreen = vo_screenheight;
    vga->hOffWinKey = 100;
    vga->vOffWinKey = 3;
    ioctl(dxr2_fd, DXR2_IOC_CALCULATE_VGA_PARAMETERS, vga);
  }
  ioctl(dxr2_fd, DXR2_IOC_SET_VGA_PARAMETERS,vga);

  return 1;
}

static int dxr2_save_vga_params(dxr2_vgaParams_t* vga,char* name) {
  struct stat s;
  char* p = get_path("dxr2_cache");
  int p_len = strlen(p), name_len = strlen(name);
  char cache_path[p_len + name_len + 2];
  int ret;
  FILE* fd;

  if(stat(p,&s) !=0) {
    mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] No vga cache dir found (%s)\n",strerror(errno));
    if(errno == EACCES) {
      free(p);
      return 0;
    }
    // Try to create the dir
    if(mkdir(p,S_IRWXU) != 0) {
      mp_msg(MSGT_VO,MSGL_ERR,"VO: [dxr2] Unable to create vga cache dir %s (%s)\n",p,strerror(errno));
      free(p);
      return 0;
    }
  }
  sprintf(cache_path,"%s/%s",p,name);
  free(p);
  fd = fopen(cache_path,"w");
  if(fd == NULL) {
    mp_msg(MSGT_VO,MSGL_ERR,"VO: [dxr2] Unable to open cache file %s for writing (%s)\n",cache_path,strerror(errno));
    return 0;
  }

  ret = fprintf(fd,"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
		vga->hOffWinKey,
		vga->vOffWinKey,
		vga->xScreen,
		vga->yScreen,
		vga->hsyncPol,
		vga->vsyncPol,
		vga->blankStart,
		vga->blankWidth,
		vga->hOffset,
		vga->vOffset,
		vga->ratio,
		olx_cor,
		oly_cor,
		olw_cor,
		olh_cor);

  fclose(fd);
  return ret >= 11 ? 1 : 0;
}

static int dxr2_load_vga_params(dxr2_vgaParams_t* vga,char* name) {
  char* p = get_path("dxr2_cache");
  int p_len = strlen(p), name_len = strlen(name);
  char cache_path[p_len + name_len + 2];
  int ret;
  int xc,yc,wc,hc;
  FILE* fd;

  sprintf(cache_path,"%s/%s",p,name);
  free(p);

  fd = fopen(cache_path,"r");
  if(fd == NULL) {
    mp_msg(MSGT_VO,MSGL_ERR,"VO: [dxr2] Unable to open cache file %s for reading (%s)\n",cache_path,strerror(errno));
    return 0;
  }
  ret = fscanf(fd, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
	       &vga->hOffWinKey,
	       &vga->vOffWinKey,
	       &vga->xScreen,
	       &vga->yScreen,
	       &vga->hsyncPol,
	       &vga->vsyncPol,
	       &vga->blankStart,
	       &vga->blankWidth,
	       &vga->hOffset,
	       &vga->vOffset,
	       &vga->ratio,
	       &xc,
	       &yc,
	       &wc,
	       &hc);

  fclose(fd);
  if(ret > 11 && !olx_cor) olx_cor = xc;
  if(ret > 12 && !oly_cor) oly_cor = yc;
  if(ret > 13 && !olw_cor) olw_cor = wc;
  if(ret > 14 && !olh_cor) olh_cor = hc;
  return ret >= 11 ? 1 : 0;
}

static int dxr2_setup_vga_params(void) {
  const vo_info_t* vi = sub_vo->info;
  dxr2_vgaParams_t vga;

  int loaded = dxr2_load_vga_params(&vga,(char*)vi->short_name);
  if(!dxr2_set_vga_params(&vga,(update_cache || ignore_cache) ? 1 : !loaded ))
    return 0;
  if(!loaded || update_cache)
    dxr2_save_vga_params(&vga,(char*)vi->short_name);
  return 1;
}

static void dxr2_set_overlay_window(void) {
  uint8_t* src[] = { sub_img, NULL, NULL };
  int stride[] = { movie_w * 3 , 0, 0 };
  dxr2_twoArg_t win;
  int cc = vo_config_count;
  vo_config_count = sub_config_count;
  sub_vo->draw_slice(src,stride,movie_w,movie_h,0,0);
  sub_vo->flip_page();
  vo_config_count = cc;


  mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] setting overlay with correction x=%d y=%d w=%d h=%d\n",olx_cor,oly_cor,	olw_cor,olh_cor);

  // Sub vo isn't a windowed one, fill in the needed stuff
  if(!sub_vo_win) {
    if(vo_fs) {
      vo_dwidth = vo_screenwidth;
      vo_dheight = vo_screenheight;
      vo_dx = vo_dy = 0;
    } else {
      vo_dwidth = movie_w;
      vo_dheight = movie_h;
      vo_dx = (vo_screenwidth - vo_dwidth) / 2;
      vo_dy = (vo_screenheight - vo_dheight) / 2;
    }
  }

  if(sub_w != vo_dwidth || sub_h != vo_dheight) {
    int new_aspect = ((1<<16)*vo_dwidth + vo_dheight/2)/vo_dheight;
    sub_w = vo_dwidth;
    sub_h = vo_dheight;
    if(new_aspect > dxr2_aspect)
      sub_w = (sub_h*dxr2_aspect + (1<<15))>>16;
    else
      sub_h = ((sub_w<<16) + (dxr2_aspect>>1)) /dxr2_aspect;
    sub_w += olw_cor;
    sub_h += olh_cor;
    sub_x_off = (vo_dwidth-sub_w) / 2;
    sub_y_off = (vo_dheight-sub_h) / 2;
    sub_x = -vo_dx; // Be sure to also replace the overlay
    win.arg1 = sub_w;
    win.arg2 = sub_h;
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] set win size w=%d h=%d and offset x=%d y=%d \n",win.arg1,win.arg2,sub_x_off,sub_y_off);
    ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_DIMENSION, &win);
  }

  if(vo_dx != sub_x || vo_dy != sub_y) {
    sub_x = vo_dx + olx_cor + sub_x_off;
    sub_y = vo_dy + oly_cor + sub_y_off;
    win.arg1 = (sub_x > 0 ? sub_x : 0);
    win.arg2 = (sub_y > 0 ? sub_y : 0);
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] set pos x=%d y=%d \n",win.arg1,win.arg2);
    ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_POSITION,&win);
  }

}

static int config(uint32_t s_width, uint32_t s_height, uint32_t width, uint32_t height, uint32_t flags, char *title, uint32_t format)
{
  int arg;
  dxr2_threeArg_t arg3;

  if(dxr2_fd < 0) {
    mp_msg(MSGT_VO,MSGL_ERR,"DXR2 fd is not valid\n");
    return VO_ERROR;
  }

  if(playing) {
    dxr2_send_eof();
    flush_dxr2();
    ioctl(dxr2_fd, DXR2_IOC_STOP, NULL);
    playing = 0;
  }

  // Video stream setup
  arg3.arg1 = DXR2_STREAM_VIDEO;
  arg3.arg2 = 0;
  ioctl(dxr2_fd, DXR2_IOC_SELECT_STREAM, &arg3);
  if (vo_fps > 28)
    arg3.arg1 = DXR2_SRC_VIDEO_FREQ_30;
  else arg3.arg1 = DXR2_SRC_VIDEO_FREQ_25;
  arg3.arg2 = s_width;
  arg3.arg3 = s_height;
  ioctl(dxr2_fd, DXR2_IOC_SET_SOURCE_VIDEO_FORMAT, &arg3);
  arg = DXR2_BITSTREAM_TYPE_MPEG_VOB;
  ioctl(dxr2_fd, DXR2_IOC_SET_BITSTREAM_TYPE, &arg);

  // Aspect ratio
  if (1.76 <= movie_aspect && movie_aspect <= 1.80) {
    arg = DXR2_ASPECTRATIO_16_9;
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] source aspect ratio 16:9\n");
  } else {
    arg = DXR2_ASPECTRATIO_4_3;
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] source aspect ratio 4:3\n");
  }
  ioctl(dxr2_fd, DXR2_IOC_SET_SOURCE_ASPECT_RATIO, &arg);
  if (1.76 <= monitor_aspect && monitor_aspect <=1.80) {
    arg = DXR2_ASPECTRATIO_16_9;
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] monitor aspect ratio 16:9\n");
  } else {
    arg = DXR2_ASPECTRATIO_4_3;
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] monitor aspect ratio 4:3\n");
  }
  ioctl(dxr2_fd, DXR2_IOC_SET_OUTPUT_ASPECT_RATIO, &arg);

  arg = ar_mode;
  ioctl(dxr2_fd, DXR2_IOC_SET_ASPECT_RATIO_MODE, &arg);

  // TV setup
  arg = mv_mode;
  ioctl(dxr2_fd, DXR2_IOC_SET_TV_MACROVISION_MODE, &arg);
  arg = _75ire_mode;
  ioctl(dxr2_fd, DXR2_IOC_SET_TV_75IRE_MODE, &arg);
  arg = bw_mode;
  ioctl(dxr2_fd, DXR2_IOC_SET_TV_BLACKWHITE_MODE, &arg);
  arg = interlaced_mode;
  ioctl(dxr2_fd, DXR2_IOC_SET_TV_INTERLACED_MODE, &arg);
  arg = pixel_mode;
  ioctl(dxr2_fd, DXR2_IOC_SET_TV_PIXEL_MODE, &arg);

  if (norm) {
    if (strcmp(norm, "ntsc")==0)
      arg = DXR2_OUTPUTFORMAT_NTSC;
    else if (strcmp(norm, "pal")==0) {
      if (vo_fps > 28) {
	mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] you want pal, but we play at 30 fps, selecting pal60 instead\n");
	arg = DXR2_OUTPUTFORMAT_PAL_60;
	norm="pal60";
      } else arg = DXR2_OUTPUTFORMAT_PAL_BDGHI;
    } else if (strcmp(norm, "pal60")==0) {
      if (vo_fps > 28)
	arg = DXR2_OUTPUTFORMAT_PAL_60;
      else {
	mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] you want pal60, but we play at 25 fps, selecting pal instead\n");
	arg = DXR2_OUTPUTFORMAT_PAL_BDGHI;
	norm="pal";
      }
    } else if (strcmp(norm, "palm")==0)
      arg = DXR2_OUTPUTFORMAT_PAL_M;
    else if (strcmp(norm, "paln")==0)
      arg = DXR2_OUTPUTFORMAT_PAL_N;
    else if (strcmp(norm, "palnc")==0)
      arg = DXR2_OUTPUTFORMAT_PAL_Nc;
    else {
      mp_msg(MSGT_VO,MSGL_WARN,"[dxr2] invalid norm %s\n", norm);
      mp_msg(MSGT_VO,MSGL_WARN,"Valid values are ntsc,pal,pal60,palm,paln,palnc\n");
      mp_msg(MSGT_VO,MSGL_WARN,"Using ntsc\n");
      norm="ntsc";
    }
  } else {
    if (vo_fps > 28) {
      arg = DXR2_OUTPUTFORMAT_NTSC;
      norm="ntsc";
    } else {
      arg = DXR2_OUTPUTFORMAT_PAL_BDGHI;
      norm="pal";
    }
  }
  mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] output norm set to %s\n", norm);
  ioctl(dxr2_fd, DXR2_IOC_SET_TV_OUTPUT_FORMAT, &arg);

  // Subtitles

  arg = DXR2_SUBPICTURE_ON;
  ioctl(dxr2_fd,DXR2_IOC_ENABLE_SUBPICTURE,&arg);
  arg3.arg1 = DXR2_STREAM_SUBPICTURE;
  arg3.arg2 = 0;
  ioctl(dxr2_fd, DXR2_IOC_SELECT_STREAM, &arg3);

  // Audio
  arg = iec958_mode;
  ioctl(dxr2_fd, DXR2_IOC_IEC958_OUTPUT_MODE, &arg);
  arg = DXR2_AUDIO_WIDTH_16;
  ioctl(dxr2_fd, DXR2_IOC_SET_AUDIO_DATA_WIDTH, &arg);
  arg = DXR2_AUDIO_FREQ_48;
  ioctl(dxr2_fd, DXR2_IOC_SET_AUDIO_SAMPLE_FREQUENCY, &arg);
  arg3.arg1 = DXR2_STREAM_AUDIO_LPCM;
  arg3.arg2 = 0;
  ioctl(dxr2_fd, DXR2_IOC_SELECT_STREAM, &arg3);
  arg = 19;
  ioctl(dxr2_fd, DXR2_IOC_SET_AUDIO_VOLUME, &arg);
  arg = mute_mode;
  ioctl(dxr2_fd, DXR2_IOC_AUDIO_MUTE, &arg);

  movie_w = width;
  movie_h = height;
  //vo_fs = flags & VOFLAG_FULLSCREEN ? 1 : 0;
  // Overlay
  while(use_ol) {
    dxr2_twoArg_t win;
    dxr2_oneArg_t om;
    int cc = vo_config_count;
    vo_config_count = sub_config_count;
    // Load or detect the overlay stuff
    if(!dxr2_setup_vga_params()) {
      sub_vo->uninit();
      sub_vo = NULL;
      vo_config_count = cc;
      break;
    }
    // Does the sub vo support the x11 stuff
    // Fix me : test the other x11 vo's and enable them
    if(strcmp(sub_vo->info->short_name,"x11") == 0)
      sub_vo_win = 1;
    else
      sub_vo_win = 0;

    // No window and no osd => we don't need any subdriver
    if(!sub_vo_win && !ol_osd) {
      sub_vo->uninit();
      sub_vo = NULL;
    }

    while(sub_vo) {
      dxr2_sixArg_t oc;
      int i,sub_flags = VOFLAG_SWSCALE | (flags & VOFLAG_FULLSCREEN);
      if(sub_vo->config(width,height,width,height,sub_flags,
			"MPlayer DXR2 render",IMGFMT_BGR24) != 0) {
	mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] sub vo config failed => No X11 window\n");
	sub_vo->uninit();
	sub_vo = NULL;
	break;
      }
      sub_config_count++;

      // Feel free to try some other other color and report your results
      oc.arg1 = ck_rmin;
      oc.arg2 = ck_rmax;
      oc.arg3 = ck_gmin;
      oc.arg4 = ck_gmax;
      oc.arg5 = ck_bmin;
      oc.arg6 = ck_bmax;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_COLOUR, &oc);

      om.arg = DXR2_OVERLAY_WINDOW_COLOUR_KEY;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_MODE,&om);
      sub_img = malloc(width*height*3);
      for(i = 0 ; i < width*height*3 ; i += 3) {
	sub_img[i] = ck_b;
	sub_img[i+1] = ck_g;
	sub_img[i+2] = ck_r;
      }
      dxr2_aspect = ((1<<16)*width + height/2)/height;
      sub_w = sub_h = 0;
      dxr2_set_overlay_window();
      break;
    }
    vo_config_count = cc;
    if(!sub_vo) { // Fallback on non windowed overlay
      vo_fs = flags & VOFLAG_FULLSCREEN ? 1 : 0;
      om.arg = DXR2_OVERLAY_WINDOW_KEY;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_MODE,&om);
      win.arg1 = flags & VOFLAG_FULLSCREEN ? vo_screenwidth : width;
      win.arg2 = flags & VOFLAG_FULLSCREEN ? vo_screenheight : height;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_DIMENSION, &win);
      win.arg1 = (vo_screenwidth - win.arg1) / 2;
      win.arg2 = (vo_screenheight - win.arg2) / 2;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_POSITION,&win);
    }
    break;
  }

  // start playing
  if(ioctl(dxr2_fd, DXR2_IOC_PLAY, NULL) == 0) {
    playing = 1;
    return 0;
  } else
    return VO_ERROR;
}

static void clear_alpha(int x0,int y0, int w,int h) {
  uint8_t* src[] = { sub_img , NULL, NULL };
  int stride[] = { movie_w * 3, 0, 0 };

  sub_vo->draw_slice(src,stride,w,h,x0,y0);
}

static void draw_osd(void)
{
  if(sub_vo && ol_osd) {
    vo_remove_text(movie_w,movie_h,clear_alpha);
    sub_vo->draw_osd();
  }
}

static int draw_frame(uint8_t * src[])
{
  vo_mpegpes_t *p=(vo_mpegpes_t *)src[0];

  if(p->id == 0x1E0) {// Video
    send_mpeg_ps_packet (p->data, p->size, p->id,
                         p->timestamp ? p->timestamp : vo_pts, 2, write_dxr2);
  } else if(p->id == 0x20) // Subtitles
    dxr2_send_sub_packet(p->data, p->size, p->id, p->timestamp);
  return 0;
}

static void flip_page (void)
{
  if(sub_vo && ol_osd && vo_osd_changed_flag)
    sub_vo->flip_page();
}


static int query_format(uint32_t format)
{
  if (format==IMGFMT_MPEGPES)
    return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW|VFCAP_TIMER|VFCAP_SPU;
  return 0;
}


static void uninit(void)
{
  mp_msg(MSGT_VO,MSGL_DBG2, "VO: [dxr2] Uninitializing\n" );

  if (dxr2_fd > 0) {
    if(playing) {
      dxr2_send_eof();
      flush_dxr2();
      playing = 0;
    }
    close(dxr2_fd);
    dxr2_fd = -1;
  }
  free(sub_img);
  sub_img = NULL;
  if(sub_vo) {
    int cc = vo_config_count;
    vo_config_count = sub_config_count;
    sub_vo->uninit();
    sub_vo = NULL;
    vo_config_count = cc;
  }
}


static void check_events(void)
{
  // I'd like to have this done in an x11 independent way
  // It's because of this that we are limited to vo_x11 for windowed overlay :-(
  if(sub_vo && sub_vo_win) {
    int e=vo_x11_check_events(mDisplay);
    if ( !(e&VO_EVENT_RESIZE) && !(e&VO_EVENT_EXPOSE) ) return;
    XSetBackground(mDisplay, vo_gc, 0);
    XClearWindow(mDisplay, vo_window);
    dxr2_set_overlay_window();
  }
}

static int preinit(const char *arg) {
  int uCodeFD = -1;
  int uCodeSize;
  dxr2_uCode_t* uCode;
  dxr2_fourArg_t crop;
  int n=0;

  sub_vo = NULL;
  sub_config_count = 0;
  if(use_ol) {
    if (arg) {
      for(n = 0 ; video_out_drivers[n] != NULL ; n++) {
	const vo_info_t* vi = video_out_drivers[n]->info;
	if(!vi)
	  continue;
	if(strcasecmp(arg,vi->short_name) == 0)
	  break;
      }
      sub_vo = video_out_drivers[n];
    } else {
      mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] We need a sub driver to initialize the overlay\n");
      use_ol = 0;
    }
  }

  if(!sub_vo) {
    if(use_ol)
      mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] Sub driver '%s' not found => no overlay\n",arg);
    use_ol = 0;
  } else {
    if(sub_vo->preinit(NULL) != 0) {
      mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] Sub vo %s preinit failed => no overlay\n",arg);
      sub_vo = NULL;
      use_ol = 0;
    } else {
      uint32_t fmt = IMGFMT_BGR24;
      mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] Sub vo %s initialized\n",arg);
      if(sub_vo->control(VOCTRL_QUERY_FORMAT,&fmt) <= 0) {
	mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] Sub vo %s doesn't support BGR24 => no overlay\n",arg);
	sub_vo->uninit();
	sub_vo = NULL;
	use_ol = 0;
      }
    }
  }

  dxr2_fd = open( "/dev/dxr2", O_WRONLY);
  if( dxr2_fd < 0 ) {
      mp_msg(MSGT_VO,MSGL_V, "VO: [dxr2] Error opening /dev/dxr2 for writing!\n" );
      return VO_ERROR;
  }

  if(ucode)
    uCodeFD = open(ucode, O_RDONLY);
  else for (n=0; ucodesearchpath[n] != NULL; n++) {
    mp_msg(MSGT_VO,MSGL_V,"VO: [dxr2] Looking for microcode in %s... ",
	   ucodesearchpath[n]);
    if ((uCodeFD = open(ucodesearchpath[n], O_RDONLY))>0) {
      mp_msg(MSGT_VO,MSGL_V,"ok\n");
      break;
    } else {
      mp_msg(MSGT_VO,MSGL_V,"failed (%s)\n", strerror(errno));
    }
  }
  if (uCodeFD < 0) {
    mp_msg(MSGT_VO,MSGL_ERR,"VO: [dxr2] Could not open microcode\n");
    return VO_ERROR;
  }

  uCodeSize = lseek(uCodeFD, 0, SEEK_END);
  if ((uCode = malloc(uCodeSize + 4)) == NULL) {

    mp_msg(MSGT_VO,MSGL_FATAL,"VO: [dxr2] Could not allocate memory for uCode: %s\n", strerror(errno));
    return VO_ERROR;
  }
  lseek(uCodeFD, 0, SEEK_SET);
  if (read(uCodeFD, uCode+4, uCodeSize) != uCodeSize) {

    mp_msg(MSGT_VO,MSGL_ERR,"VO: [dxr2] Could not read uCode uCode: %s\n", strerror(errno));
    free(uCode);
    return VO_ERROR;
  }
  close(uCodeFD);
  uCode->uCodeLength = uCodeSize;

  // upload ucode
  ioctl(dxr2_fd, DXR2_IOC_INIT_ZIVADS, uCode);

  // reset card
  ioctl(dxr2_fd, DXR2_IOC_RESET, NULL);
  playing = 0;

  if(!use_ol) {
    crop.arg1=0;
    crop.arg2=0;
    crop.arg3=0;
    crop.arg4=0;
    ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_CROPPING, &crop);
  }

  free(uCode);
  return 0;
}

static int control(uint32_t request, void *data)
{
  switch (request) {
  case VOCTRL_QUERY_FORMAT:
    return query_format(*((uint32_t*)data));
  case VOCTRL_PAUSE:
    ioctl(dxr2_fd,DXR2_IOC_PAUSE, NULL);
    return VO_TRUE;
  case VOCTRL_RESUME:
    ioctl(dxr2_fd, DXR2_IOC_PLAY, NULL);
    return VO_TRUE;
  case VOCTRL_RESET:
    flush_dxr2();
    ioctl(dxr2_fd, DXR2_IOC_PLAY, NULL);
    return VO_TRUE;
  case VOCTRL_ONTOP:
    vo_x11_ontop();
    return VO_TRUE;
  case VOCTRL_GUISUPPORT:
    if (sub_vo) return sub_vo->control(VOCTRL_GUISUPPORT, NULL);
    else return VO_FALSE;
  case VOCTRL_FULLSCREEN:
    if(!use_ol)
      return VO_NOTIMPL;
    else if(sub_vo) {
      int r = sub_vo->control(VOCTRL_FULLSCREEN,0);
      if(r == VO_TRUE && !sub_vo_win)
	dxr2_set_overlay_window();
      return r;
    } else {
      dxr2_twoArg_t win;
      vo_fs = !vo_fs;
      win.arg1 = vo_fs ? vo_screenwidth : movie_w;
      win.arg2 = vo_fs ? vo_screenheight : movie_h;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_DIMENSION, &win);
      win.arg1 = (vo_screenwidth - win.arg1) / 2;
      win.arg2 = (vo_screenheight - win.arg2) / 2;
      ioctl(dxr2_fd, DXR2_IOC_SET_OVERLAY_POSITION,&win);
      return VO_TRUE;
    }
  case VOCTRL_SET_SPU_PALETTE: {
    if(ioctl(dxr2_fd,DXR2_IOC_SET_SUBPICTURE_PALETTE,data) < 0) {
      mp_msg(MSGT_VO,MSGL_WARN,"VO: [dxr2] SPU palette loading failed\n");
      return VO_ERROR;
    }
    return VO_TRUE;
  }
  }
  return VO_NOTIMPL;
}