/* * MPlayer video driver for DirectFramebuffer device * * copyright (C) 2002 Jiri Svoboda * * based on vo_directfb2.c * * 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 #include #include #include #include #include #include "config.h" #include "video_out.h" #include "fastmemcpy.h" #include "sub/sub.h" #include "mp_msg.h" #include "aspect.h" #include "subopt-helper.h" #include "mp_fifo.h" #include "input/keycodes.h" #include "libmpcodecs/mp_image.h" #include "libmpcodecs/vfcap.h" #include "osd.h" // triple buffering #define TRIPLE 1 /****************************** * vo_directfb globals * ******************************/ #define DFBCHECK(x ...) \ do { \ DFBResult err = x; \ \ if (err != DFB_OK) \ { \ fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__); \ DirectFBErrorFatal(# x, err); \ } \ } while (0) /* * filled by preinit */ // main DirectFB handle static IDirectFB *dfb = NULL; // keyboard handle static IDirectFBInputDevice *keyboard = NULL; // A buffer for input events. static IDirectFBEventBuffer *buffer = NULL; /* * filled during config */ // handle of used layer static IDirectFBDisplayLayer *layer = NULL; // surface of used layer static IDirectFBSurface *primary = NULL; static int primarylocked = 0; // handle of temporary surface (if used) static IDirectFBSurface *frame = NULL; static int framelocked = 0; // flipping mode flag (layer/surface) static int flipping = 0; // scaling flag static int stretch = 0; // picture position static int xoffset = 0, yoffset = 0; // picture size static int out_width = 0, out_height = 0; // frame/primary size static int width = 0, height = 0; // frame primary format DFBSurfacePixelFormat pixel_format; /* static void (*draw_alpha_p)(int w, int h, unsigned char *src, unsigned char *srca, int stride, unsigned char *dst, int dstride); */ /****************************** * cmd line parameteres * ******************************/ /* command line/config file options */ static int layer_id = -1; static int buffer_mode = 1; static int use_input = 1; static int field_parity = -1; /****************************** * implementation * ******************************/ static void unlock(void) { if (frame && framelocked) frame->Unlock(frame); if (primary && primarylocked) primary->Unlock(primary); } static int get_parity(strarg_t *arg) { if (strargcmp(arg, "top") == 0) return 0; if (strargcmp(arg, "bottom") == 0) return 1; return -1; } static int check_parity(void *arg) { return get_parity(arg) != -1; } static int get_mode(strarg_t *arg) { if (strargcmp(arg, "single") == 0) return 1; if (strargcmp(arg, "double") == 0) return 2; if (strargcmp(arg, "triple") == 0) return 3; return 0; } static int check_mode(void *arg) { return get_mode(arg) != 0; } static int preinit(struct vo *vo, const char *arg) { DFBResult ret; strarg_t mode_str = { 0, NULL }; strarg_t par_str = { 0, NULL }; strarg_t dfb_params = { 0, NULL }; const opt_t subopts[] = { {"input", OPT_ARG_BOOL, &use_input, NULL}, {"buffermode", OPT_ARG_STR, &mode_str, check_mode}, {"fieldparity", OPT_ARG_STR, &par_str, check_parity}, {"layer", OPT_ARG_INT, &layer_id, NULL}, {"dfbopts", OPT_ARG_STR, &dfb_params, NULL}, {NULL} }; //mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Preinit entered\n"); if (dfb) return 0; // we are already initialized! // set defaults buffer_mode = 1 + vo_doublebuffering; // honor -double switch layer_id = -1; use_input = 1; field_parity = -1; if (subopt_parse(arg, subopts) != 0) { mp_msg(MSGT_VO, MSGL_ERR, "\n-vo directfb command line help:\n" "Example: mplayer -vo directfb:layer=1:buffermode=single\n" "\nOptions (use 'no' prefix to disable):\n" " input Use DirectFB for keyboard input\n" "\nOther options:\n" " layer=n\n" " n=0..xx Use layer with id n for output (0=primary)\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" " dfbopts=\n" " Specify a parameter list for DirectFB\n" "\n"); return -1; } if (mode_str.len) buffer_mode = get_mode(&mode_str); if (par_str.len) field_parity = get_parity(&par_str); if (dfb_params.len > 0) { int argc = 2; char arg0[10] = "mplayer"; char *arg1 = malloc(dfb_params.len + 7); char *argv[3]; char **a; a = &argv[0]; strcpy(arg1, "--dfb:"); strncat(arg1, dfb_params.str, dfb_params.len); argv[0] = arg0; argv[1] = arg1; argv[2] = NULL; DFBCHECK(DirectFBInit(&argc, &a)); free(arg1); } else { DFBCHECK(DirectFBInit(NULL, NULL)); } if (((directfb_major_version <= 0) && (directfb_minor_version <= 9) && (directfb_micro_version < 15))) { mp_msg(MSGT_VO, MSGL_ERR, "DirectFB: Unsupported DirectFB version\n"); return 1; } /* * (set options) */ // uncomment this if you do not wish to create a new VT for DirectFB // DFBCHECK (DirectFBSetOption ("no-vt-switch","")); // uncomment this if you want to allow VT switching // DFBCHECK (DirectFBSetOption ("vt-switching","")); // uncomment this if you want to hide gfx cursor (req dfb >=0.9.9) DFBCHECK(DirectFBSetOption("no-cursor", "")); // bg color fix DFBCHECK(DirectFBSetOption("bg-color", "00000000")); /* * (Initialize) */ DFBCHECK(DirectFBCreate(&dfb)); /* * (Get keyboard) */ if (use_input) { ret = dfb->GetInputDevice(dfb, DIDID_KEYBOARD, &keyboard); if (ret == DFB_OK) mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Keyboard init OK\n"); else { keyboard = NULL; mp_msg(MSGT_VO, MSGL_ERR, "DirectFB: Keyboard init FAILED\n"); } } /* * Create an input buffer for the keyboard. */ if (keyboard) DFBCHECK(keyboard->CreateEventBuffer(keyboard, &buffer)); // just to start clean ... if (buffer) buffer->Reset(buffer); //mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Preinit OK\n"); return 0; } static DFBSurfacePixelFormat convformat(uint32_t format) { // add more formats !!! switch (format) { case IMGFMT_RGB32: return DSPF_RGB32; break; case IMGFMT_BGR32: return DSPF_RGB32; break; case IMGFMT_RGB24: return DSPF_RGB24; break; case IMGFMT_BGR24: return DSPF_RGB24; break; case IMGFMT_RGB16: return DSPF_RGB16; break; case IMGFMT_BGR16: return DSPF_RGB16; break; case IMGFMT_RGB15: return DSPF_ARGB1555; break; case IMGFMT_BGR15: return DSPF_ARGB1555; break; case IMGFMT_RGB12: return DSPF_ARGB4444; break; case IMGFMT_BGR12: return DSPF_ARGB4444; break; case IMGFMT_YUY2: return DSPF_YUY2; break; case IMGFMT_UYVY: return DSPF_UYVY; break; case IMGFMT_YV12: return DSPF_YV12; break; case IMGFMT_I420: return DSPF_I420; break; // case IMGFMT_IYUV: return DSPF_IYUV; break; case IMGFMT_RGB8: return DSPF_RGB332; break; case IMGFMT_BGR8: return DSPF_RGB332; break; default: return 0; } return 0; } typedef struct enum1_s { uint32_t format; int scale; int result; unsigned int id; unsigned int width; unsigned int height; int setsize; } enum1_t; static DFBEnumerationResult test_format_callback( unsigned int id, DFBDisplayLayerDescription desc, void *data) { enum1_t *params = (enum1_t *)data; IDirectFBDisplayLayer *layer; DFBResult ret; if ((layer_id == -1) || (layer_id == id)) { ret = dfb->GetDisplayLayer(dfb, id, &layer); if (ret) { DirectFBError("dfb->GetDisplayLayer failed", ret); return DFENUM_OK; } else { DFBDisplayLayerConfig dlc; if (params->setsize) { dlc.flags = DLCONF_WIDTH | DLCONF_HEIGHT; dlc.width = params->width; dlc.height = params->height; layer->SetConfiguration(layer, &dlc); } dlc.flags = DLCONF_PIXELFORMAT; dlc.pixelformat = convformat(params->format); layer->SetOpacity(layer, 0); ret = layer->TestConfiguration(layer, &dlc, NULL); layer->Release(layer); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Test format - layer %i scale/pos %i\n", id, (desc.caps & DLCAPS_SCREEN_LOCATION)); if (ret == DFB_OK) { // printf("Test OK\n"); if (params->result) { if ((!params->scale) && (desc.caps & DLCAPS_SCREEN_LOCATION)) { params->scale = 1; params->id = id; mp_msg( MSGT_VO, MSGL_DBG2, "DirectFB: Test format - added layer %i scale/pos %i\n", id, (desc.caps & DLCAPS_SCREEN_LOCATION)); } } else { params->result = 1; params->id = id; if (desc.caps & DLCAPS_SCREEN_LOCATION) params->scale = 1; mp_msg( MSGT_VO, MSGL_DBG2, "DirectFB: Test format - added layer %i scale/pos %i\n", id, (desc.caps & DLCAPS_SCREEN_LOCATION)); }; } ; }; } ; return DFENUM_OK; } static int query_format(uint32_t format) { int ret = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD; // osd should be removed the in future -> will be handled outside... enum1_t params; if (!convformat(format)) return 0; // temporarily disable YV12 // if (format == IMGFMT_YV12) return 0; // if (format == IMGFMT_I420) return 0; if (format == IMGFMT_IYUV) return 0; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Format query: %s\n", vo_format_name(format)); params.format = format; params.scale = 0; params.result = 0; params.setsize = 0; DFBCHECK(dfb->EnumDisplayLayers(dfb, test_format_callback, ¶ms)); if (params.result) { if (params.scale) ret |= VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN; return ret; } return 0; } typedef struct videomode_s { int width; int height; int out_width; int out_height; int overx; int overy; int bpp; } videomode_t; static DFBEnumerationResult video_modes_callback(int width, int height, int bpp, void *data) { videomode_t *params = (videomode_t *)data; int overx = 0, overy = 0, closer = 0, over = 0; int we_are_under = 0; //mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Validator entered %i %i %i\n",width,height,bpp); overx = width - params->out_width; overy = height - params->out_height; if (!params->width) { params->width = width; params->height = height; params->overx = overx; params->overy = overy; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Mode added %i %i %i\n", width, height, bpp); } if ((params->overy < 0) || (params->overx < 0)) we_are_under = 1; // stored mode is smaller than req mode if (abs(overx * overy) < abs(params->overx * params->overy)) closer = 1; // current mode is closer to desired res if ((overx >= 0) && (overy >= 0)) over = 1; // current mode is bigger or equaul to desired res if ((closer && (over || we_are_under)) || (we_are_under && over)) { params->width = width; params->height = height; params->overx = overx; params->overy = overy; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Better mode added %i %i %i\n", width, height, bpp); } ; return DFENUM_OK; } #define CONFIG_ERROR -1 static int config(struct vo *vo, uint32_t s_width, uint32_t s_height, uint32_t d_width, uint32_t d_height, uint32_t flags, uint32_t format) { /* * (Locals) */ // decode flags int fs = flags & VOFLAG_FULLSCREEN; int vm = flags & VOFLAG_MODESWITCHING; DFBSurfaceDescription dsc; DFBResult ret; DFBDisplayLayerConfig dlc; DFBSurfaceCapabilities caps; enum1_t params; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config entered [%ix%i]\n", s_width, s_height); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: With requested format: %s\n", vo_format_name( format)); // initial cleanup if (frame) { frame->Release(frame); frame = NULL; } if (primary) { primary->Release(primary); primary = NULL; } if (layer) { layer->Release(layer); layer = NULL; } // vm things if (vm) { videomode_t params; params.out_width = d_width; params.out_height = d_height; params.width = 0; params.height = 0; switch (format) { case IMGFMT_RGB32: case IMGFMT_BGR32: params.bpp = 32; break; case IMGFMT_RGB24: case IMGFMT_BGR24: params.bpp = 24; break; case IMGFMT_RGB16: case IMGFMT_BGR16: case IMGFMT_RGB15: case IMGFMT_BGR15: case IMGFMT_RGB12: case IMGFMT_BGR12: params.bpp = 16; break; default: params.bpp = 0; } mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - trying to change videomode\n"); DFBCHECK(dfb->EnumVideoModes(dfb, video_modes_callback, ¶ms)); ret = dfb->SetVideoMode(dfb, params.width, params.height, params.bpp); if (ret) { ret = dfb->SetVideoMode(dfb, params.width, params.height, 24); if (ret) { ret = dfb->SetVideoMode(dfb, params.width, params.height, 32); if (ret) { ret = dfb->SetVideoMode(dfb, params.width, params.height, 16); if (ret) ret = dfb->SetVideoMode(dfb, params.width, params.height, 8); } } } } // vm end // just to be sure clear primary layer ret = dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer); if (ret == DFB_OK) { ret = layer->GetSurface(layer, &primary); if (ret == DFB_OK) { primary->Clear(primary, 0, 0, 0, 0xff); ret = primary->Flip(primary, NULL, 0); if (ret == DFB_OK) primary->Clear(primary, 0, 0, 0, 0xff); primary->Release(primary); } primary = NULL; layer->Release(layer); } layer = NULL; // find best layer mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - looking for suitable layer\n"); params.format = format; params.scale = 0; params.result = 0; params.width = s_width; params.height = s_height; params.setsize = 1; DFBCHECK(dfb->EnumDisplayLayers(dfb, test_format_callback, ¶ms)); if (!params.result) { mp_msg(MSGT_VO, MSGL_ERR, "DirectFB: ConfigError - no suitable layer found\n"); params.id = DLID_PRIMARY; } mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - layer %i\n", params.id); // setup layer DFBCHECK(dfb->GetDisplayLayer(dfb, params.id, &layer)); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - switching layer to exclusive mode\n"); ret = layer->SetCooperativeLevel(layer, DLSCL_EXCLUSIVE); if (DFB_OK != ret) { mp_msg( MSGT_VO, MSGL_WARN, "DirectFB: Warning - cannot switch layer to exclusive mode. This could cause\nproblems. You may need to select correct pixel format manually!\n"); DirectFBError("MPlayer - Switch layer to exlusive mode.", ret); } ; if (params.scale) { mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - changing layer configuration (size)\n"); dlc.flags = DLCONF_WIDTH | DLCONF_HEIGHT; dlc.width = s_width; dlc.height = s_height; ret = layer->SetConfiguration(layer, &dlc); if (ret) { mp_msg(MSGT_VO, MSGL_ERR, "DirectFB: ConfigError in layer configuration (size)\n"); DirectFBError("MPlayer - Layer size change.", ret); } ; } // look if we need to change the pixel format of the layer // and just to be sure also fetch all layer properties dlc.flags = DLCONF_PIXELFORMAT | DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_OPTIONS | DLCONF_BUFFERMODE; ret = layer->GetConfiguration(layer, &dlc); dlc.flags = DLCONF_PIXELFORMAT | DLCONF_WIDTH | DLCONF_HEIGHT; if (ret) mp_msg(MSGT_VO, MSGL_WARN, "DirectFB: Warning - could not get layer properties!\n"); else mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Layer reports format:%x\n", dlc.pixelformat); if ((dlc.pixelformat != convformat(params.format)) || (ret != DFB_OK)) { dlc.flags = DLCONF_PIXELFORMAT; dlc.pixelformat = convformat(params.format); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Desired pixelformat: %x\n", dlc.pixelformat); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - changing layer configuration (format)\n"); ret = layer->SetConfiguration(layer, &dlc); if (ret) { unsigned int bpp; mp_msg( MSGT_VO, MSGL_ERR, "DirectFB: ConfigError in layer configuration (format, flags=%x)\n", dlc.flags); DirectFBError("MPlayer - layer pixelformat change", ret); // ugly fbdev workaround - try to switch pixelformat via videomode change switch (dlc.pixelformat) { case DSPF_ARGB: case DSPF_RGB32: bpp = 32; break; case DSPF_RGB24: bpp = 24; break; case DSPF_RGB16: bpp = 16; break; case DSPF_ARGB1555: bpp = 15; break; case DSPF_ARGB4444: bpp = 12; break; case DSPF_RGB332: bpp = 8; break; } switch (dlc.pixelformat) { case DSPF_ARGB: case DSPF_RGB32: case DSPF_RGB24: case DSPF_RGB16: case DSPF_ARGB1555: case DSPF_ARGB4444: case DSPF_RGB332: mp_msg( MSGT_VO, MSGL_V, "DirectFB: Trying to recover via videomode change (VM).\n"); // get size dlc.flags = DLCONF_WIDTH | DLCONF_HEIGHT; if (DFB_OK == layer->GetConfiguration(layer, &dlc)) { // try to set videomode mp_msg(MSGT_VO, MSGL_V, "DirectFB: Videomode %ix%i BPP %i\n", dlc.width, dlc.height, bpp); ret = dfb->SetVideoMode(dfb, dlc.width, dlc.height, bpp); if (ret) DirectFBError("MPlayer - VM - pixelformat change", ret); } ; //get current pixel format dlc.flags = DLCONF_PIXELFORMAT; ret = layer->GetConfiguration(layer, &dlc); if (ret) DirectFBError("MPlayer - VM - Layer->GetConfiguration", ret); else mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Layer now has pixelformat [%x]\n", dlc.pixelformat); ; // check if we were succesful if ((dlc.pixelformat != convformat(params.format)) || (ret != DFB_OK)) { mp_msg(MSGT_VO, MSGL_INFO, "DirectFB: Recovery failed!.\n"); return CONFIG_ERROR; } break; default: return CONFIG_ERROR; } ; } ; } ; // flipping of layer // try triple, double... buffering dlc.flags = DLCONF_BUFFERMODE; #ifdef TRIPLE if (buffer_mode > 2) { dlc.buffermode = DLBM_TRIPLE; ret = layer->SetConfiguration(layer, &dlc); } else ret = !DFB_OK; if (ret != DFB_OK) { #endif if (buffer_mode > 1) { dlc.buffermode = DLBM_BACKVIDEO; ret = layer->SetConfiguration(layer, &dlc); if (ret != DFB_OK) { dlc.buffermode = DLBM_BACKSYSTEM; ret = layer->SetConfiguration(layer, &dlc); } } if (ret == DFB_OK) mp_msg(MSGT_VO, MSGL_V, "DirectFB: Double buffering is active\n"); #ifdef TRIPLE } else mp_msg(MSGT_VO, MSGL_V, "DirectFB: Triple buffering is active\n"); #endif if (field_parity != -1) { dlc.flags = DLCONF_OPTIONS; ret = layer->GetConfiguration(layer, &dlc); if (ret == DFB_OK) { dlc.options |= DLOP_FIELD_PARITY; ret = layer->SetConfiguration(layer, &dlc); if (ret == DFB_OK) layer->SetFieldParity(layer, field_parity); } } mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Requested field parity: "); switch (field_parity) { case -1: mp_msg(MSGT_VO, MSGL_DBG2, "Don't care\n"); break; case 0: mp_msg(MSGT_VO, MSGL_DBG2, "Top field first\n"); break; case 1: mp_msg(MSGT_VO, MSGL_DBG2, "Bottom field first\n"); break; } // get layer surface ret = layer->GetSurface(layer, &primary); if (ret) { mp_msg(MSGT_VO, MSGL_ERR, "DirectFB: ConfigError - could not get surface\n"); return CONFIG_ERROR; // what shall we report on failure? } // test surface for flipping DFBCHECK(primary->GetCapabilities(primary, &caps)); primary->Clear(primary, 0, 0, 0, 0xff); flipping = 0; if (caps & (DSCAPS_FLIPPING #ifdef TRIPLE | DSCAPS_TRIPLE #endif )) { ret = primary->Flip(primary, NULL, 0); if (ret == DFB_OK) { flipping = 1; primary->Clear(primary, 0, 0, 0, 0xff); #ifdef TRIPLE // if we have 3 buffers clean once more if (caps & DSCAPS_TRIPLE) { primary->Flip(primary, NULL, 0); primary->Clear(primary, 0, 0, 0, 0xff); flipping = 2; } #endif } } ; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - flipping = %i\n", flipping); // is scale needed ? Aspect ratio and layer pos/size // get surface size DFBCHECK(primary->GetSize(primary, &width, &height)); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Config - surface size = %ix%i\n", width, height); aspect_save_orig(vo, s_width, s_height); aspect_save_prescale(vo, d_width, d_height); if (params.scale) { aspect_save_screenres(vo, 10000, 10000); aspect(vo, &out_width, &out_height, A_ZOOM); ret = layer->SetScreenLocation(layer, (1 - (float)out_width / 10000) / 2, (1 - (float)out_height / 10000) / 2, ((float)out_width / 10000), ((float)out_height / 10000)); if (ret) mp_msg(MSGT_VO, MSGL_ERR, "DirectFB: ConfigError in layer configuration (position)\n"); xoffset = 0; yoffset = 0; } else { aspect_save_screenres(vo, width, height); if (fs) /* -fs */ aspect(vo, &out_width, &out_height, A_ZOOM); else aspect(vo, &out_width, &out_height, A_NOZOOM); xoffset = (width - out_width) / 2; yoffset = (height - out_height) / 2; } if (((s_width == out_width) && (s_height == out_height)) || (params.scale)) stretch = 0; else stretch = 1; // temporary buffer in case of not flipping or scaling if ((!flipping) || stretch) { DFBCHECK(primary->GetPixelFormat(primary, &dsc.pixelformat)); dsc.flags = DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_WIDTH; dsc.width = s_width; dsc.height = s_height; DFBCHECK(dfb->CreateSurface(dfb, &dsc, &frame)); DFBCHECK(frame->GetSize(frame, &width, &height)); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Frame is active.\n"); } // get format for draw_alpha - should be removed soon - osd will be rendered outside vo driver if (frame) DFBCHECK(frame->GetPixelFormat(frame, &pixel_format)); else DFBCHECK(primary->GetPixelFormat(primary, &pixel_format)); // finally turn on layer layer->SetOpacity(layer, 255); //mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Config finished [%ix%i] - [%ix%i]\n",out_width,out_height,width,height); return 0; } static int to_mp_key(int sym) { switch (sym) { case DIKS_ESCAPE: return KEY_ESC; case DIKS_PAGE_UP: return KEY_PAGE_UP; case DIKS_PAGE_DOWN: return KEY_PAGE_DOWN; case DIKS_CURSOR_UP: return KEY_UP; case DIKS_CURSOR_DOWN: return KEY_DOWN; case DIKS_CURSOR_LEFT: return KEY_LEFT; case DIKS_CURSOR_RIGHT: return KEY_RIGHT; case DIKS_INSERT: return KEY_INSERT; case DIKS_DELETE: return KEY_DELETE; case DIKS_HOME: return KEY_HOME; case DIKS_END: return KEY_END; default: return sym; } } static void check_events(struct vo *vo) { if (buffer) { DFBInputEvent event; //if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf ("DirectFB: Check events entered\n"); if (buffer->GetEvent(buffer, DFB_EVENT(&event)) == DFB_OK) { if (event.type == DIET_KEYPRESS) { mplayer_put_key(vo->key_fifo, to_mp_key(event.key_symbol)); } } // empty buffer, because of repeating (keyboard repeat is faster than key handling // and this causes problems during seek) // temporary workaround should be solved in the future buffer->Reset(buffer); } //if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf ("DirectFB: Check events finished\n"); } static void flip_page(struct vo *vo) { DFBSurfaceBlittingFlags flags = DSBLIT_NOFX; unlock(); // unlock frame & primary // if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf("DirectFB: Flip page entered"); DFBCHECK(primary->SetBlittingFlags(primary, flags)); if (frame) { if (stretch) { DFBRectangle rect; rect.x = xoffset; rect.y = yoffset; rect.w = out_width; rect.h = out_height; DFBCHECK(primary->StretchBlit(primary, frame, NULL, &rect)); } else { DFBCHECK(primary->Blit(primary, frame, NULL, xoffset, yoffset)); }; } ; #ifdef TRIPLE switch (flipping) { case 1: DFBCHECK(primary->Flip(primary, NULL, DSFLIP_WAIT)); break; case 2: DFBCHECK(primary->Flip(primary, NULL, DSFLIP_ONSYNC)); break; default:; // should never be reached } #else if (flipping) DFBCHECK(primary->Flip(primary, NULL, DSFLIP_WAITFORSYNC)); #endif } static void uninit(struct vo *vo) { //mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Uninit entered\n"); unlock(); /* * (Release) */ /* mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Releasing buffer\n"); if (buffer) buffer->Release (buffer); mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Releasing keyboard\n"); if (keyboard) keyboard->Release (keyboard); */ if (frame) { mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Releasing frame\n"); frame->Release(frame); frame = NULL; } ; // switch off BES // if (layer) layer->SetOpacity(layer,0); if (layer) { mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Releasing layer\n"); layer->Release(layer); layer = NULL; } if (primary) { mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: Releasing primary\n"); primary->Release(primary); primary = NULL; } /* mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Releasing DirectFB library\n"); dfb->Release (dfb); */ //mp_msg(MSGT_VO, MSGL_INFO,"DirectFB: Uninit done.\n"); } static uint32_t directfb_set_video_eq(const char *data, int value) //data==name { DFBColorAdjustment ca; float factor = (float)0xffff / 200.0; DFBDisplayLayerDescription desc; unlock(); if (layer) { layer->GetDescription(layer, &desc); ca.flags = DCAF_NONE; if (!strcmp(data, "brightness")) { if (desc.caps & DLCAPS_BRIGHTNESS) { ca.brightness = value * factor + 0x8000; ca.flags |= DCAF_BRIGHTNESS; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: SetVEq Brightness 0x%X %i\n", ca.brightness, value); } else return VO_FALSE; } if (!strcmp(data, "contrast")) { if ((desc.caps & DLCAPS_CONTRAST)) { ca.contrast = value * factor + 0x8000; ca.flags |= DCAF_CONTRAST; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: SetVEq Contrast 0x%X %i\n", ca.contrast, value); } else return VO_FALSE; } if (!strcmp(data, "hue")) { if ((desc.caps & DLCAPS_HUE)) { ca.hue = value * factor + 0x8000; ca.flags |= DCAF_HUE; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: SetVEq Hue 0x%X %i\n", ca.hue, value); } else return VO_FALSE; } if (!strcmp(data, "saturation")) { if ((desc.caps & DLCAPS_SATURATION)) { ca.saturation = value * factor + 0x8000; ca.flags |= DCAF_SATURATION; mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: SetVEq Saturation 0x%X %i\n", ca.saturation, value); } else return VO_FALSE; } if (ca.flags != DCAF_NONE) { layer->SetColorAdjustment(layer, &ca); return VO_TRUE; } } return VO_FALSE; } static uint32_t directfb_get_video_eq(const char *data, int *value) // data==name { DFBColorAdjustment ca; float factor = 200.0 / (float)0xffff; DFBDisplayLayerDescription desc; if (layer) { unlock(); layer->GetDescription(layer, &desc); layer->GetColorAdjustment(layer, &ca); if (!strcmp(data, "brightness")) { if (desc.caps & DLCAPS_BRIGHTNESS) { *value = (int) ((ca.brightness - 0x8000) * factor); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: GetVEq Brightness 0x%X %i\n", ca.brightness, *value); return VO_TRUE; } else return VO_FALSE; } if (!strcmp(data, "contrast")) { if ((desc.caps & DLCAPS_CONTRAST)) { *value = (int) ((ca.contrast - 0x8000) * factor); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: GetVEq Contrast 0x%X %i\n", ca.contrast, *value); return VO_TRUE; } else return VO_FALSE; } if (!strcmp(data, "hue")) { if ((desc.caps & DLCAPS_HUE)) { *value = (int) ((ca.hue - 0x8000) * factor); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: GetVEq Hue 0x%X %i\n", ca.hue, *value); return VO_TRUE; } else return VO_FALSE; } if (!strcmp(data, "saturation")) { if ((desc.caps & DLCAPS_SATURATION)) { *value = (int) ((ca.saturation - 0x8000) * factor); mp_msg(MSGT_VO, MSGL_DBG2, "DirectFB: GetVEq Saturation 0x%X %i\n", ca.saturation, *value); return VO_TRUE; } else return VO_FALSE; } } return VO_FALSE; } static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y) { int i; unsigned int pitch; uint8_t *dst; uint8_t *dst2; uint8_t *srcp; unsigned int p; // if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf("DirectFB: draw_slice entered\n"); unlock(); if (frame) { DFBCHECK(frame->Lock(frame, DSLF_WRITE | DSLF_READ, (void *)&dst, &pitch)); framelocked = 1; } else { DFBCHECK(primary->Lock(primary, DSLF_WRITE, (void *)&dst, &pitch)); primarylocked = 1; }; p = FFMIN(w, pitch); dst += y * pitch + x; dst2 = dst + pitch * height - y * pitch + y * pitch / 4 - x / 2; srcp = src[0]; for (i = 0; i < h; i++) { fast_memcpy(dst, srcp, p); dst += pitch; srcp += stride[0]; } if (pixel_format == DSPF_YV12) { dst = dst2; srcp = src[2]; p = p / 2; for (i = 0; i < h / 2; i++) { fast_memcpy(dst, srcp, p); dst += pitch / 2; srcp += stride[2]; } dst = dst2 + pitch * height / 4; srcp = src[1]; for (i = 0; i < h / 2; i++) { fast_memcpy(dst, srcp, p); dst += pitch / 2; srcp += stride[1]; } } else { dst = dst2; srcp = src[1]; p = p / 2; for (i = 0; i < h / 2; i++) { fast_memcpy(dst, srcp, p); dst += pitch / 2; srcp += stride[1]; } dst = dst2 + pitch * height / 4; srcp = src[2]; for (i = 0; i < h / 2; i++) { fast_memcpy(dst, srcp, p); dst += pitch / 2; srcp += stride[2]; } } unlock(); return 0; } static uint32_t put_image(mp_image_t *mpi) { // static IDirectFBSurface *tmp = NULL; // DFBSurfaceDescription dsc; // DFBRectangle rect; // if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf("DirectFB: Put_image entered %i %i %i %i %i %i\n",mpi->x,mpi->y,mpi->w,mpi->h,mpi->width,mpi->height); unlock(); // already out? if ((mpi->flags & (MP_IMGFLAG_DIRECT | MP_IMGFLAG_DRAW_CALLBACK))) { // if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf("DirectFB: Put_image - nothing to do\n"); return VO_TRUE; } if (mpi->flags & MP_IMGFLAG_PLANAR) { // memcpy all planes - sad but necessary int i; unsigned int pitch; uint8_t *dst; uint8_t *src; unsigned int p; // if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf("DirectFB: Put_image - planar branch\n"); if (frame) { DFBCHECK(frame->Lock(frame, DSLF_WRITE | DSLF_READ, (void *)&dst, &pitch)); framelocked = 1; } else { DFBCHECK(primary->Lock(primary, DSLF_WRITE, (void *)&dst, &pitch)); primarylocked = 1; }; p = FFMIN(mpi->w, pitch); src = mpi->planes[0]; for (i = 0; i < mpi->h; i++) fast_memcpy(dst + i * pitch, src + i * mpi->stride[0], p); if (pixel_format == DSPF_YV12) { dst += pitch * height; p = p / 2; src = mpi->planes[2]; for (i = 0; i < mpi->h / 2; i++) fast_memcpy(dst + i * pitch / 2, src + i * mpi->stride[2], p); dst += pitch * height / 4; src = mpi->planes[1]; for (i = 0; i < mpi->h / 2; i++) fast_memcpy(dst + i * pitch / 2, src + i * mpi->stride[1], p); } else { dst += pitch * height; p = p / 2; src = mpi->planes[1]; for (i = 0; i < mpi->h / 2; i++) fast_memcpy(dst + i * pitch / 2, src + i * mpi->stride[1], p); dst += pitch * height / 4; src = mpi->planes[2]; for (i = 0; i < mpi->h / 2; i++) fast_memcpy(dst + i * pitch / 2, src + i * mpi->stride[2], p); } unlock(); } else { // I had to disable native directfb blit because it wasn't working under some conditions :-( /* dsc.flags = DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_WIDTH | DSDESC_PREALLOCATED; dsc.preallocated[0].data = mpi->planes[0]; dsc.preallocated[0].pitch = mpi->stride[0]; dsc.width = mpi->width; dsc.height = mpi->height; dsc.pixelformat = convformat(mpi->imgfmt); DFBCHECK (dfb->CreateSurface( dfb, &dsc, &tmp)); rect.x=mpi->x; rect.y=mpi->y; rect.w=mpi->w; rect.h=mpi->h; if (frame) { DFBCHECK (tmp->Blit(tmp,frame,&rect,0,0)); } else { DFBCHECK (tmp->Blit(tmp,primary,&rect,xoffset,yoffset)); }; tmp->Release(tmp); */ unsigned int pitch; uint8_t *dst; // if ( mp_msg_test(MSGT_VO,MSGL_V) ) printf("DirectFB: Put_image - non-planar branch\n"); if (frame) { DFBCHECK(frame->Lock(frame, DSLF_WRITE, (void *)&dst, &pitch)); framelocked = 1; mem2agpcpy_pic(dst, mpi->planes[0], mpi->w * (mpi->bpp >> 3), mpi->h, pitch, mpi->stride[0]); } else { DFBCHECK(primary->Lock(primary, DSLF_WRITE, (void *)&dst, &pitch)); primarylocked = 1; mem2agpcpy_pic(dst + yoffset * pitch + xoffset * (mpi->bpp >> 3), mpi->planes[0], mpi->w * (mpi->bpp >> 3), mpi->h, pitch, mpi->stride[0]); }; unlock(); } return VO_TRUE; } static int control(struct vo *vo, uint32_t request, void *data) { switch (request) { case VOCTRL_QUERY_FORMAT: return query_format(*((uint32_t *)data)); case VOCTRL_DRAW_IMAGE: return put_image(data); case VOCTRL_SET_EQUALIZER: { struct voctrl_set_equalizer_args *args = data; return directfb_set_video_eq(args->name, args->value); } case VOCTRL_GET_EQUALIZER: { struct voctrl_get_equalizer_args *args = data; return directfb_get_video_eq(args->name, args->valueptr); } } ; return VO_NOTIMPL; } // hopefully will be removed soon static void draw_osd_elem(void *ctx, int x0, int y0, int w, int h, unsigned char *src, unsigned char *srca, int stride) { void *dst; int pitch; unlock(); // isn't it silly I have to unlock surface and then lock it again :-) if (frame) { DFBCHECK(frame->Lock(frame, DSLF_WRITE | DSLF_READ, &dst, &pitch)); framelocked = 1; } else { DFBCHECK(primary->Lock(primary, DSLF_WRITE, &dst, &pitch)); primarylocked = 1; }; switch (pixel_format) { case DSPF_RGB32: case DSPF_ARGB: vo_draw_alpha_rgb32(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 4 * x0, pitch); break; case DSPF_RGB24: vo_draw_alpha_rgb24(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 3 * x0, pitch); break; case DSPF_RGB16: vo_draw_alpha_rgb16(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 2 * x0, pitch); break; case DSPF_ARGB1555: vo_draw_alpha_rgb15(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 2 * x0, pitch); break; case DSPF_ARGB4444: vo_draw_alpha_rgb12(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 2 * x0, pitch); break; case DSPF_YUY2: vo_draw_alpha_yuy2(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 2 * x0, pitch); break; case DSPF_UYVY: vo_draw_alpha_yuy2(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 2 * x0 + 1, pitch); break; case DSPF_I420: case DSPF_YV12: vo_draw_alpha_yv12(w, h, src, srca, stride, ((uint8_t *) dst) + pitch * y0 + 1 * x0, pitch); break; } unlock(); } static void draw_osd(struct vo *vo, struct osd_state *osd) { osd_draw_text(osd, width, height, draw_osd_elem, NULL); } const struct vo_driver video_out_directfb = { .is_new = false, .info = &(const vo_info_t) { "Direct Framebuffer Device", "directfb", "Jiri Svoboda Jiri.Svoboda@seznam.cz", "v 2.0 (for DirectFB version >=0.9.22)" }, .preinit = preinit, .config = config, .control = control, .draw_slice = draw_slice, .draw_osd = draw_osd, .flip_page = flip_page, .check_events = check_events, .uninit = uninit, };