mpv/libvo/vo_dfbmga.c

1537 lines
48 KiB
C

/*
* MPlayer video driver for DirectFB / Matrox G200/G400/G450/G550
*
* copyright (C) 2002-2008 Ville Syrjala <syrjala@sci.fi>
* Originally based on vo_directfb.c by Jiri Svoboda <Jiri.Svoboda@seznam.cz>.
*
* This file is part of MPlayer.
*
* MPlayer 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.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 <directfb.h>
#include <directfb_version.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "video_out.h"
#include "video_out_internal.h"
#include "fastmemcpy.h"
#include "sub/sub.h"
#include "mp_msg.h"
#include "aspect.h"
#include "mp_fifo.h"
#include "input/keycodes.h"
static const vo_info_t info = {
"DirectFB / Matrox G200/G400/G450/G550",
"dfbmga",
"Ville Syrjala <syrjala@sci.fi>",
""
};
const LIBVO_EXTERN(dfbmga)
static IDirectFB *dfb;
static IDirectFBDisplayLayer *crtc1;
static IDirectFBDisplayLayer *bes;
static IDirectFBDisplayLayer *crtc2;
static IDirectFBDisplayLayer *spic;
static int num_bufs;
static int current_buf;
static int current_ip_buf;
static IDirectFBSurface *bufs[3];
static IDirectFBSurface *frame;
static IDirectFBSurface *subframe;
static IDirectFBSurface *besframe;
static IDirectFBSurface *c1frame;
static IDirectFBSurface *c2frame;
static IDirectFBSurface *spicframe;
static DFBSurfacePixelFormat frame_format;
static DFBSurfacePixelFormat subframe_format;
static DFBRectangle besrect;
static DFBRectangle c1rect;
static DFBRectangle c2rect;
static DFBRectangle *subrect;
static IDirectFBInputDevice *keyboard;
static IDirectFBInputDevice *remote;
static IDirectFBEventBuffer *buffer;
static int blit_done;
static int c1stretch;
static int c2stretch;
static int use_bes;
static int use_crtc1;
static int use_crtc2;
static int use_spic;
static int use_input;
static int use_remote;
static int field_parity;
static int flipping;
static DFBDisplayLayerBufferMode buffermode;
static int tvnorm;
static int osd_changed;
static int osd_dirty;
static int osd_current;
static int osd_max;
static int is_g200;
static uint32_t in_width;
static uint32_t in_height;
static uint32_t buf_height;
static uint32_t screen_width;
static uint32_t screen_height;
static uint32_t sub_width;
static uint32_t sub_height;
static char *
pixelformat_name( DFBSurfacePixelFormat format )
{
switch(format) {
case DSPF_ARGB:
return "ARGB";
case DSPF_RGB32:
return "RGB32";
case DSPF_RGB16:
return "RGB16";
case DSPF_ARGB1555:
return "ARGB1555";
case DSPF_YUY2:
return "YUY2";
case DSPF_UYVY:
return "UYVY";
case DSPF_YV12:
return "YV12";
case DSPF_I420:
return "I420";
case DSPF_ALUT44:
return "ALUT44";
case DSPF_NV12:
return "NV12";
case DSPF_NV21:
return "NV21";
default:
return "Unknown pixel format";
}
}
static DFBSurfacePixelFormat
imgfmt_to_pixelformat( uint32_t format )
{
switch (format) {
case IMGFMT_BGR32:
return DSPF_RGB32;
case IMGFMT_BGR16:
return DSPF_RGB16;
case IMGFMT_BGR15:
return DSPF_ARGB1555;
case IMGFMT_YUY2:
return DSPF_YUY2;
case IMGFMT_UYVY:
return DSPF_UYVY;
case IMGFMT_YV12:
return DSPF_YV12;
case IMGFMT_I420:
case IMGFMT_IYUV:
return DSPF_I420;
case IMGFMT_NV12:
return DSPF_NV12;
case IMGFMT_NV21:
return DSPF_NV21;
default:
return DSPF_UNKNOWN;
}
}
struct layer_enum
{
const char *name;
IDirectFBDisplayLayer **layer;
DFBResult res;
};
static DFBEnumerationResult
get_layer_by_name( DFBDisplayLayerID id,
DFBDisplayLayerDescription desc,
void *data )
{
struct layer_enum *l = (struct layer_enum *) data;
if (!strcmp( l->name, desc.name ))
if ((l->res = dfb->GetDisplayLayer( dfb, id, l->layer )) == DFB_OK)
return DFENUM_CANCEL;
return DFENUM_OK;
}
static int
preinit( const char *arg )
{
DFBResult res;
int force_input = -1;
/* Some defaults */
use_bes = 0;
use_crtc1 = 0;
use_crtc2 = 1;
use_spic = 1;
field_parity = -1;
buffermode = DLBM_TRIPLE;
osd_max = 4;
flipping = 1;
tvnorm = -1;
use_input = !getenv( "DISPLAY" );
if (vo_subdevice) {
int show_help = 0;
int opt_no = 0;
while (*vo_subdevice != '\0') {
if (!strncmp(vo_subdevice, "bes", 3)) {
use_bes = !opt_no;
vo_subdevice += 3;
opt_no = 0;
} else if (!strncmp(vo_subdevice, "crtc1", 5)) {
use_crtc1 = !opt_no;
vo_subdevice += 5;
opt_no = 0;
} else if (!strncmp(vo_subdevice, "crtc2", 5)) {
use_crtc2 = !opt_no;
vo_subdevice += 5;
opt_no = 0;
} else if (!strncmp(vo_subdevice, "spic", 4)) {
use_spic = !opt_no;
vo_subdevice += 4;
opt_no = 0;
} else if (!strncmp(vo_subdevice, "input", 5)) {
force_input = !opt_no;
vo_subdevice += 5;
opt_no = 0;
} else if (!strncmp(vo_subdevice, "remote", 6)) {
use_remote = !opt_no;
vo_subdevice += 6;
opt_no = 0;
} else if (!strncmp(vo_subdevice, "buffermode=", 11)) {
if (opt_no) {
show_help = 1;
break;
}
vo_subdevice += 11;
if (!strncmp(vo_subdevice, "single", 6)) {
buffermode = DLBM_FRONTONLY;
osd_max = 1;
flipping = 0;
vo_subdevice += 6;
} else if (!strncmp(vo_subdevice, "double", 6)) {
buffermode = DLBM_BACKVIDEO;
osd_max = 2;
flipping = 1;
vo_subdevice += 6;
} else if (!strncmp(vo_subdevice, "triple", 6)) {
buffermode = DLBM_TRIPLE;
osd_max = 4;
flipping = 1;
vo_subdevice += 6;
} else {
show_help = 1;
break;
}
opt_no = 0;
} else if (!strncmp(vo_subdevice, "fieldparity=", 12)) {
if (opt_no) {
show_help = 1;
break;
}
vo_subdevice += 12;
if (!strncmp(vo_subdevice, "top", 3)) {
field_parity = 0;
vo_subdevice += 3;
} else if (!strncmp(vo_subdevice, "bottom", 6)) {
field_parity = 1;
vo_subdevice += 6;
} else {
show_help = 1;
break;
}
opt_no = 0;
} else if (!strncmp(vo_subdevice, "tvnorm=", 7)) {
if (opt_no) {
show_help = 1;
break;
}
vo_subdevice += 7;
if (!strncmp(vo_subdevice, "pal", 3)) {
tvnorm = 0;
vo_subdevice += 3;
} else if (!strncmp(vo_subdevice, "ntsc" , 4)) {
tvnorm = 1;
vo_subdevice += 4;
} else if (!strncmp(vo_subdevice, "auto" , 4)) {
tvnorm = 2;
vo_subdevice += 4;
} else {
show_help = 1;
break;
}
opt_no = 0;
} else if (!strncmp(vo_subdevice, "no", 2)) {
if (opt_no) {
show_help = 1;
break;
}
vo_subdevice += 2;
opt_no = 1;
} else if (*vo_subdevice == ':') {
if (opt_no) {
show_help = 1;
break;
}
vo_subdevice++;
opt_no = 0;
} else {
show_help = 1;
break;
}
}
if (show_help) {
mp_msg( MSGT_VO, MSGL_ERR,
"\nvo_dfbmga command line help:\n"
"Example: mplayer -vo dfbmga:nocrtc2:bes:buffermode=single\n"
"\nOptions (use 'no' prefix to disable):\n"
" bes Use Backend Scaler\n"
" crtc1 Use CRTC1\n"
" crtc2 Use CRTC2\n"
" spic Use hardware sub-picture for OSD\n"
" input Use DirectFB for keyboard input\n"
" remote Use DirectFB for remote control input\n"
"\nOther options:\n"
" buffermode=(single|double|triple)\n"
" single Use single buffering\n"
" double Use double buffering\n"
" triple Use triple buffering\n"
" fieldparity=(top|bottom)\n"
" top Top field first\n"
" bottom Bottom field first\n"
" tvnorm=(pal|ntsc|auto)\n"
" pal Force PAL\n"
" ntsc Force NTSC\n"
" auto Select according to FPS\n"
"\n" );
return -1;
}
}
if (!use_bes && !use_crtc1 && !use_crtc2) {
mp_msg( MSGT_VO, MSGL_ERR, "vo_dfbmga: No output selected\n" );
return -1;
}
if (use_bes && use_crtc1) {
mp_msg( MSGT_VO, MSGL_ERR, "vo_dfbmga: Both BES and CRTC1 outputs selected\n" );
return -1;
}
if ((res = DirectFBInit( NULL, NULL )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: DirectFBInit() failed - %s\n",
DirectFBErrorString( res ) );
return -1;
}
switch (tvnorm) {
case 0:
DirectFBSetOption( "matrox-tv-standard", "pal" );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: Forced TV standard to PAL\n" );
break;
case 1:
DirectFBSetOption( "matrox-tv-standard", "ntsc" );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: Forced TV standard to NTSC\n" );
break;
case 2:
if (vo_fps > 27) {
DirectFBSetOption( "matrox-tv-standard", "ntsc" );
mp_msg( MSGT_VO, MSGL_INFO,
"vo_dfbmga: Selected TV standard based upon FPS: NTSC\n" );
} else {
DirectFBSetOption( "matrox-tv-standard", "pal" );
mp_msg( MSGT_VO, MSGL_INFO,
"vo_dfbmga: Selected TV standard based upon FPS: PAL\n" );
}
break;
}
if ((res = DirectFBCreate( &dfb )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: DirectFBCreate() failed - %s\n",
DirectFBErrorString( res ) );
return -1;
}
if (use_crtc1 || use_bes) {
struct layer_enum l = {
"FBDev Primary Layer",
&crtc1,
DFB_UNSUPPORTED
};
dfb->EnumDisplayLayers( dfb, get_layer_by_name, &l );
if (l.res != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "vo_dfbmga: Can't get CRTC1 layer - %s\n",
DirectFBErrorString( l.res ) );
uninit();
return -1;
}
if ((res = crtc1->SetCooperativeLevel( crtc1, DLSCL_EXCLUSIVE )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "Can't get exclusive access to CRTC1 layer - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
use_input = 1;
}
if (force_input != -1)
use_input = force_input;
if (use_bes) {
DFBDisplayLayerConfig dlc;
DFBDisplayLayerConfigFlags failed;
struct layer_enum l = {
"Matrox Backend Scaler",
&bes,
DFB_UNSUPPORTED
};
dfb->EnumDisplayLayers( dfb, get_layer_by_name, &l );
if (l.res != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "Can't get BES layer - %s\n",
DirectFBErrorString( l.res ) );
uninit();
return -1;
}
if ((res = bes->SetCooperativeLevel( bes, DLSCL_EXCLUSIVE )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "Can't get exclusive access to BES - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
dlc.flags = DLCONF_PIXELFORMAT;
dlc.pixelformat = DSPF_RGB16;
if (bes->TestConfiguration( bes, &dlc, &failed ) != DFB_OK) {
is_g200 = 1;
use_crtc2 = 0;
}
}
if (use_crtc2) {
struct layer_enum l = {
"Matrox CRTC2 Layer",
&crtc2,
DFB_UNSUPPORTED
};
dfb->EnumDisplayLayers( dfb, get_layer_by_name, &l );
if (l.res != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "Can't get CRTC2 layer - %s\n",
DirectFBErrorString( l.res ) );
uninit();
return -1;
}
if ((res = crtc2->SetCooperativeLevel( crtc2, DLSCL_EXCLUSIVE )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "Can't get exclusive access to CRTC2 - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
}
if (use_input || use_remote) {
if ((res = dfb->CreateEventBuffer( dfb, &buffer )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Can't create event buffer - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
}
if (use_input) {
if ((res = dfb->GetInputDevice( dfb, DIDID_KEYBOARD, &keyboard )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Can't get keyboard - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
if ((res = keyboard->AttachEventBuffer( keyboard, buffer )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Can't attach event buffer to keyboard - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
}
if (use_remote) {
if ((res = dfb->GetInputDevice( dfb, DIDID_REMOTE, &remote )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Can't get remote control - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
if ((res = remote->AttachEventBuffer( remote, buffer )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Can't attach event buffer to remote control - %s\n",
DirectFBErrorString( res ) );
uninit();
return -1;
}
}
return 0;
}
static void release_config( void )
{
if (spicframe)
spicframe->Release( spicframe );
if (spic)
spic->Release( spic );
if (c2frame)
c2frame->Release( c2frame );
if (c1frame)
c1frame->Release( c1frame );
if (besframe)
besframe->Release( besframe );
if (bufs[0])
bufs[0]->Release( bufs[0] );
if (bufs[1])
bufs[1]->Release( bufs[1] );
if (bufs[2])
bufs[2]->Release( bufs[2] );
spicframe = NULL;
spic = NULL;
c2frame = NULL;
c1frame = NULL;
besframe = NULL;
bufs[0] = NULL;
bufs[1] = NULL;
bufs[2] = NULL;
}
static int
config( uint32_t width, uint32_t height,
uint32_t d_width, uint32_t d_height,
uint32_t flags,
char *title,
uint32_t format )
{
DFBResult res;
DFBDisplayLayerConfig dlc;
DFBDisplayLayerConfigFlags failed;
uint32_t out_width;
uint32_t out_height;
release_config();
in_width = width;
in_height = height;
aspect_save_orig(width, height);
aspect_save_prescale(d_width, d_height);
dlc.pixelformat = imgfmt_to_pixelformat( format );
{
/* Draw to a temporary surface */
DFBSurfaceDescription dsc;
dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT |
DSDESC_PIXELFORMAT;
dsc.width = (in_width + 15) & ~15;
dsc.height = (in_height + 15) & ~15;
dsc.pixelformat = dlc.pixelformat;
/* Don't waste video memory since we don't need direct stretchblit */
if (use_bes) {
dsc.flags |= DSDESC_CAPS;
dsc.caps = DSCAPS_SYSTEMONLY;
}
for (num_bufs = 0; num_bufs < 3; num_bufs++) {
if ((res = dfb->CreateSurface( dfb, &dsc, &bufs[num_bufs] )) != DFB_OK) {
if (num_bufs == 0) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Can't create surfaces - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
break;
}
}
frame = bufs[0];
current_buf = 0;
current_ip_buf = 0;
buf_height = dsc.height;
}
frame->GetPixelFormat( frame, &frame_format );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: Video surface %dx%d %s\n",
in_width, in_height,
pixelformat_name( frame_format ) );
/*
* BES
*/
if (use_bes) {
aspect_save_screenres( 0x10000, 0x10000 );
aspect( &out_width, &out_height, A_ZOOM );
besrect.x = (0x10000 - out_width) * in_width / out_width / 2;
besrect.y = (0x10000 - out_height) * in_height / out_height / 2;
besrect.w = in_width;
besrect.h = in_height;
dlc.flags = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE;
dlc.width = besrect.w + besrect.x * 2;
dlc.height = besrect.h + besrect.y * 2;
dlc.buffermode = buffermode;
if ((res = bes->TestConfiguration( bes, &dlc, &failed )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Invalid BES configuration - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
if ((res = bes->SetConfiguration( bes, &dlc )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: BES configuration failed - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
bes->GetSurface( bes, &besframe );
besframe->SetBlittingFlags( besframe, DSBLIT_NOFX );
bes->SetScreenLocation( bes, 0.0, 0.0, 1.0, 1.0 );
besframe->Clear( besframe, 0, 0, 0, 0xff );
besframe->Flip( besframe, NULL, 0 );
besframe->Clear( besframe, 0, 0, 0, 0xff );
besframe->Flip( besframe, NULL, 0 );
besframe->Clear( besframe, 0, 0, 0, 0xff );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: BES using %s buffering\n",
dlc.buffermode == DLBM_TRIPLE ? "triple" :
dlc.buffermode == DLBM_BACKVIDEO ? "double" : "single" );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: BES surface %dx%d %s\n", dlc.width, dlc.height, pixelformat_name( dlc.pixelformat ) );
}
/*
* CRTC1
*/
if (use_crtc1) {
dlc.flags = DLCONF_BUFFERMODE;
dlc.buffermode = buffermode;
if ((res = crtc1->TestConfiguration( crtc1, &dlc, &failed )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Invalid CRTC1 configuration - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
if ((res = crtc1->SetConfiguration( crtc1, &dlc )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: CRTC1 configuration failed - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
if ((res = crtc1->GetConfiguration( crtc1, &dlc )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Getting CRTC1 configuration failed - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
crtc1->GetSurface( crtc1, &c1frame );
c1frame->SetBlittingFlags( c1frame, DSBLIT_NOFX );
c1frame->SetColor( c1frame, 0, 0, 0, 0xff );
c1frame->GetSize( c1frame, &screen_width, &screen_height );
aspect_save_screenres( screen_width, screen_height );
aspect( &out_width, &out_height, (flags & VOFLAG_FULLSCREEN) ? A_ZOOM : A_NOZOOM );
if (in_width != out_width || in_height != out_height)
c1stretch = 1;
else
c1stretch = 0;
c1rect.x = (screen_width - out_width) / 2;
c1rect.y = (screen_height - out_height) / 2;
c1rect.w = out_width;
c1rect.h = out_height;
c1frame->Clear( c1frame, 0, 0, 0, 0xff );
c1frame->Flip( c1frame, NULL, 0 );
c1frame->Clear( c1frame, 0, 0, 0, 0xff );
c1frame->Flip( c1frame, NULL, 0 );
c1frame->Clear( c1frame, 0, 0, 0, 0xff );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: CRTC1 using %s buffering\n",
dlc.buffermode == DLBM_TRIPLE ? "triple" :
dlc.buffermode == DLBM_BACKVIDEO ? "double" : "single" );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: CRTC1 surface %dx%d %s\n", screen_width, screen_height, pixelformat_name( dlc.pixelformat ) );
}
/*
* CRTC2
*/
if (use_crtc2) {
dlc.flags = DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE | DLCONF_OPTIONS;
dlc.buffermode = buffermode;
dlc.options = DLOP_NONE;
if (field_parity != -1) {
dlc.options |= DLOP_FIELD_PARITY;
}
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: Field parity set to: ");
switch (field_parity) {
case -1:
mp_msg( MSGT_VO, MSGL_INFO, "Don't care\n");
break;
case 0:
mp_msg( MSGT_VO, MSGL_INFO, "Top field first\n");
break;
case 1:
mp_msg( MSGT_VO, MSGL_INFO, "Bottom field first\n");
break;
}
switch (dlc.pixelformat) {
case DSPF_I420:
case DSPF_YV12:
/* sub-picture supported */
break;
case DSPF_YUY2:
case DSPF_UYVY:
/* Blit to YUY2/UYVY not supported */
dlc.pixelformat = DSPF_ARGB;
/* fall through */
default:
/* sub-picture not supported */
use_spic = 0;
}
if ((res = crtc2->TestConfiguration( crtc2, &dlc, &failed )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Invalid CRTC2 configuration - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
if ((res = crtc2->SetConfiguration( crtc2, &dlc )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: CRTC2 configuration failed - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
if (field_parity != -1)
crtc2->SetFieldParity( crtc2, field_parity );
crtc2->GetSurface( crtc2, &c2frame );
c2frame->SetBlittingFlags( c2frame, DSBLIT_NOFX );
c2frame->SetColor( c2frame, 0, 0, 0, 0xff );
c2frame->GetSize( c2frame, &screen_width, &screen_height );
/* Don't stretch only slightly smaller videos */
if ((in_width > (0.95 * screen_width)) &&
(in_width < screen_width))
out_width = in_width;
else
out_width = screen_width;
if ((in_height > (0.95 * screen_height)) &&
(in_height < screen_height))
out_height = in_height;
else
out_height = screen_height;
aspect_save_screenres( out_width, out_height );
aspect( &out_width, &out_height, (flags & VOFLAG_FULLSCREEN) ? A_ZOOM : A_NOZOOM );
if (in_width != out_width ||
in_height != out_height)
c2stretch = 1;
else
c2stretch = 0;
c2rect.x = (screen_width - out_width) / 2;
c2rect.y = (screen_height - out_height) / 2;
c2rect.w = out_width;
c2rect.h = out_height;
c2frame->Clear( c2frame, 0, 0, 0, 0xff );
c2frame->Flip( c2frame, NULL, 0 );
c2frame->Clear( c2frame, 0, 0, 0, 0xff );
c2frame->Flip( c2frame, NULL, 0 );
c2frame->Clear( c2frame, 0, 0, 0, 0xff );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: CRTC2 using %s buffering\n",
dlc.buffermode == DLBM_TRIPLE ? "triple" :
dlc.buffermode == DLBM_BACKVIDEO ? "double" : "single" );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: CRTC2 surface %dx%d %s\n", screen_width, screen_height, pixelformat_name( dlc.pixelformat ) );
} else {
use_spic = 0;
}
/*
* Sub-picture
*/
if (use_spic) {
/* Draw OSD to sub-picture surface */
IDirectFBPalette *palette;
DFBColor color;
int i;
struct layer_enum l = {
"Matrox CRTC2 Sub-Picture",
&spic,
DFB_UNSUPPORTED
};
dfb->EnumDisplayLayers( dfb, get_layer_by_name, &l );
if (l.res != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "vo_dfbmga: Can't get sub-picture layer - %s\n",
DirectFBErrorString( l.res ) );
return -1;
}
if ((res = spic->SetCooperativeLevel( spic, DLSCL_EXCLUSIVE )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR, "Can't get exclusive access to sub-picture - %s\n",
DirectFBErrorString( res ) );
return -1;
}
dlc.flags = DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE;
dlc.pixelformat = DSPF_ALUT44;
dlc.buffermode = buffermode;
dlc.flags |= DLCONF_OPTIONS;
dlc.options = DLOP_ALPHACHANNEL;
if ((res = spic->TestConfiguration( spic, &dlc, &failed )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Invalid sub-picture configuration - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
if ((res = spic->SetConfiguration( spic, &dlc )) != DFB_OK) {
mp_msg( MSGT_VO, MSGL_ERR,
"vo_dfbmga: Sub-picture configuration failed - %s!\n",
DirectFBErrorString( res ) );
return -1;
}
spic->GetSurface( spic, &spicframe );
spicframe->GetPalette( spicframe, &palette );
color.a = 0xff;
for (i = 0; i < 16; i++) {
color.r = i * 17;
color.g = i * 17;
color.b = i * 17;
palette->SetEntries( palette, &color, 1, i );
}
palette->Release( palette );
spicframe->Clear( spicframe, 0, 0, 0, 0 );
spicframe->Flip( spicframe, NULL, 0 );
spicframe->Clear( spicframe, 0, 0, 0, 0 );
spicframe->Flip( spicframe, NULL, 0 );
spicframe->Clear( spicframe, 0, 0, 0, 0 );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: Sub-picture layer using %s buffering\n",
dlc.buffermode == DLBM_TRIPLE ? "triple" :
dlc.buffermode == DLBM_BACKVIDEO ? "double" : "single" );
subframe = spicframe;
subrect = NULL;
} else if (use_crtc2) {
/* Draw OSD to CRTC2 surface */
subframe = c2frame;
subrect = &c2rect;
} else if (use_crtc1) {
/* Draw OSD to CRTC1 surface */
subframe = c1frame;
subrect = &c1rect;
} else {
/* Draw OSD to BES surface */
subframe = besframe;
subrect = &besrect;
}
subframe->GetSize( subframe, &sub_width, &sub_height );
subframe->GetPixelFormat( subframe, &subframe_format );
mp_msg( MSGT_VO, MSGL_INFO, "vo_dfbmga: Sub-picture surface %dx%d %s (%s)\n",
sub_width, sub_height,
pixelformat_name( subframe_format ),
use_crtc2 ? (use_spic ? "Sub-picture layer" : "CRTC2") :
use_crtc1 ? "CRTC1" : "BES" );
osd_dirty = 0;
osd_current = 1;
blit_done = 0;
return 0;
}
static int
query_format( uint32_t format )
{
switch (format) {
case IMGFMT_YV12:
case IMGFMT_I420:
case IMGFMT_IYUV:
if (is_g200 || use_crtc1)
return 0;
break;
case IMGFMT_BGR32:
case IMGFMT_BGR16:
case IMGFMT_BGR15:
if (is_g200 && use_bes)
return 0;
break;
case IMGFMT_UYVY:
if (is_g200)
return 0;
break;
case IMGFMT_YUY2:
break;
case IMGFMT_NV12:
case IMGFMT_NV21:
if (use_crtc1 || use_crtc2)
return 0;
break;
default:
return 0;
}
return VFCAP_HWSCALE_UP |
VFCAP_HWSCALE_DOWN |
VFCAP_CSP_SUPPORTED_BY_HW |
VFCAP_CSP_SUPPORTED |
VFCAP_OSD;
}
static void
vo_draw_alpha_alut44( int w, int h,
unsigned char* src,
unsigned char *srca,
int srcstride,
unsigned char* dst,
int dststride )
{
int x;
while (h--) {
for (x = 0; x < w; x++) {
if (srca[x])
dst[x] = ((255 - srca[x]) & 0xF0) | (src[x] >> 4);
}
src += srcstride;
srca += srcstride;
dst += dststride;
}
}
static void
clear_alpha( int x0, int y0,
int w, int h )
{
if (use_spic && !flipping && vo_osd_changed_flag)
subframe->FillRectangle( subframe, x0, y0, w, h );
}
static void
draw_alpha( int x0, int y0,
int w, int h,
unsigned char *src,
unsigned char *srca,
int stride )
{
uint8_t *dst;
void *ptr;
int pitch;
if (use_spic) {
if (!osd_changed || (!flipping && !vo_osd_changed_flag))
return;
osd_dirty |= osd_current;
} else {
if (x0 < subrect->x ||
y0 < subrect->y ||
x0 + w > subrect->x + subrect->w ||
y0 + h > subrect->y + subrect->h)
osd_dirty |= osd_current;
}
if (subframe->Lock( subframe, DSLF_READ | DSLF_WRITE, &ptr, &pitch ) != DFB_OK)
return;
dst = ptr;
switch (subframe_format) {
case DSPF_ALUT44:
vo_draw_alpha_alut44( w, h, src, srca, stride,
dst + pitch * y0 + x0,
pitch );
break;
case DSPF_RGB32:
case DSPF_ARGB:
vo_draw_alpha_rgb32( w, h, src, srca, stride,
dst + pitch * y0 + 4 * x0,
pitch );
break;
case DSPF_RGB16:
vo_draw_alpha_rgb16( w, h, src, srca, stride,
dst + pitch * y0 + 2 * x0,
pitch );
break;
case DSPF_ARGB1555:
vo_draw_alpha_rgb15( w, h, src, srca, stride,
dst + pitch * y0 + 2 * x0,
pitch );
break;
case DSPF_YUY2:
vo_draw_alpha_yuy2( w, h, src, srca, stride,
dst + pitch * y0 + 2 * x0,
pitch );
break;
case DSPF_UYVY:
vo_draw_alpha_yuy2( w, h, src, srca, stride,
dst + pitch * y0 + 2 * x0 + 1,
pitch );
break;
case DSPF_NV12:
case DSPF_NV21:
case DSPF_I420:
case DSPF_YV12:
vo_draw_alpha_yv12( w, h, src, srca, stride,
dst + pitch * y0 + x0,
pitch );
break;
}
subframe->Unlock( subframe );
}
static int
draw_frame( uint8_t * src[] )
{
return -1;
}
static int
draw_slice( uint8_t * src[], int stride[], int w, int h, int x, int y )
{
uint8_t *dst;
void *ptr;
int pitch;
if (frame->Lock( frame, DSLF_WRITE, &ptr, &pitch ) != DFB_OK)
return VO_FALSE;
dst = ptr;
memcpy_pic( dst + pitch * y + x, src[0],
w, h, pitch, stride[0] );
dst += pitch * buf_height;
y /= 2;
h /= 2;
if (frame_format == DSPF_NV12 || frame_format == DSPF_NV21) {
memcpy_pic( dst + pitch * y + x, src[1],
w, h, pitch, stride[1] );
} else {
x /= 2;
w /= 2;
pitch /= 2;
if (frame_format == DSPF_I420 )
memcpy_pic( dst + pitch * y + x, src[1],
w, h, pitch, stride[1] );
else
memcpy_pic( dst + pitch * y + x, src[2],
w, h, pitch, stride[2] );
dst += pitch * buf_height / 2;
if (frame_format == DSPF_I420 )
memcpy_pic( dst + pitch * y + x, src[2],
w, h, pitch, stride[2] );
else
memcpy_pic( dst + pitch * y + x, src[1],
w, h, pitch, stride[1] );
}
frame->Unlock( frame );
return VO_TRUE;
}
static void
blit_to_screen( void )
{
IDirectFBSurface *blitsrc = frame;
DFBRectangle *srect = NULL;
if (use_bes) {
if (vo_vsync && !flipping)
bes->WaitForSync( bes );
besframe->Blit( besframe, blitsrc, NULL, besrect.x, besrect.y );
blitsrc = besframe;
srect = &besrect;
}
if (use_crtc1) {
if (vo_vsync && !flipping)
crtc1->WaitForSync( crtc1 );
if (c1stretch)
c1frame->StretchBlit( c1frame, blitsrc, srect, &c1rect );
else
c1frame->Blit( c1frame, blitsrc, srect, c1rect.x, c1rect.y );
}
if (use_crtc2) {
if (vo_vsync && !flipping)
crtc2->WaitForSync( crtc2 );
if (c2stretch)
c2frame->StretchBlit( c2frame, blitsrc, srect, &c2rect );
else
c2frame->Blit( c2frame, blitsrc, srect, c2rect.x, c2rect.y );
}
}
static void
draw_osd( void )
{
frame = bufs[current_buf];
frame->Unlock( frame );
osd_changed = vo_osd_changed( 0 );
if (osd_dirty & osd_current) {
if (use_spic) {
if (flipping)
subframe->Clear( subframe, 0, 0, 0, 0 );
} else {
/* Clear black bars around the picture */
subframe->FillRectangle( subframe,
0, 0,
sub_width, subrect->y );
subframe->FillRectangle( subframe,
0, subrect->y + subrect->h,
sub_width, subrect->y );
subframe->FillRectangle( subframe,
0, subrect->y,
subrect->x, subrect->h );
subframe->FillRectangle( subframe,
subrect->x + subrect->w, subrect->y,
subrect->x, subrect->h );
}
osd_dirty &= ~osd_current;
}
blit_to_screen();
blit_done = 1;
vo_remove_text( sub_width, sub_height, clear_alpha );
vo_draw_text( sub_width, sub_height, draw_alpha );
if (use_spic && flipping && osd_changed) {
subframe->Flip( subframe, NULL, 0 );
osd_current <<= 1;
if (osd_current > osd_max)
osd_current = 1;
}
}
static void
flip_page( void )
{
if (!blit_done)
blit_to_screen();
if (flipping) {
if (use_crtc2)
c2frame->Flip( c2frame, NULL, vo_vsync ? DSFLIP_WAITFORSYNC : DSFLIP_ONSYNC );
if (use_crtc1)
c1frame->Flip( c1frame, NULL, vo_vsync ? DSFLIP_WAITFORSYNC : DSFLIP_ONSYNC );
if (use_bes)
besframe->Flip( besframe, NULL, vo_vsync ? DSFLIP_WAITFORSYNC : DSFLIP_ONSYNC );
if (!use_spic) {
osd_current <<= 1;
if (osd_current > osd_max)
osd_current = 1;
}
}
blit_done = 0;
current_buf = 0;
}
static void
uninit( void )
{
release_config();
if (buffer)
buffer->Release( buffer );
if (remote)
remote->Release( remote );
if (keyboard)
keyboard->Release( keyboard );
if (crtc2)
crtc2->Release( crtc2 );
if (bes)
bes->Release( bes );
if (crtc1)
crtc1->Release( crtc1 );
if (dfb)
dfb->Release( dfb );
buffer = NULL;
remote = NULL;
keyboard = NULL;
crtc2 = NULL;
bes = NULL;
crtc1 = NULL;
dfb = NULL;
}
static uint32_t
get_image( mp_image_t *mpi )
{
int buf = current_buf;
uint8_t *dst;
void *ptr;
int pitch;
if (mpi->flags & MP_IMGFLAG_READABLE &&
(mpi->type == MP_IMGTYPE_IPB || mpi->type == MP_IMGTYPE_IP)) {
if (num_bufs < 2)
return VO_FALSE;
current_ip_buf ^= 1;
if (mpi->type == MP_IMGTYPE_IPB && num_bufs < 3 && current_ip_buf)
return VO_FALSE;
buf = current_ip_buf;
if (mpi->type == MP_IMGTYPE_IPB)
buf++;
}
frame = bufs[buf];
frame->Unlock( frame );
/* Always use DSLF_READ to preserve system memory copy */
if (frame->Lock( frame, DSLF_WRITE | DSLF_READ,
&ptr, &pitch ) != DFB_OK)
return VO_FALSE;
dst = ptr;
if ((mpi->width == pitch) ||
(mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH))) {
mpi->planes[0] = dst;
mpi->width = in_width;
mpi->stride[0] = pitch;
if (mpi->flags & MP_IMGFLAG_PLANAR) {
if (mpi->num_planes > 2) {
mpi->stride[1] = mpi->stride[2] = pitch / 2;
if (mpi->flags & MP_IMGFLAG_SWAPPED) {
/* I420 */
mpi->planes[1] = dst + buf_height * pitch;
mpi->planes[2] = mpi->planes[1] + buf_height * pitch / 4;
} else {
/* YV12 */
mpi->planes[2] = dst + buf_height * pitch;
mpi->planes[1] = mpi->planes[2] + buf_height * pitch / 4;
}
} else {
/* NV12/NV21 */
mpi->stride[1] = pitch;
mpi->planes[1] = dst + buf_height * pitch;
}
}
mpi->flags |= MP_IMGFLAG_DIRECT;
mpi->priv = (void *) buf;
current_buf = buf;
return VO_TRUE;
}
frame->Unlock( frame );
return VO_FALSE;
}
static uint32_t
draw_image( mp_image_t *mpi )
{
if (mpi->flags & MP_IMGFLAG_DIRECT) {
current_buf = (int) mpi->priv;
return VO_TRUE;
}
if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
return VO_TRUE;
if (mpi->flags & MP_IMGFLAG_PLANAR)
return draw_slice( mpi->planes, mpi->stride,
mpi->w, mpi->h, 0, 0 );
else {
void *dst;
int pitch;
if (frame->Lock( frame, DSLF_WRITE, &dst, &pitch ) != DFB_OK)
return VO_FALSE;
memcpy_pic( dst, mpi->planes[0],
mpi->w * (mpi->bpp / 8), mpi->h,
pitch, mpi->stride[0] );
frame->Unlock( frame );
return VO_TRUE;
}
}
static int
set_equalizer( const char *data, int value )
{
DFBResult res;
DFBColorAdjustment ca;
float factor = (float) 0xffff / 200.0;
ca.flags = DCAF_NONE;
if (!strcasecmp( data, "brightness" )) {
ca.flags |= DCAF_BRIGHTNESS;
ca.brightness = value * factor + 0x8000;
}
if (!strcasecmp( data, "contrast" )) {
ca.flags |= DCAF_CONTRAST;
ca.contrast = value * factor + 0x8000;
}
if (!strcasecmp( data, "hue" )) {
ca.flags |= DCAF_HUE;
ca.hue = value * factor + 0x8000;
}
if (!strcasecmp( data, "saturation" )) {
ca.flags |= DCAF_SATURATION;
ca.saturation = value * factor + 0x8000;
}
/* Prefer CRTC2 over BES */
if (use_crtc2)
res = crtc2->SetColorAdjustment( crtc2, &ca );
else if (use_crtc1)
res = crtc1->SetColorAdjustment( crtc1, &ca );
else
res = bes->SetColorAdjustment( bes, &ca );
if (res != DFB_OK)
return VO_FALSE;
return VO_TRUE;
}
static int
get_equalizer( const char *data, int *value )
{
DFBResult res;
DFBColorAdjustment ca;
float factor = 200.0 / (float) 0xffff;
/* Prefer CRTC2 over BES */
if (use_crtc2)
res = crtc2->GetColorAdjustment( crtc2, &ca );
else if (use_crtc1)
res = crtc1->GetColorAdjustment( crtc1, &ca );
else
res = bes->GetColorAdjustment( bes, &ca );
if (res != DFB_OK)
return VO_FALSE;
if (!strcasecmp( data, "brightness" ) &&
(ca.flags & DCAF_BRIGHTNESS))
*value = (ca.brightness - 0x8000) * factor;
if (!strcasecmp( data, "contrast" ) &&
(ca.flags & DCAF_CONTRAST))
*value = (ca.contrast - 0x8000) * factor;
if (!strcasecmp( data, "hue" ) &&
(ca.flags & DCAF_HUE))
*value = (ca.hue - 0x8000) * factor;
if (!strcasecmp( data, "saturation" ) &&
(ca.flags & DCAF_SATURATION))
*value = (ca.saturation - 0x8000) * factor;
return VO_TRUE;
}
static int
control( uint32_t request, void *data)
{
switch (request) {
case VOCTRL_QUERY_FORMAT:
return query_format( *((uint32_t *) data) );
case VOCTRL_GET_IMAGE:
return get_image( data );
case VOCTRL_DRAW_IMAGE:
return draw_image( data );
case VOCTRL_SET_EQUALIZER:
{
struct voctrl_set_equalizer_args *args = data;
return set_equalizer(args->name, args->value);
}
case VOCTRL_GET_EQUALIZER:
{
struct voctrl_get_equalizer_args *args = data;
return get_equalizer(args->name, args->valueptr);
}
}
return VO_NOTIMPL;
}
static void
check_events( void )
{
DFBInputEvent event;
if (!buffer)
return;
if (buffer->GetEvent( buffer, DFB_EVENT( &event )) == DFB_OK) {
if (event.type == DIET_KEYPRESS) {
switch (event.key_symbol) {
case DIKS_ESCAPE:
mplayer_put_key( KEY_ESC );
break;
case DIKS_PAGE_UP:
mplayer_put_key( KEY_PAGE_UP );
break;
case DIKS_PAGE_DOWN:
mplayer_put_key( KEY_PAGE_DOWN );
break;
case DIKS_CURSOR_UP:
mplayer_put_key( KEY_UP );
break;
case DIKS_CURSOR_DOWN:
mplayer_put_key( KEY_DOWN );
break;
case DIKS_CURSOR_LEFT:
mplayer_put_key( KEY_LEFT );
break;
case DIKS_CURSOR_RIGHT:
mplayer_put_key( KEY_RIGHT );
break;
case DIKS_INSERT:
mplayer_put_key( KEY_INSERT );
break;
case DIKS_DELETE:
mplayer_put_key( KEY_DELETE );
break;
case DIKS_HOME:
mplayer_put_key( KEY_HOME );
break;
case DIKS_END:
mplayer_put_key( KEY_END );
break;
case DIKS_POWER:
mplayer_put_key( KEY_POWER );
break;
case DIKS_MENU:
mplayer_put_key( KEY_MENU );
break;
case DIKS_PLAY:
mplayer_put_key( KEY_PLAY );
break;
case DIKS_STOP:
mplayer_put_key( KEY_STOP );
break;
case DIKS_PAUSE:
mplayer_put_key( KEY_PAUSE );
break;
case DIKS_PLAYPAUSE:
mplayer_put_key( KEY_PLAYPAUSE );
break;
case DIKS_FORWARD:
mplayer_put_key( KEY_FORWARD );
break;
case DIKS_NEXT:
mplayer_put_key( KEY_NEXT );
break;
case DIKS_REWIND:
mplayer_put_key( KEY_REWIND );
break;
case DIKS_PREVIOUS:
mplayer_put_key( KEY_PREV );
break;
case DIKS_VOLUME_UP:
mplayer_put_key( KEY_VOLUME_UP );
break;
case DIKS_VOLUME_DOWN:
mplayer_put_key( KEY_VOLUME_DOWN );
break;
case DIKS_MUTE:
mplayer_put_key( KEY_MUTE );
break;
default:
mplayer_put_key( event.key_symbol );
}
}
}
/*
* empty buffer, because of repeating
* keyboard repeat is faster than key handling and this causes problems during seek
* temporary workabout. should be solved in the future
*/
buffer->Reset( buffer );
}