/* * Copyright (C) 2000-2004 the xine project * * This file is part of xine, a free 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: dxr3_decode_spu.c,v 1.46 2004/04/10 15:29:57 mroi Exp $ */ /* dxr3 spu decoder plugin. * Accepts the spu data from xine and sends it directly to the * corresponding dxr3 device. Also handles dvd menu button highlights. * Takes precedence over libspudec due to a higher priority. */ #include #include #include #include #include #include #include #include #include #define LOG_MODULE "dxr3_decode_spu" #define LOG_VERBOSE /* #define LOG */ #define LOG_PTS 0 #define LOG_SPU 0 #define LOG_BTN 0 #include "xine_internal.h" #include "xineutils.h" #include "buffer.h" #include "xine-engine/bswap.h" #ifdef HAVE_DVDNAV # include # include #else # include "nav_types.h" # include "nav_read.h" #endif #include "video_out_dxr3.h" #include "dxr3.h" #define MAX_SPU_STREAMS 32 /* plugin class initialization function */ static void *dxr3_spudec_init_plugin(xine_t *xine, void *); /* plugin catalog information */ static uint32_t supported_types[] = { BUF_SPU_DVD, 0 }; static decoder_info_t dxr3_spudec_info = { supported_types, /* supported types */ 10 /* priority */ }; plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_SPU_DECODER, 16, "dxr3-spudec", XINE_VERSION_CODE, &dxr3_spudec_info, &dxr3_spudec_init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } }; /* plugin class functions */ static spu_decoder_t *dxr3_spudec_open_plugin(spu_decoder_class_t *class_gen, xine_stream_t *stream); static char *dxr3_spudec_get_identifier(spu_decoder_class_t *class_gen); static char *dxr3_spudec_get_description(spu_decoder_class_t *class_gen); static void dxr3_spudec_class_dispose(spu_decoder_class_t *class_gen); /* plugin instance functions */ static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf); static void dxr3_spudec_reset(spu_decoder_t *this_gen); static void dxr3_spudec_discontinuity(spu_decoder_t *this_gen); static void dxr3_spudec_dispose(spu_decoder_t *this_gen); static int dxr3_spudec_interact_info(spu_decoder_t *this_gen, void *data); static void dxr3_spudec_set_button(spu_decoder_t *this_gen, int32_t button, int32_t mode); /* plugin structures */ typedef struct dxr3_spu_stream_state_s { uint32_t stream_filter; int spu_length; int spu_ctrl; int spu_end; int end_found; int bytes_passed; /* used to parse the spu */ } dxr3_spu_stream_state_t; typedef struct pci_node_s pci_node_t; struct pci_node_s { pci_t pci; uint64_t vpts; pci_node_t *next; }; typedef struct dxr3_spudec_class_s { spu_decoder_class_t spu_decoder_class; int instance; /* we allow only one instance of this plugin */ } dxr3_spudec_class_t; typedef struct dxr3_spudec_s { spu_decoder_t spu_decoder; dxr3_spudec_class_t *class; xine_stream_t *stream; dxr3_driver_t *dxr3_vo; /* we need to talk to the video out */ xine_event_queue_t *event_queue; int devnum; int fd_spu; /* to access the dxr3 spu device */ dxr3_spu_stream_state_t spu_stream_state[MAX_SPU_STREAMS]; uint32_t clut[16]; /* the current color lookup table */ int menu; /* are we in a menu? */ int button_filter; pci_node_t pci_cur; /* a list of PCI packs, with the list head being current */ pthread_mutex_t pci_lock; uint32_t buttonN; /* currently highlighted button */ int anamorphic; /* this is needed to detect anamorphic menus */ } dxr3_spudec_t; /* helper functions */ static inline int dxr3_present(xine_stream_t *stream); static inline void dxr3_spudec_handle_event(dxr3_spudec_t *this); /* the NAV functions must be called with the pci_lock held */ static inline void dxr3_spudec_clear_nav_list(dxr3_spudec_t *this); static inline void dxr3_spudec_update_nav(dxr3_spudec_t *this); static void dxr3_spudec_process_nav(dxr3_spudec_t *this); static int dxr3_spudec_copy_nav_to_btn(dxr3_spudec_t *this, int32_t mode, em8300_button_t *btn); static inline void dxr3_swab_clut(int* clut); static void *dxr3_spudec_init_plugin(xine_t *xine, void* data) { dxr3_spudec_class_t *this; this = (dxr3_spudec_class_t *)xine_xmalloc(sizeof(dxr3_spudec_class_t)); if (!this) return NULL; this->spu_decoder_class.open_plugin = dxr3_spudec_open_plugin; this->spu_decoder_class.get_identifier = dxr3_spudec_get_identifier; this->spu_decoder_class.get_description = dxr3_spudec_get_description; this->spu_decoder_class.dispose = dxr3_spudec_class_dispose; this->instance = 0; return &this->spu_decoder_class; } static spu_decoder_t *dxr3_spudec_open_plugin(spu_decoder_class_t *class_gen, xine_stream_t *stream) { dxr3_spudec_t *this; dxr3_spudec_class_t *class = (dxr3_spudec_class_t *)class_gen; char tmpstr[128]; int i; if (class->instance) return NULL; if (!dxr3_present(stream)) return NULL; this = (dxr3_spudec_t *)xine_xmalloc(sizeof(dxr3_spudec_t)); if (!this) return NULL; this->spu_decoder.decode_data = dxr3_spudec_decode_data; this->spu_decoder.reset = dxr3_spudec_reset; this->spu_decoder.discontinuity = dxr3_spudec_discontinuity; this->spu_decoder.dispose = dxr3_spudec_dispose; this->spu_decoder.get_interact_info = dxr3_spudec_interact_info; this->spu_decoder.set_button = dxr3_spudec_set_button; this->class = class; this->stream = stream; /* We need to talk to dxr3 video out to coordinate spus and overlays */ this->dxr3_vo = (dxr3_driver_t *)stream->video_driver; this->event_queue = xine_event_new_queue(stream); this->devnum = stream->xine->config->register_num(stream->xine->config, CONF_KEY, 0, CONF_NAME, CONF_HELP, 10, NULL, NULL); pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (this->dxr3_vo->fd_spu) this->fd_spu = this->dxr3_vo->fd_spu; else { /* open dxr3 spu device */ snprintf(tmpstr, sizeof(tmpstr), "/dev/em8300_sp-%d", this->devnum); if ((this->fd_spu = open(tmpstr, O_WRONLY)) < 0) { xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("dxr3_decode_spu: Failed to open spu device %s (%s)\n"), tmpstr, strerror(errno)); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); free(this); return NULL; } llprintf(LOG_SPU, "init: SPU_FD = %i\n",this->fd_spu); /* We are talking directly to the dxr3 video out to allow concurrent * access to the same spu device */ this->dxr3_vo->fd_spu = this->fd_spu; } pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); for (i = 0; i < MAX_SPU_STREAMS; i++) { this->spu_stream_state[i].stream_filter = 1; this->spu_stream_state[i].spu_length = 0; } this->menu = 0; this->button_filter = 1; this->pci_cur.pci.hli.hl_gi.hli_ss = 0; this->pci_cur.next = NULL; this->buttonN = 1; this->anamorphic = 0; pthread_mutex_init(&this->pci_lock, NULL); class->instance = 1; return &this->spu_decoder; } static char *dxr3_spudec_get_identifier(spu_decoder_class_t *class_gen) { return "dxr3-spudec"; } static char *dxr3_spudec_get_description(spu_decoder_class_t *class_gen) { return "subtitle decoder plugin using the hardware decoding capabilities of a DXR3 decoder card"; } static void dxr3_spudec_class_dispose(spu_decoder_class_t *class_gen) { free(class_gen); } static void dxr3_spudec_decode_data(spu_decoder_t *this_gen, buf_element_t *buf) { dxr3_spudec_t *this = (dxr3_spudec_t *)this_gen; ssize_t written; uint32_t stream_id = buf->type & 0x1f; dxr3_spu_stream_state_t *state = &this->spu_stream_state[stream_id]; uint32_t spu_channel = this->stream->spu_channel; dxr3_spudec_handle_event(this); if ( (buf->type & 0xffff0000) != BUF_SPU_DVD || !(buf->decoder_flags & BUF_FLAG_SPECIAL) || buf->decoder_info[1] != BUF_SPECIAL_SPU_DVD_SUBTYPE ) return; if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_CLUT ) { llprintf(LOG_SPU, "BUF_SPU_CLUT\n"); if (buf->content[0] == 0) /* cheap endianess detection */ dxr3_swab_clut((int *)buf->content); pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPALETTE, buf->content)) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: failed to set CLUT (%s)\n", strerror(errno)); /* remember clut, when video out places some overlay we may need to restore it */ memcpy(this->clut, buf->content, 16 * sizeof(uint32_t)); this->dxr3_vo->clut_cluttered = 0; pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); return; } if( buf->decoder_info[2] == SPU_DVD_SUBTYPE_SUBP_CONTROL ) { /* FIXME: is BUF_SPU_SUBP_CONTROL used anymore? */ int i; uint32_t *subp_control = (uint32_t *)buf->content; for (i = 0; i < MAX_SPU_STREAMS; i++) this->spu_stream_state[i].stream_filter = subp_control[i]; return; } if( buf->decoder_info[2] == SPU_DVD_SUBTYPE_NAV ) { uint8_t *p = buf->content; llprintf(LOG_BTN, "got NAV packet\n"); pthread_mutex_lock(&this->pci_lock); /* just watch out for menus */ if (p[3] == 0xbf && p[6] == 0x00) { /* Private stream 2 */ pci_t pci; navRead_PCI(&pci, p + 7); llprintf(LOG_BTN, "PCI packet hli_ss is %d\n", pci.hli.hl_gi.hli_ss); if (pci.hli.hl_gi.hli_ss == 1) { /* menu ahead */ /* NAV packets contain start and end presentation timestamps, which tell the * application, when the highlight information in the NAV is supposed to be valid. * We handle these timestamps only in a very stripped-down way: We keep a list * of NAV packets (or better: the PCI part of them), tagged with a VPTS timestamp * telling, when the NAV should be processed. However, we only enqueue a new node * into this list, when we receive new highlight information during an already * showing menu. This happens very rarerly on common DVDs, so it is of low impact. * And we only check for processing of queued entries at some prominent * locations in this SPU decoder. Since presentation timestamps rarely solve a real * purpose on most DVDs, this is ok compared to the full-blown solution, which would * require a separate thread managing the queue all the time. */ if (this->pci_cur.pci.hli.hl_gi.hli_ss != 0 && pci.hli.hl_gi.hli_s_ptm > this->pci_cur.pci.hli.hl_gi.hli_s_ptm) { pci_node_t *node = &this->pci_cur; printf("dxr3_decode_spu: DEBUG: allocating new PCI node for hli_s_ptm %d\n", pci.hli.hl_gi.hli_s_ptm); /* append PCI at the end of the list */ while (node->next) node = node->next; node->next = (pci_node_t *)xine_xmalloc(sizeof(pci_node_t)); node->next->vpts = this->stream->metronom->got_spu_packet(this->stream->metronom, pci.hli.hl_gi.hli_s_ptm); node->next->next = NULL; xine_fast_memcpy(&node->next->pci, &pci, sizeof(pci_t)); } else { dxr3_spudec_clear_nav_list(this); /* menu ahead, remember PCI for later use */ xine_fast_memcpy(&this->pci_cur.pci, &pci, sizeof(pci_t)); dxr3_spudec_process_nav(this); } } if ((pci.hli.hl_gi.hli_ss == 0) && (this->pci_cur.pci.hli.hl_gi.hli_ss == 1)) { /* this is (or: should be, I hope I got this right) a subpicture plane, that hides all menu buttons */ uint8_t empty_spu[] = { 0x00, 0x26, 0x00, 0x08, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x20, 0x01, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x06, 0x00, 0x04, 0x00, 0x07, 0xFF, 0x00, 0x01, 0x00, 0x20, 0x02, 0xFF }; /* leaving menu */ dxr3_spudec_clear_nav_list(this); this->pci_cur.pci.hli.hl_gi.hli_ss = 0; this->menu = 0; this->button_filter = 1; pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, NULL); write(this->fd_spu, empty_spu, sizeof(empty_spu)); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } } pthread_mutex_unlock(&this->pci_lock); return; } /* check, if we need to process the next PCI from the list */ pthread_mutex_lock(&this->pci_lock); dxr3_spudec_update_nav(this); pthread_mutex_unlock(&this->pci_lock); /* Look for the display duration entry in the spu packets. * If the spu is a menu button highlight pane, this entry must not exist, * because the spu is hidden, when the menu is left, not by timeout. * Some broken dvds do not respect this and therefore confuse the spu * decoding pipeline of the card. We fix this here. */ if (!state->spu_length) { state->spu_length = buf->content[0] << 8 | buf->content[1]; state->spu_ctrl = (buf->content[2] << 8 | buf->content[3]) + 2; state->spu_end = 0; state->end_found = 0; state->bytes_passed = 0; } if (!state->end_found) { int offset_in_buffer = state->spu_ctrl - state->bytes_passed; if (offset_in_buffer >= 0 && offset_in_buffer < buf->size) state->spu_end = buf->content[offset_in_buffer] << 8; offset_in_buffer++; if (offset_in_buffer >= 0 && offset_in_buffer < buf->size) { state->spu_end |= buf->content[offset_in_buffer]; state->end_found = 1; } } if (state->end_found && this->menu) { int offset_in_buffer = state->spu_end - state->bytes_passed; if (offset_in_buffer >= 0 && offset_in_buffer < buf->size) buf->content[offset_in_buffer] = 0x00; offset_in_buffer++; if (offset_in_buffer >= 0 && offset_in_buffer < buf->size) buf->content[offset_in_buffer] = 0x00; offset_in_buffer += 3; if (offset_in_buffer >= 0 && offset_in_buffer < buf->size && buf->content[offset_in_buffer] == 0x02) buf->content[offset_in_buffer] = 0x00; } state->spu_length -= buf->size; if (state->spu_length < 0) state->spu_length = 0; state->bytes_passed += buf->size; /* filter unwanted streams */ if (buf->decoder_flags & BUF_FLAG_PREVIEW) { llprintf(LOG_SPU, "Dropping SPU channel %d. Preview data\n", stream_id); return; } if (state->stream_filter == 0) { llprintf(LOG_SPU, "Dropping SPU channel %d. Stream filtered\n", stream_id); return; } if (this->anamorphic && !this->dxr3_vo->widescreen_enabled && this->stream->spu_channel_user == -1 && this->stream->spu_channel_letterbox >= 0) { /* Use the letterbox version of the subpicture for letterboxed display. */ spu_channel = this->stream->spu_channel_letterbox; } if ((spu_channel & 0x1f) != stream_id) { llprintf(LOG_SPU, "Dropping SPU channel %d. Not selected stream_id\n", stream_id); return; } if ((this->menu == 0) && (spu_channel & 0x80)) { llprintf(LOG_SPU, "Dropping SPU channel %d. Only allow forced display SPUs\n", stream_id); return; } pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); /* write sync timestamp to the card */ if (buf->pts) { int64_t vpts; uint32_t vpts32; vpts = this->stream->metronom->got_spu_packet(this->stream->metronom, buf->pts); llprintf(LOG_PTS, "pts = %lld vpts = %lld\n", buf->pts, vpts); vpts32 = vpts; if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPTS, &vpts32)) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: spu setpts failed (%s)\n", strerror(errno)); } /* has video out tampered with our palette */ if (this->dxr3_vo->clut_cluttered) { if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_SETPALETTE, this->clut)) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: failed to set CLUT (%s)\n", strerror(errno)); this->dxr3_vo->clut_cluttered = 0; } /* write spu data to the card */ llprintf(LOG_SPU, "write: SPU_FD = %i\n",this->fd_spu); written = write(this->fd_spu, buf->content, buf->size); if (written < 0) { xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: spu device write failed (%s)\n", strerror(errno)); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); return; } if (written != buf->size) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: Could only write %d of %d spu bytes.\n", written, buf->size); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } static void dxr3_spudec_reset(spu_decoder_t *this_gen) { dxr3_spudec_t *this = (dxr3_spudec_t *)this_gen; int i; for (i = 0; i < MAX_SPU_STREAMS; i++) this->spu_stream_state[i].spu_length = 0; pthread_mutex_lock(&this->pci_lock); dxr3_spudec_clear_nav_list(this); pthread_mutex_unlock(&this->pci_lock); } static void dxr3_spudec_discontinuity(spu_decoder_t *this_gen) { dxr3_spudec_t *this = (dxr3_spudec_t *)this_gen; pthread_mutex_lock(&this->pci_lock); dxr3_spudec_clear_nav_list(this); pthread_mutex_unlock(&this->pci_lock); } static void dxr3_spudec_dispose(spu_decoder_t *this_gen) { uint8_t empty_spu[] = { 0x00, 0x26, 0x00, 0x08, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x20, 0x01, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x06, 0x00, 0x04, 0x00, 0x07, 0xFF, 0x00, 0x01, 0x00, 0x20, 0x02, 0xFF }; dxr3_spudec_t *this = (dxr3_spudec_t *)this_gen; llprintf(LOG_SPU, "close: SPU_FD = %i\n",this->fd_spu); pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); /* clear any remaining spu */ ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, NULL); write(this->fd_spu, empty_spu, sizeof(empty_spu)); close(this->fd_spu); this->fd_spu = 0; this->dxr3_vo->fd_spu = 0; pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); dxr3_spudec_clear_nav_list(this); xine_event_dispose_queue(this->event_queue); pthread_mutex_destroy(&this->pci_lock); this->class->instance = 0; free (this); } static int dxr3_spudec_interact_info(spu_decoder_t *this_gen, void *data) { dxr3_spudec_t *this = (dxr3_spudec_t *)this_gen; pthread_mutex_lock(&this->pci_lock); dxr3_spudec_update_nav(this); memcpy(data, &this->pci_cur.pci, sizeof(pci_t)); pthread_mutex_unlock(&this->pci_lock); return 1; } static void dxr3_spudec_set_button(spu_decoder_t *this_gen, int32_t button, int32_t mode) { dxr3_spudec_t *this = (dxr3_spudec_t *)this_gen; em8300_button_t btn; llprintf(LOG_BTN, "setting button\n"); this->buttonN = button; pthread_mutex_lock(&this->pci_lock); dxr3_spudec_update_nav(this); if (mode > 0 && !this->button_filter && (dxr3_spudec_copy_nav_to_btn(this, mode - 1, &btn ) > 0)) { pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, &btn)) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: failed to set spu button (%s)\n", strerror(errno)); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } pthread_mutex_unlock(&this->pci_lock); if (mode == 2) this->button_filter = 1; llprintf(LOG_BTN, "buttonN = %u\n", this->buttonN); } static inline int dxr3_present(xine_stream_t *stream) { plugin_node_t *node; video_driver_class_t *vo_class; int present = 0; if (stream->video_driver && stream->video_driver->node) { node = (plugin_node_t *)stream->video_driver->node; if (node->plugin_class) { vo_class = (video_driver_class_t *)node->plugin_class; if (vo_class->get_identifier) present = (strcmp(vo_class->get_identifier(vo_class), DXR3_VO_ID) == 0); } } llprintf(LOG_SPU, "dxr3 %s\n", present ? "present" : "not present"); return present; } static inline void dxr3_spudec_handle_event(dxr3_spudec_t *this) { xine_event_t *event; while ((event = xine_event_get(this->event_queue))) { llprintf(LOG_SPU, "event caught: SPU_FD = %i\n",this->fd_spu); switch (event->type) { case XINE_EVENT_FRAME_FORMAT_CHANGE: /* we are in anamorphic mode, if the frame is 16:9, but not pan&scan'ed */ this->anamorphic = (((xine_format_change_data_t *)event->data)->aspect == 3) && (((xine_format_change_data_t *)event->data)->pan_scan == 0); llprintf(LOG_BTN, "anamorphic mode %s\n", this->anamorphic ? "on" : "off"); break; } xine_event_free(event); } } static inline void dxr3_spudec_clear_nav_list(dxr3_spudec_t *this) { while (this->pci_cur.next) { pci_node_t *node = this->pci_cur.next->next; free(this->pci_cur.next); this->pci_cur.next = node; } /* invalidate current timestamp */ this->pci_cur.pci.hli.hl_gi.hli_s_ptm = (uint32_t)-1; } static inline void dxr3_spudec_update_nav(dxr3_spudec_t *this) { metronom_clock_t *clock = this->stream->xine->clock; if (this->pci_cur.next && this->pci_cur.next->vpts <= clock->get_current_time(clock)) { pci_node_t *node = this->pci_cur.next; xine_fast_memcpy(&this->pci_cur, this->pci_cur.next, sizeof(pci_node_t)); dxr3_spudec_process_nav(this); free(node); } } static void dxr3_spudec_process_nav(dxr3_spudec_t *this) { em8300_button_t btn; this->menu = 1; this->button_filter = 0; if (this->pci_cur.pci.hli.hl_gi.fosl_btnn > 0) { /* a button is forced here, inform nav plugin */ xine_event_t event; this->buttonN = this->pci_cur.pci.hli.hl_gi.fosl_btnn; event.type = XINE_EVENT_INPUT_BUTTON_FORCE; event.stream = this->stream; event.data = &this->buttonN; event.data_length = sizeof(this->buttonN); xine_event_send(this->stream, &event); } if ((dxr3_spudec_copy_nav_to_btn(this, 0, &btn ) > 0)) { pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, &btn)) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: failed to set spu button (%s)\n", strerror(errno)); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } else { /* current button does not exist -> use another one */ xine_event_t event; xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("requested button not available\n")); if (this->buttonN > this->pci_cur.pci.hli.hl_gi.btn_ns) this->buttonN = this->pci_cur.pci.hli.hl_gi.btn_ns; else this->buttonN = 1; event.type = XINE_EVENT_INPUT_BUTTON_FORCE; event.stream = this->stream; event.data = &this->buttonN; event.data_length = sizeof(this->buttonN); xine_event_send(this->stream, &event); if ((dxr3_spudec_copy_nav_to_btn(this, 0, &btn ) > 0)) { pthread_mutex_lock(&this->dxr3_vo->spu_device_lock); if (ioctl(this->fd_spu, EM8300_IOCTL_SPU_BUTTON, &btn)) xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: failed to set spu button (%s)\n", strerror(errno)); pthread_mutex_unlock(&this->dxr3_vo->spu_device_lock); } else { xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "no working menu button found\n"); } } } static int dxr3_spudec_copy_nav_to_btn(dxr3_spudec_t *this, int32_t mode, em8300_button_t *btn) { btni_t *button_ptr = NULL; if ((this->buttonN <= 0) || (this->buttonN > this->pci_cur.pci.hli.hl_gi.btn_ns)) return -1; /* choosing a button from a matching button group */ if (this->anamorphic && !this->dxr3_vo->widescreen_enabled && this->stream->spu_channel_user == -1 && this->stream->spu_channel_letterbox != this->stream->spu_channel && this->stream->spu_channel_letterbox >= 0) { unsigned int btns_per_group = 36 / this->pci_cur.pci.hli.hl_gi.btngr_ns; /* use a letterbox button group for letterboxed anamorphic menus on tv out */ if (!button_ptr && this->pci_cur.pci.hli.hl_gi.btngr_ns >= 1 && (this->pci_cur.pci.hli.hl_gi.btngr1_dsp_ty & 2)) button_ptr = &this->pci_cur.pci.hli.btnit[0 * btns_per_group + this->buttonN - 1]; if (!button_ptr && this->pci_cur.pci.hli.hl_gi.btngr_ns >= 2 && (this->pci_cur.pci.hli.hl_gi.btngr2_dsp_ty & 2)) button_ptr = &this->pci_cur.pci.hli.btnit[1 * btns_per_group + this->buttonN - 1]; if (!button_ptr && this->pci_cur.pci.hli.hl_gi.btngr_ns >= 3 && (this->pci_cur.pci.hli.hl_gi.btngr3_dsp_ty & 2)) button_ptr = &this->pci_cur.pci.hli.btnit[2 * btns_per_group + this->buttonN - 1]; xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "No suitable letterbox button group found.\n"); _x_assert(button_ptr); } else { unsigned int btns_per_group = 36 / this->pci_cur.pci.hli.hl_gi.btngr_ns; /* otherwise use a normal 4:3 or widescreen button group */ if (!button_ptr && this->pci_cur.pci.hli.hl_gi.btngr_ns >= 1 && !(this->pci_cur.pci.hli.hl_gi.btngr1_dsp_ty & 6)) button_ptr = &this->pci_cur.pci.hli.btnit[0 * btns_per_group + this->buttonN - 1]; if (!button_ptr && this->pci_cur.pci.hli.hl_gi.btngr_ns >= 2 && !(this->pci_cur.pci.hli.hl_gi.btngr2_dsp_ty & 6)) button_ptr = &this->pci_cur.pci.hli.btnit[1 * btns_per_group + this->buttonN - 1]; if (!button_ptr && this->pci_cur.pci.hli.hl_gi.btngr_ns >= 3 && !(this->pci_cur.pci.hli.hl_gi.btngr3_dsp_ty & 6)) button_ptr = &this->pci_cur.pci.hli.btnit[2 * btns_per_group + this->buttonN - 1]; } if (!button_ptr) { xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "dxr3_decode_spu: No suitable menu button group found, using group 1.\n"); button_ptr = &this->pci_cur.pci.hli.btnit[this->buttonN - 1]; } if(button_ptr->btn_coln != 0) { llprintf(LOG_BTN, "normal button clut, mode %d\n", mode); btn->color = (this->pci_cur.pci.hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> 16); btn->contrast = (this->pci_cur.pci.hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode]); btn->left = button_ptr->x_start; btn->top = button_ptr->y_start; btn->right = button_ptr->x_end; btn->bottom = button_ptr->y_end; return 1; } return -1; } static inline void dxr3_swab_clut(int *clut) { int i; for (i=0; i<16; i++) clut[i] = bswap_32(clut[i]); }