diff options
Diffstat (limited to 'src/dxr3/video_out_dxr3.c')
-rw-r--r-- | src/dxr3/video_out_dxr3.c | 1221 |
1 files changed, 1221 insertions, 0 deletions
diff --git a/src/dxr3/video_out_dxr3.c b/src/dxr3/video_out_dxr3.c new file mode 100644 index 000000000..507632add --- /dev/null +++ b/src/dxr3/video_out_dxr3.c @@ -0,0 +1,1221 @@ +/* + * Copyright (C) 2000-2001 the xine project + * + * This file is part of xine, a unix video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: video_out_dxr3.c,v 1.20 2002/05/24 22:09:44 miguelfreitas Exp $ + */ + +/* mpeg1 encoding video out plugin for the dxr3. + * + * modifications to the original dxr3 video out plugin by + * Mike Lampard <mike at web2u.com.au> + * this first standalone version by + * Harm van der Heijden <hrm at users.sourceforge.net> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#ifdef HAVE_XINERAMA +# include <X11/extensions/Xinerama.h> +#endif + +#include "xine_internal.h" +#include "xineutils.h" +#include "video_out.h" +#include "../video_out/video_out_x11.h" +#include "alphablend.h" +#include "dxr3.h" +#include "video_out_dxr3.h" + +#define LOG_VID 1 +#define LOG_OVR 0 + + +/* plugin initialization functions */ +vo_info_t *get_video_out_plugin_info(); +vo_driver_t *init_video_out_plugin(config_values_t *config, void *visual_gen); + +/* functions required by xine api */ +static uint32_t dxr3_get_capabilities(vo_driver_t *this_gen); +static vo_frame_t *dxr3_alloc_frame(vo_driver_t *this_gen); +static void dxr3_frame_copy(vo_frame_t *frame_gen, uint8_t **src); +static void dxr3_frame_field(vo_frame_t *vo_img, int which_field); +static void dxr3_frame_dispose(vo_frame_t *frame_gen); +static void dxr3_update_frame_format(vo_driver_t *this_gen, vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + int ratio_code, int format, int flags); +static void dxr3_overlay_blend(vo_driver_t *this_gen, vo_frame_t *frame_gen, + vo_overlay_t *overlay); +static void dxr3_display_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen); +static int dxr3_redraw_needed(vo_driver_t *this_gen); +static int dxr3_get_property(vo_driver_t *this_gen, int property); +static int dxr3_set_property(vo_driver_t *this_gen, int property, int value); +static void dxr3_get_property_min_max(vo_driver_t *this_gen, int property, + int *min, int *max); +static int dxr3_gui_data_exchange(vo_driver_t *this_gen, + int data_type, void *data); +static void dxr3_exit(vo_driver_t *this_gen); + +/* overlay helper functions only called once during plugin init */ +static void gather_screen_vars(dxr3_driver_t *this, x11_visual_t *vis); +static int dxr3_overlay_read_state(dxr3_overlay_t *this); +static int dxr3_overlay_set_keycolor(dxr3_overlay_t *this); +static int dxr3_overlay_set_attributes(dxr3_overlay_t *this); + +/* overlay helper functions */ +static int dxr3_overlay_set_window(dxr3_overlay_t *this, int xpos, int ypos, + int width, int height); +static void dxr3_overlay_update(dxr3_driver_t *this); +static void dxr3_zoomTV(dxr3_driver_t *this); +static void dxr3_translate_gui2video(dxr3_driver_t *this, int x, int y, + int *vid_x, int *vid_y); +static int is_fullscreen(dxr3_driver_t *this); + +/* config callbacks */ +static void dxr3_update_add_bars(void *data, cfg_entry_t *entry); +static void dxr3_update_swap_fields(void *data, cfg_entry_t *entry); +static void dxr3_update_enhanced_mode(void *this_gen, cfg_entry_t *entry); + + +vo_info_t *get_video_out_plugin_info() +{ + static vo_info_t vo_info_dxr3 = { + 5, /* api version */ + "dxr3", + "xine video output plugin for dxr3 cards", + VISUAL_TYPE_X11, + 10 /* priority */ + }; + + return &vo_info_dxr3; +} + +vo_driver_t *init_video_out_plugin(config_values_t *config, void *visual_gen) +{ + dxr3_driver_t *this; + char tmpstr[100]; + char *available_encoders, *default_encoder; + const char *confstr; + int dashpos; + + this = (dxr3_driver_t *)malloc(sizeof(dxr3_driver_t)); + if (!this) return NULL; + memset(this, 0, sizeof(dxr3_driver_t)); + + this->vo_driver.get_capabilities = dxr3_get_capabilities; + this->vo_driver.alloc_frame = dxr3_alloc_frame; + this->vo_driver.update_frame_format = dxr3_update_frame_format; + this->vo_driver.overlay_blend = dxr3_overlay_blend; + this->vo_driver.display_frame = dxr3_display_frame; + this->vo_driver.redraw_needed = dxr3_redraw_needed; + this->vo_driver.get_property = dxr3_get_property; + this->vo_driver.set_property = dxr3_set_property; + this->vo_driver.get_property_min_max = dxr3_get_property_min_max; + this->vo_driver.gui_data_exchange = dxr3_gui_data_exchange; + this->vo_driver.exit = dxr3_exit; + + this->config = config; + this->aspect = ASPECT_FULL; + this->overlay_enabled = 0; + this->swap_fields = config->register_bool(config, + "dxr3.enc_swap_fields", 0, "swap odd and even lines", NULL, dxr3_update_swap_fields, this); + this->add_bars = config->register_bool(config, + "dxr3.enc_add_bars", 1, "Add black bars to correct aspect ratio", + "If disabled, will assume source has 4:3 aspect ratio.", dxr3_update_add_bars, this); + this->enhanced_mode = config->register_bool(config, + "dxr3.enc_alt_play_mode", 1, "dxr3: use alternate play mode for mpeg encoder playback", + "Enabling this option will utilise a smoother play mode", dxr3_update_enhanced_mode, this); + + confstr = config->register_string(config, CONF_LOOKUP, CONF_DEFAULT, CONF_NAME, CONF_HELP, NULL, NULL); + strncpy(this->devname, confstr, 128); + this->devname[127] = '\0'; + dashpos = strlen(this->devname) - 2; /* the dash in the new device naming scheme would be here */ + if (this->devname[dashpos] == '-') { + /* use new device naming scheme with trailing number */ + strncpy(this->devnum, &this->devname[dashpos], 3); + this->devname[dashpos] = '\0'; + } else { + /* use old device naming scheme without trailing number */ + /* FIXME: remove this when everyone uses em8300 >=0.12.0 */ + this->devnum[0] = '\0'; + } + + snprintf(tmpstr, sizeof(tmpstr), "%s%s", this->devname, this->devnum); +#if LOG_VID + printf("video_out_dxr3: Entering video init, devname = %s.\n", tmpstr); +#endif + if ((this->fd_control = open(tmpstr, O_WRONLY)) < 0) { + printf("video_out_dxr3: Failed to open control device %s (%s)\n", + tmpstr, strerror(errno)); + return 0; + } + + snprintf (tmpstr, sizeof(tmpstr), "%s_mv%s", this->devname, this->devnum); + if ((this->fd_video = open (tmpstr, O_WRONLY | O_SYNC )) < 0) { + printf("dxr3:Ffailed to open video device %s (%s)\n", + tmpstr, strerror(errno)); + return 0; + } + /* close now and and let the decoder/encoder reopen if they want */ + close(this->fd_video); + this->fd_video = CLOSED_FOR_DECODER; + + /* which encoder to use? Whadda we got? */ + default_encoder = 0; + this->enc = 0; + /* memory leak... but config doesn't copy our help string :-( */ + available_encoders = malloc(256); + strcpy(available_encoders, "Mpeg1 encoder. Options: "); +#ifdef HAVE_LIBFAME + default_encoder = "fame"; + strcat(available_encoders, "\"fame\" (very fast, good quality) "); +#endif +#ifdef HAVE_LIBRTE + default_encoder = "rte"; + strcat(available_encoders, "\"rte\" (fast, high quality) "); +#endif +#if LOG_VID + printf("video_out_dxr3: %s\n", available_encoders); +#endif + if (default_encoder) { + confstr = config->register_string(config, "dxr3.encoder", + default_encoder, available_encoders, NULL, NULL, NULL); +#ifdef HAVE_LIBRTE + if ((strcmp(confstr, "rte") == 0) && !dxr3_rte_init(this)) + return 0; +#endif +#ifdef HAVE_LIBFAME + if ((strcmp(confstr, "fame") == 0) && !dxr3_fame_init(this)) + return 0; +#endif + if (this->enc == 0) + printf("video_out_dxr3: mpeg encoder \"%s\" not compiled in or not supported.\n" + "video_out_dxr3: valid options are %s\n", confstr, available_encoders); + } + else + printf("video_out_dxr3: no mpeg encoder compiled in.\n" + "video_out_dxr3: that's ok, you don't need it for mpeg video like DVDs, but\n" + "video_out_dxr3: you will not be able to play non-mpeg content using this video out\n" + "video_out_dxr3: driver. See the README.dxr3 for details on configuring an encoder.\n"); + + /* init bcs */ + if (ioctl(this->fd_control, EM8300_IOCTL_GETBCS, &this->bcs)) + printf("video_out_dxr3: cannot read bcs values (%s)\n", + strerror(errno)); + this->bcs.contrast = config->register_range(config, "dxr3.contrast", + this->bcs.contrast, 100, 900, "Dxr3: contrast control", NULL, NULL, NULL); + this->bcs.saturation = config->register_range(config, "dxr3.saturation", + this->bcs.saturation, 100, 900, "Dxr3: saturation control", NULL, NULL, NULL); + this->bcs.brightness = config->register_range(config, "dxr3.brightness", + this->bcs.brightness, 100, 900, "Dxr3: brightness control", NULL, NULL, NULL); + + /* overlay or tvout? */ + confstr = config->register_string(config, "dxr3.videoout_mode", "tv", + "Dxr3: videoout mode (tv or overlay)", NULL, NULL, NULL); +#if LOG_VID + printf("video_out_dxr3: overlaymode = %s\n", confstr); +#endif + if (strcasecmp(confstr, "tv") == 0) { + this->overlay_enabled = 0; + this->tv_switchable = 0; /* don't allow on-the-fly switching */ + } else if (strcasecmp(confstr, "overlay") == 0) { +#if LOG_VID + printf("video_out_dxr3: setting up overlay mode\n"); +#endif + gather_screen_vars(this, visual_gen); + if (dxr3_overlay_read_state(&this->overlay) == 0) { + this->overlay_enabled = 1; + this->tv_switchable = 1; + confstr = config->register_string(config, "dxr3.keycolor", "0x80a040", + "Dxr3: overlay colorkey value", NULL, NULL, NULL); + sscanf(confstr, "%x", &this->overlay.colorkey); + confstr = config->register_string(config, "dxr3.color_interval", "50.0", + "Dxr3: overlay colorkey range", + "A greater value widens the tolerance for the overlay keycolor", NULL, NULL); + sscanf(confstr, "%f", &this->overlay.color_interval); + } else { + printf("video_out_dxr3: please run autocal, overlay disabled\n"); + this->overlay_enabled = 0; + this->tv_switchable = 0; + } + } + + /* init tvmode */ + confstr = config->register_string(config, "dxr3.preferred_tvmode", "default", + "Dxr3 preferred tv mode - PAL, PAL60, NTSC or default", NULL, NULL, NULL); + if (strcasecmp(confstr, "ntsc") == 0) { + this->tv_mode = EM8300_VIDEOMODE_NTSC; +#if LOG_VID + printf("video_out_dxr3: setting tv_mode to NTSC\n"); +#endif + } else if (strcasecmp(confstr, "pal") == 0) { + this->tv_mode = EM8300_VIDEOMODE_PAL; +#if LOG_VID + printf("video_out_dxr3: setting tv_mode to PAL 50Hz\n"); +#endif + } else if (strcasecmp(confstr, "pal60") == 0) { + this->tv_mode = EM8300_VIDEOMODE_PAL60; +#if LOG_VID + printf("video_out_dxr3: setting tv_mode to PAL 60Hz\n"); +#endif + } else { + this->tv_mode = EM8300_VIDEOMODE_DEFAULT; + } + if (this->tv_mode != EM8300_VIDEOMODE_DEFAULT) + if (ioctl(this->fd_control, EM8300_IOCTL_SET_VIDEOMODE, &this->tv_mode)) + printf("video_out_dxr3: setting video mode failed.\n"); + + /* initialize overlay */ + if (this->overlay_enabled) { + em8300_overlay_screen_t scr; + + this->overlay.fd_control = this->fd_control; + + /* allocate keycolor */ + this->color.red = ((this->overlay.colorkey >> 16) & 0xff) * 256; + this->color.green = ((this->overlay.colorkey >> 8) & 0xff) * 256; + this->color.blue = ((this->overlay.colorkey ) & 0xff) * 256; + XAllocColor(this->display, DefaultColormap(this->display, 0), &this->color); + + /* set the screen */ + scr.xsize = this->overlay.screen_xres; + scr.ysize = this->overlay.screen_yres; + if (ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETSCREEN, &scr)) + printf("video_out_dxr3: setting the overlay screen failed.\n"); + + if (dxr3_overlay_set_window(&this->overlay, 1, 1, 2, 2) != 0) + printf("video_out_dxr3: setting the overlay window failed.\n"); + if (dxr3_overlay_set_keycolor(&this->overlay) != 0) + printf("video_out_dxr3: setting the overlay keycolor failed.\n"); + if (dxr3_overlay_set_attributes(&this->overlay) != 0) + printf("video_out_dxr3: setting an overlay attribute failed.\n"); + + if (ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETMODE, EM8300_OVERLAY_MODE_OVERLAY) != 0) + printf("video_out_dxr3: failed to enable overlay.\n"); + } + + dxr3_set_property(&this->vo_driver, VO_PROP_ASPECT_RATIO, this->aspect); + return &this->vo_driver; +} + +static uint32_t dxr3_get_capabilities(vo_driver_t *this_gen) +{ + return VO_CAP_YV12 | VO_CAP_YUY2 | + VO_CAP_SATURATION | VO_CAP_BRIGHTNESS | VO_CAP_CONTRAST; +} + +static vo_frame_t *dxr3_alloc_frame(vo_driver_t *this_gen) +{ + dxr3_frame_t *frame; + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + + frame = (dxr3_frame_t *)malloc(sizeof(dxr3_frame_t)); + memset(frame, 0, sizeof(dxr3_frame_t)); + + pthread_mutex_init(&frame->vo_frame.mutex, NULL); + + if (this->enc && this->enc->on_frame_copy) + frame->vo_frame.copy = dxr3_frame_copy; + else + frame->vo_frame.copy = 0; + frame->vo_frame.field = dxr3_frame_field; + frame->vo_frame.dispose = dxr3_frame_dispose; + frame->vo_frame.driver = this_gen; + + return &frame->vo_frame; +} + +static void dxr3_frame_copy(vo_frame_t *frame_gen, uint8_t **src) +{ + dxr3_frame_t *frame = (dxr3_frame_t *)frame_gen; + dxr3_driver_t *this = (dxr3_driver_t *)frame_gen->driver; + + if (frame_gen->format != IMGFMT_MPEG && this->enc && this->enc->on_frame_copy) + this->enc->on_frame_copy(this, frame, src); +} + +static void dxr3_frame_field(vo_frame_t *vo_img, int which_field) +{ + /* dummy function */ +} + +static void dxr3_frame_dispose(vo_frame_t *frame_gen) +{ + dxr3_frame_t *frame = (dxr3_frame_t *)frame_gen; + + if (frame->mem) free(frame->mem); + pthread_mutex_destroy(&frame_gen->mutex); + free(frame); +} + +static void dxr3_update_frame_format(vo_driver_t *this_gen, vo_frame_t *frame_gen, + uint32_t width, uint32_t height, int ratio_code, int format, int flags) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + dxr3_frame_t *frame = (dxr3_frame_t *)frame_gen; + int i; + int aspect, oheight; + + /* update the overlay window co-ords if required */ + dxr3_overlay_update(this); + + if (format == IMGFMT_MPEG) { /* talking to dxr3 decoder */ + /* a bit of a hack. we must release the em8300_mv fd for + * the dxr3 decoder plugin */ + if (this->fd_video >= 0) { + close(this->fd_video); + this->fd_video = CLOSED_FOR_DECODER; + } + + /* for mpeg source, we don't have to do much. */ + this->video_width = width; + this->video_iheight = height; + this->video_oheight = height; + this->top_bar = 0; + this->video_aspect = ratio_code; + + frame->width = width; + frame->iheight = height; + frame->oheight = height; + + if (frame->mem) { + free(frame->mem); + frame->mem = NULL; + frame->real_base[0] = frame->real_base[1] = frame->real_base[2] = NULL; + frame_gen->base[0] = frame_gen->base[1] = frame_gen->base[2] = NULL; + } + + if (ratio_code < 3 || ratio_code> 4) aspect = ASPECT_FULL; + else aspect = ASPECT_ANAMORPHIC; + + if(this->aspect != aspect) + dxr3_set_property(this_gen, VO_PROP_ASPECT_RATIO, aspect); + + return; + } + + /* the following is for the mpeg encoding part only */ + + aspect = this->aspect; + oheight = this->video_oheight; + + if (this->fd_video == CLOSED_FOR_DECODER) { /* decoder should have released it */ + this->fd_video = CLOSED_FOR_ENCODER; /* allow encoder to reopen it */ + this->need_redraw = 1; + } + + if (this->add_bars == 0) { + /* don't add black bars; assume source is in 4:3 */ + ratio_code = XINE_ASPECT_RATIO_4_3; + } + + if ((this->video_width != width) || (this->video_iheight != height) || + (this->video_aspect != ratio_code)) { + /* check aspect ratio, see if we need to add black borders */ + switch (ratio_code) { + case XINE_ASPECT_RATIO_4_3: + aspect = ASPECT_FULL; + oheight = height; + break; + case XINE_ASPECT_RATIO_ANAMORPHIC: + aspect = ASPECT_ANAMORPHIC; + oheight = height; + break; + default: /* assume square pixel */ + aspect = ASPECT_ANAMORPHIC; + oheight = (int)(width * 9./16.); + if (oheight < height) { /* frame too high, try 4:3 */ + aspect = ASPECT_FULL; + oheight = (int)(width * 3./4.); + } + } + + /* find closest multiple of 16 */ + oheight = 16*(int)(oheight / 16. + 0.5); + if (oheight < height) oheight = height; + + /* Tell the viewers about the aspect ratio stuff. */ + if (oheight - height > 0) + printf("video_out_dxr3: adding %d black lines to get %s aspect ratio.\n", + oheight - height, aspect == ASPECT_FULL ? "4:3" : "16:9"); + + this->video_width = width; + this->video_iheight = height; + this->video_oheight = oheight; + this->video_aspect = ratio_code; + this->format = format; + this->need_redraw = 1; + this->need_update = 1; + + if (!this->enc) { + /* no encoder plugin! Let's bug the user! */ + printf("video_out_dxr3: ********************************************************\n" + "video_out_dxr3: * *\n" + "video_out_dxr3: * need an mpeg encoder to play non-mpeg videos on dxr3 *\n" + "video_out_dxr3: * read the README.dxr3 for details. *\n" + "video_out_dxr3: * (if you get this message while trying to play an *\n" + "video_out_dxr3: * mpeg video, there is something wrong with the dxr3 *\n" + "video_out_dxr3: * decoder plugin. check if it is set up correctly) *\n" + "video_out_dxr3: * *\n" + "video_out_dxr3: ********************************************************\n"); + } + } + + /* if dimensions changed, we need to re-allocate frame memory */ + if ((frame->width != width) || (frame->iheight != height) || + (frame->oheight != oheight)) { + if (frame->mem) { + free (frame->mem); + frame->mem = NULL; + } + + /* make top black bar multiple of 16, + * so old and new macroblocks overlap */ + this->top_bar = ((oheight - height) / 32) * 16; + if (format == IMGFMT_YUY2) { + int image_size = width * oheight; /* includes black bars */ + + /* planar format, only base[0] */ + /* add one extra line for field swap stuff */ + frame->real_base[0] = xine_xmalloc_aligned(16, (image_size + width) * 2, + (void**)&frame->mem); + + /* don't use first line */ + frame->real_base[0] += width * 2; + frame->real_base[1] = frame->real_base[2] = 0; + + /* fix offset, so the decoder does not see the top black bar */ + frame->vo_frame.base[0] = frame->real_base[0] + width * 2 * this->top_bar; + frame->vo_frame.base[1] = frame->vo_frame.base[2] = 0; + + /* fill with black (yuy2 16,128,16,128,...) */ + memset(frame->real_base[0], 128, 2 * image_size); /* U and V */ + for (i = 0; i < 2 * image_size; i += 2) /* Y */ + *(frame->real_base[0] + i) = 16; + + } else { /* IMGFMT_YV12 */ + int image_size = width * oheight; /* includes black bars */ + + /* add one extra line for field swap stuff */ + frame->real_base[0] = xine_xmalloc_aligned(16, (image_size + width) * 3/2, + (void**)&frame->mem); + + /* don't use first line */ + frame->real_base[0] += width; + frame->real_base[1] = frame->real_base[0] + image_size; + frame->real_base[2] = frame->real_base[1] + image_size/4; + + /* fix offsets, so the decoder does not see the top black bar */ + frame->vo_frame.base[0] = frame->real_base[0] + width * this->top_bar; + frame->vo_frame.base[1] = frame->real_base[1] + width * this->top_bar/4; + frame->vo_frame.base[2] = frame->real_base[2] + width * this->top_bar/4; + + /* fill with black (yuv 16,128,128) */ + memset(frame->real_base[0], 16, image_size); + memset(frame->real_base[1], 128, image_size/4); + memset(frame->real_base[2], 128, image_size/4); + } + } + + if (this->swap_fields != frame->swap_fields) { + if (format == IMGFMT_YUY2) { + if (this->swap_fields) + frame->vo_frame.base[0] -= width *2; + else + frame->vo_frame.base[0] += width *2; + } else { + if (this->swap_fields) + frame->vo_frame.base[0] -= width; + else + frame->vo_frame.base[0] += width; + } + } + + frame->width = width; + frame->iheight = height; + frame->oheight = oheight; + frame->swap_fields = this->swap_fields; + + if(this->aspect != aspect) + dxr3_set_property(this_gen, VO_PROP_ASPECT_RATIO, aspect); +} + +static void dxr3_overlay_blend(vo_driver_t *this_gen, vo_frame_t *frame_gen, + vo_overlay_t *overlay) +{ + /* FIXME: We only blend non-mpeg frames here. + Is there any way to provide overlays for mpeg content? Subpictures? */ + if (frame_gen->format != IMGFMT_MPEG) { + dxr3_frame_t *frame = (dxr3_frame_t *)frame_gen; + + if (overlay->rle) { + if (frame_gen->format == IMGFMT_YV12) + blend_yuv(frame->vo_frame.base, overlay, frame->width, frame->iheight); + else + blend_yuy2(frame->vo_frame.base[0], overlay, frame->width, frame->iheight); + } + } +} + +static void dxr3_display_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + dxr3_frame_t *frame = (dxr3_frame_t *)frame_gen; + + if (frame_gen->format != IMGFMT_MPEG && this->enc && this->enc->on_display_frame) { + if (this->need_update) { + /* we cannot do this earlier, because vo_frame.duration is only valid here */ + if (this->enc && this->enc->on_update_format) + this->enc->on_update_format(this, frame); + this->need_update = 0; + } + /* for non-mpeg, the encoder plugin is responsible for calling + * frame_gen->displayed(frame_gen) ! */ + this->enc->on_display_frame(this, frame); + } else { + frame_gen->displayed(frame_gen); + } +} + +static int dxr3_redraw_needed(vo_driver_t *this_gen) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + + dxr3_overlay_update(this); + + if (this->need_redraw) { + this->need_redraw = 0; + return 1; + } + return 0; +} + +static int dxr3_get_property(vo_driver_t *this_gen, int property) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + + switch (property) { + case VO_PROP_SATURATION: + return this->bcs.saturation; + case VO_PROP_CONTRAST: + return this->bcs.contrast; + case VO_PROP_BRIGHTNESS: + return this->bcs.brightness; + case VO_PROP_ASPECT_RATIO: + return this->aspect; + case VO_PROP_COLORKEY: + return this->overlay.colorkey; + case VO_PROP_ZOOM_FACTOR: + case VO_PROP_TVMODE: + return 0; + case VO_PROP_VO_TYPE: + return VO_TYPE_DXR3; + } + printf("video_out_dxr3: property %d not implemented.\n", property); + return 0; +} + +static int dxr3_set_property(vo_driver_t *this_gen, int property, int value) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + int val, bcs_changed = 0; + int fullscreen; + double ratio; + + switch (property) { + case VO_PROP_SATURATION: + this->bcs.saturation = value; + bcs_changed = 1; + break; + case VO_PROP_CONTRAST: + this->bcs.contrast = value; + bcs_changed = 1; + break; + case VO_PROP_BRIGHTNESS: + this->bcs.brightness = value; + bcs_changed = 1; + break; + case VO_PROP_ASPECT_RATIO: + /* xitk-ui increments the value, so we make + * just a two value "loop" */ + if (value > ASPECT_FULL) value = ASPECT_ANAMORPHIC; + this->aspect = value; + fullscreen = this->overlay_enabled ? is_fullscreen(this) : 0; + + if (value == ASPECT_ANAMORPHIC) { +#if LOG_VID + printf("video_out_dxr3: setting aspect ratio to anamorphic\n"); +#endif + if (!this->overlay_enabled || fullscreen) + val = EM8300_ASPECTRATIO_16_9; + else /* The overlay window can adapt to the ratio */ + val = EM8300_ASPECTRATIO_4_3; + ratio = 16.0/9.0; + } else { +#if LOG_VID + printf("video_out_dxr3: setting aspect ratio to full\n"); +#endif + val = EM8300_ASPECTRATIO_4_3; + ratio = 4.0/3.0; + } + + if (ioctl(this->fd_control, EM8300_IOCTL_SET_ASPECTRATIO, &val)) + printf("video_out_dxr3: failed to set aspect ratio (%s)\n", strerror(errno)); + + if (this->overlay_enabled && !fullscreen) { + int dummy; + this->frame_output_cb(this->user_data, this->width, this->width / ratio, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy); + } + break; + case VO_PROP_COLORKEY: + printf("video_out_dxr3: VO_PROP_COLORKEY not implemented!"); + this->overlay.colorkey = value; + break; + case VO_PROP_ZOOM_FACTOR: + if(!this->overlay_enabled) { /* TV-out only */ + if (value == 1) { +#if LOG_VID + printf("video_out_dxr3: enabling 16:9 zoom\n"); +#endif + val = EM8300_ASPECTRATIO_4_3; + if (ioctl(this->fd_control, EM8300_IOCTL_SET_ASPECTRATIO, &val)) + printf("video_out_dxr3: failed to set aspect ratio (%s)\n", strerror(errno)); + dxr3_zoomTV(this); + } else if (value == -1) { +#if LOG_VID + printf("video_out_dxr3: disabling 16:9 zoom\n"); +#endif + if (ioctl(this->fd_control, EM8300_IOCTL_SET_ASPECTRATIO, &this->aspect)) + printf("video_out_dxr3: failed to set aspect ratio (%s)\n", strerror(errno)); + } + } + break; + case VO_PROP_TVMODE: + if (++this->tv_mode > EM8300_VIDEOMODE_LAST) this->tv_mode = EM8300_VIDEOMODE_PAL; +#if LOG_VID + printf("video_out_dxr3: Changing TVMode to "); +#endif + if (this->tv_mode == EM8300_VIDEOMODE_PAL) printf("PAL\n"); + if (this->tv_mode == EM8300_VIDEOMODE_PAL60) printf("PAL60\n"); + if (this->tv_mode == EM8300_VIDEOMODE_NTSC) printf("NTSC\n"); + if (ioctl(this->fd_control, EM8300_IOCTL_SET_VIDEOMODE, &this->tv_mode)) + printf("video_out_dxr3: setting video mode failed (%s)\n", strerror(errno)); + break; + } + + if (bcs_changed) { + if (ioctl(this->fd_control, EM8300_IOCTL_SETBCS, &this->bcs)) + printf("video_out_dxr3: bcs set failed (%s)\n", strerror(errno)); + this->config->update_num(this->config, "dxr3.contrast", this->bcs.contrast); + this->config->update_num(this->config, "dxr3.saturation", this->bcs.saturation); + this->config->update_num(this->config, "dxr3.brightness", this->bcs.brightness); + } + + return value; +} + +static void dxr3_get_property_min_max(vo_driver_t *this_gen, int property, + int *min, int *max) +{ + switch (property) { + case VO_PROP_SATURATION: + case VO_PROP_CONTRAST: + case VO_PROP_BRIGHTNESS: + *min = 0; + *max = 1000; + break; + default: + *min = 0; + *max = 0; + } +} + +static int dxr3_gui_data_exchange(vo_driver_t *this_gen, int data_type, void *data) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + + if (!this->overlay_enabled && !this->tv_switchable) return 0; + + switch (data_type) { + case GUI_DATA_EX_EXPOSE_EVENT: + XLockDisplay(this->display); + XFillRectangle(this->display, this->win, this->gc, + 0, 0, this->width, this->height); + XUnlockDisplay(this->display); + dxr3_overlay_update(this); + break; + case GUI_DATA_EX_DRAWABLE_CHANGED: + { + XWindowAttributes a; + this->win = (Drawable)data; + this->gc = XCreateGC(this->display, this->win, 0, NULL); + XGetWindowAttributes(this->display, this->win, &a); + dxr3_set_property(this_gen, VO_PROP_ASPECT_RATIO, this->aspect); + } + break; + case GUI_DATA_EX_TRANSLATE_GUI_TO_VIDEO: + { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + dxr3_translate_gui2video(this, rect->x, rect->y, &x1, &y1); + dxr3_translate_gui2video(this, rect->w, rect->h, &x2, &y2); + rect->x = x1; + rect->y = y1; + rect->w = x2 - x1; + rect->h = y2 - y1; + } + break; + case GUI_DATA_EX_VIDEOWIN_VISIBLE: + { + int window_showing = (int)data; + int val; + if (!window_showing) { +#if LOG_VID + printf("video_out_dxr3: Hiding video window and diverting video to TV\n"); +#endif + val = EM8300_OVERLAY_MODE_OFF; + ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETMODE, &val); + this->overlay_enabled = 0; + } else { +#if LOG_VID + printf("video_out_dxr3: Using video window for overlaying video\n"); +#endif + val = EM8300_OVERLAY_MODE_OVERLAY; + ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETMODE, &val); + this->overlay_enabled = 1; + } + dxr3_set_property(this_gen, VO_PROP_ASPECT_RATIO, this->aspect); + } + break; + default: + return -1; + } + return 0; +} + +static void dxr3_exit(vo_driver_t *this_gen) +{ + dxr3_driver_t *this = (dxr3_driver_t *)this_gen; + int val = EM8300_OVERLAY_MODE_OFF; + +#if LOG_VID + printf("video_out_dxr3: vo exit called\n"); +#endif + if (this->enc && this->enc->on_close) + this->enc->on_close(this); + if(this->overlay_enabled) + ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETMODE, &val); + + free(this); +} + + +static void gather_screen_vars(dxr3_driver_t *this, x11_visual_t *vis) +{ + int scrn; +#ifdef HAVE_XINERAMA + int screens; + int dummy_a, dummy_b; + XineramaScreenInfo *screeninfo = NULL; +#endif + + this->win = vis->d; + this->display = vis->display; + this->user_data = vis->user_data; + this->gc = XCreateGC(this->display, this->win, 0, NULL); + scrn = DefaultScreen(this->display); + +#ifdef HAVE_XINERAMA + if (XineramaQueryExtension(this->display, &dummy_a, &dummy_b) && + (screeninfo = XineramaQueryScreens(this->display, &screens)) && + XineramaIsActive(this->display)) { + this->overlay.screen_xres = screeninfo[0].width; + this->overlay.screen_yres = screeninfo[0].height; + } else +#endif + { + this->overlay.screen_xres = DisplayWidth(this->display, scrn); + this->overlay.screen_yres = DisplayHeight(this->display, scrn); + } + + this->overlay.screen_depth = DisplayPlanes(this->display, scrn); + this->frame_output_cb = (void *)vis->frame_output_cb; + +#if LOG_OVR + printf("video_out_dxr3: xres: %d, yres: %d, depth: %d\n", + this->overlay.screen_xres, this->overlay.screen_yres, this->overlay.screen_depth); +#endif +} + +/* dxr3_overlay_read_state helper structure */ +#define TYPE_INT 1 +#define TYPE_XINT 2 +#define TYPE_COEFF 3 +#define TYPE_FLOAT 4 + +struct lut_entry { + char *name; + int type; + void *ptr; +}; + +/* dxr3_overlay_read_state helper function */ +static int lookup_parameter(struct lut_entry *lut, char *name, + void **ptr, int *type) +{ + int i; + + for (i = 0; lut[i].name; i++) + if (strcmp(name, lut[i].name) == 0) { + *ptr = lut[i].ptr; + *type = lut[i].type; +#if LOG_OVR + printf("video_out_dxr3: found parameter \"%s\"\n", name); +#endif + return 1; + } +#if LOG_OVR + printf("video_out_dxr3: WARNING: unknown parameter \"%s\"\n", name); +#endif + return 0; +} + +static int dxr3_overlay_read_state(dxr3_overlay_t *this) +{ + char *loc; + char fname[256], tmp[128], line[256]; + FILE *fp; + struct lut_entry lut[] = { + {"xoffset", TYPE_INT, &this->xoffset}, + {"yoffset", TYPE_INT, &this->yoffset}, + {"xcorr", TYPE_INT, &this->xcorr}, + {"jitter", TYPE_INT, &this->jitter}, + {"stability", TYPE_INT, &this->stability}, + {"keycolor", TYPE_XINT, &this->colorkey}, + {"colcal_upper", TYPE_COEFF, &this->colcal_upper[0]}, + {"colcal_lower", TYPE_COEFF, &this->colcal_lower[0]}, + {"color_interval", TYPE_FLOAT, &this->color_interval}, + {0,0,0} + }; + char *tok; + void *ptr; + int type; + int j; + + /* store previous locale */ + loc = setlocale(LC_NUMERIC, NULL); + /* set american locale for floating point values + * (used by .overlay/res file) */ + setlocale(LC_NUMERIC, "en_US"); + + snprintf(tmp, sizeof(tmp), "/res_%dx%dx%d", + this->screen_xres, this->screen_yres, this->screen_depth); + strncpy(fname, getenv("HOME"), sizeof(fname) - strlen(tmp) - sizeof("/.overlay")); + fname[sizeof(fname) - strlen(tmp) - sizeof("/.overlay")] = '\0'; + strcat(fname, "/.overlay"); + strcat(fname, tmp); +#if LOG_OVR + printf("video_out_dxr3: attempting to open %s\n", fname); +#endif + if (!(fp = fopen(fname, "r"))) { + printf("video_out_dxr3: ERROR Reading overlay init file. Run autocal!\n"); + return -1; + } + + while (!feof(fp)) { + if (!fgets(line, 256, fp)) + break; + tok = strtok(line, " "); + if (lookup_parameter(lut, tok, &ptr, &type)) { + tok = strtok(NULL, " \n"); + switch(type) { + case TYPE_INT: + sscanf(tok, "%d", (int *)ptr); +#if LOG_OVR + printf("video_out_dxr3: value \"%s\" = %d\n", tok, *(int *)ptr); +#endif + break; + case TYPE_XINT: + sscanf(tok, "%x", (int *)ptr); +#if LOG_OVR + printf("video_out_dxr3: value \"%s\" = %d\n", tok, *(int *)ptr); +#endif + break; + case TYPE_FLOAT: + sscanf(tok, "%f", (float *)ptr); +#if LOG_OVR + printf("video_out_dxr3: value \"%s\" = %f\n", tok, *(float *)ptr); +#endif + break; + case TYPE_COEFF: + for(j = 0; j < 3; j++) { + sscanf(tok, "%f", &((struct coeff *)ptr)[j].k); +#if LOG_OVR + printf("video_out_dxr3: value (%d,k) \"%s\" = %f\n", j, tok, ((struct coeff *)ptr)[j].k); +#endif + tok = strtok(NULL, " \n"); + sscanf(tok, "%f", &((struct coeff *)ptr)[j].m); +#if LOG_OVR + printf("video_out_dxr3: value (%d,m) \"%s\" = %f\n", j, tok, ((struct coeff *)ptr)[j].m); +#endif + tok = strtok(NULL, " \n"); + } + break; + } + } + } + + fclose(fp); + /* restore original locale */ + setlocale(LC_NUMERIC, loc); + + return 0; +} + +/* dxr3_overlay_set_keycolor helper function */ +static int col_interp(float x, struct coeff c) +{ + int y; + y = rint(x * c.k + c.m); + if (y > 255) y = 255; + if (y < 0) y = 0; + return y; +} + +static int dxr3_overlay_set_keycolor(dxr3_overlay_t *this) +{ + em8300_attribute_t attr; + float r = (this->colorkey & 0xff0000) >> 16; + float g = (this->colorkey & 0x00ff00) >> 8; + float b = (this->colorkey & 0x0000ff); + float interval = this->color_interval; + int32_t overlay_limit; + int ret; + +#if LOG_OVR + printf("video_out_dxr3: set_keycolor: r = %f, g = %f, b = %f, interval = %f\n", + r, g, b, interval); +#endif + + overlay_limit = /* lower limit */ + col_interp(r - interval, this->colcal_lower[0]) << 16 | + col_interp(g - interval, this->colcal_lower[1]) << 8 | + col_interp(b - interval, this->colcal_lower[2]); +#if LOG_OVR + printf("video_out_dxr3: lower overlay_limit = %d\n", overlay_limit); +#endif + attr.attribute = EM9010_ATTRIBUTE_KEYCOLOR_LOWER; + attr.value = overlay_limit; + if ((ret = ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr)) < 0) { + printf("video_out_dxr3: WARNING: error setting overlay lower limit attribute\n"); + return ret; + } + + overlay_limit = /* upper limit */ + col_interp(r + interval, this->colcal_upper[0]) << 16 | + col_interp(g + interval, this->colcal_upper[1]) << 8 | + col_interp(b + interval, this->colcal_upper[2]); +#if LOG_OVR + printf("video_out_dxr3: upper overlay_limit = %d\n", overlay_limit); +#endif + attr.attribute = EM9010_ATTRIBUTE_KEYCOLOR_UPPER; + attr.value = overlay_limit; + if ((ret = ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr)) < 0) + printf("video_out_dxr3: WARNING: error setting overlay upper limit attribute\n"); + return ret; +} + +static int dxr3_overlay_set_attributes(dxr3_overlay_t *this) +{ + em8300_attribute_t attr; + + attr.attribute = EM9010_ATTRIBUTE_XOFFSET; + attr.value = this->xoffset; + if(ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr) == -1) + return -1; + attr.attribute = EM9010_ATTRIBUTE_YOFFSET; + attr.value = this->yoffset; + if(ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr) == -1) + return -1; + attr.attribute = EM9010_ATTRIBUTE_XCORR; + attr.value = this->xcorr; + if(ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr) == -1) + return -1; + attr.attribute = EM9010_ATTRIBUTE_STABILITY; + attr.value = this->stability; + if(ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr) == -1) + return -1; + attr.attribute = EM9010_ATTRIBUTE_JITTER; + attr.value = this->jitter; + return ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SET_ATTRIBUTE, &attr); +} + + +static int dxr3_overlay_set_window(dxr3_overlay_t *this, + int xpos, int ypos, int width, int height) +{ + em8300_overlay_window_t win; + + /* is some part of the picture visible? */ + if (xpos+width < 0) return 0; + if (ypos+height < 0) return 0; + if (xpos > this->screen_xres) return 0; + if (ypos > this->screen_yres) return 0; + + win.xpos = xpos; + win.ypos = ypos; + win.width = width; + win.height = height; + + return ioctl(this->fd_control, EM8300_IOCTL_OVERLAY_SETWINDOW, &win); +} + +static void dxr3_overlay_update(dxr3_driver_t *this) +{ + if (this->overlay_enabled) { + int gui_win_x, gui_win_y, gypos, gxpos, gw, gh; + + this->frame_output_cb(this->user_data, + this->video_width, this->video_oheight, + &gxpos, &gypos, &gw, &gh, &gui_win_x, &gui_win_y); + + if (this->xpos != gxpos || this->ypos != gypos || + this->width != gw || this->height != gh) { + XWindowAttributes a; + Window junkwin; + int rx, ry; + + /* detect true window position and adapt overlay to it */ + XLockDisplay(this->display); + XSetForeground(this->display, this->gc, this->color.pixel); + XGetWindowAttributes(this->display, this->win, &a); + XTranslateCoordinates (this->display, this->win, a.root, + gxpos + 1, gypos + 1, &rx, &ry, &junkwin); + XUnlockDisplay(this->display); + + this->xpos = rx; + this->ypos = ry; + this->width = gw; + this->height = gh; + + dxr3_overlay_set_window(&this->overlay, this->xpos, this->ypos, + this->width, this->height); + } + } +} + +static void dxr3_zoomTV(dxr3_driver_t *this) +{ + em8300_register_t frame, visible, update; + + /* change left bound */ + frame.microcode_register = 1; + frame.reg = 93; // dicom frame left + frame.val = 0x10; + + visible.microcode_register = 1; + visible.reg = 97; // dicom visible left + visible.val = 0x10; + + update.microcode_register = 1; + update.reg = 65; // dicom_update + update.val = 1; + + ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &frame); + ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &visible); + ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &update); + + /* change right bound */ + frame.microcode_register = 1; + frame.reg = 94; // dicom frame right + frame.val = 0x10; + + visible.microcode_register = 1; + visible.reg = 98; // dicom visible right + visible.val = 968; + + update.microcode_register = 1; + update.reg = 65; // dicom_update + update.val = 1; + + ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &frame); + ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &visible); + ioctl(this->fd_control, EM8300_IOCTL_WRITEREG, &update); +} + +static void dxr3_translate_gui2video(dxr3_driver_t *this, int x, int y, + int *vid_x, int *vid_y) +{ + *vid_x = x * this->video_width / this->width; + *vid_y = y * this->video_oheight / this->height - this->top_bar; +} + +static int is_fullscreen(dxr3_driver_t *this) +{ + XWindowAttributes a; + + XGetWindowAttributes(this->display, this->win, &a); + + return + a.x == 0 && + a.y == 0 && + a.width == this->overlay.screen_xres && + a.height == this->overlay.screen_yres; +} + + +static void dxr3_update_add_bars(void *data, cfg_entry_t *entry) +{ + dxr3_driver_t *this = (dxr3_driver_t *)data; + this->add_bars = entry->num_value; + printf("video_out_dxr3: setting add_bars to correct aspect ratio to %s\n", + (this->add_bars ? "on" : "off")); +} + +static void dxr3_update_swap_fields(void *data, cfg_entry_t *entry) +{ + dxr3_driver_t *this = (dxr3_driver_t *)data; + this->swap_fields = entry->num_value; + printf("video_out_dxr3: setting swap fields to %s\n", + (this->swap_fields ? "on" : "off")); +} + +static void dxr3_update_enhanced_mode(void *data, cfg_entry_t *entry) +{ + dxr3_driver_t *this = (dxr3_driver_t *)data; + this->enhanced_mode = entry->num_value; + printf("video_out_dxr3: setting enhanced encoding playback to %s\n", + (this->enhanced_mode ? "on" : "off")); +} |