/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2001 the xine project * * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 * * 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: xine_decoder.c,v 1.12 2003/12/25 11:07:32 georgedon Exp $ * * stuff needed to turn libspu into a xine decoder plugin */ #include #include #include #include #include #include #include #include "xine_internal.h" #include "buffer.h" #include "video_out/alphablend.h" /* For clut_t */ #include "xine-engine/bswap.h" #include "xineutils.h" #include "spu.h" #ifdef HAVE_DVDNAV # include # include #else # include "nav_read.h" # include "nav_types.h" #endif /* #define LOG_DEBUG 1 #define LOG_BUTTON 1 */ static clut_t __default_clut[] = { CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x10, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), CLUT_Y_CR_CB_INIT(0x51, 0xef, 0x5a), CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x36, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x51, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x10, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef), CLUT_Y_CR_CB_INIT(0x5c, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0xbf, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x1c, 0x80, 0x80), CLUT_Y_CR_CB_INIT(0x28, 0x6d, 0xef) }; static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { uint32_t stream_id; spudec_seq_t *cur_seq; spudec_decoder_t *this = (spudec_decoder_t *) this_gen; stream_id = buf->type & 0x1f ; cur_seq = &this->spudec_stream_state[stream_id].ra_seq; #ifdef LOG_DEBUG printf("libspudec:got buffer type = %x\n", buf->type); #endif 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 ) { #ifdef LOG_DEBUG printf("libspudec: SPU CLUT\n"); #endif if (buf->content[0]) { /* cheap endianess detection */ xine_fast_memcpy(this->state.clut, buf->content, sizeof(uint32_t)*16); } else { int i; uint32_t *clut = (uint32_t*) buf->content; for (i = 0; i < 16; i++) this->state.clut[i] = bswap_32(clut[i]); } this->state.need_clut = 0; return; } if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_SUBP_CONTROL ) { /* FIXME: I don't think SUBP_CONTROL is used any more */ int i; uint32_t *subp_control = (uint32_t*) buf->content; for (i = 0; i < 32; i++) { this->spudec_stream_state[i].stream_filter = subp_control[i]; } return; } if ( buf->decoder_info[2] == SPU_DVD_SUBTYPE_NAV ) { #ifdef LOG_DEBUG printf("libspudec:got nav packet 1\n"); #endif spudec_decode_nav(this,buf); return; } #ifdef LOG_DEBUG printf("libspudec:got buffer type = %x\n", buf->type); #endif if (buf->decoder_flags & BUF_FLAG_PREVIEW) /* skip preview data */ return; if ( this->spudec_stream_state[stream_id].stream_filter == 0) return; if (buf->pts) { metronom_t *metronom = this->stream->metronom; int64_t vpts = metronom->got_spu_packet(metronom, buf->pts); // printf("setting vpts=%d, pts=%d\n", vpts, buf->pts); this->spudec_stream_state[stream_id].vpts = vpts; /* Show timer */ this->spudec_stream_state[stream_id].pts = buf->pts; /* Required to match up with NAV packets */ //printf("=========================== set stream_id=%d, vpts=%d\n", stream_id, this->spudec_stream_state[stream_id].vpts); } spudec_reassembly(&this->spudec_stream_state[stream_id].ra_seq, buf->content, buf->size); if(this->spudec_stream_state[stream_id].ra_seq.complete == 1) { if(this->spudec_stream_state[stream_id].ra_seq.broken) { printf("libspudec: dropping broken SPU\n"); this->spudec_stream_state[stream_id].ra_seq.broken = 0; } else { spudec_process(this,stream_id); } } } extern int default_active_button; static void spudec_reset (spu_decoder_t *this_gen) { spudec_decoder_t *this = (spudec_decoder_t *) this_gen; video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); int i; default_active_button=-1; if( this->menu_handle >= 0 ) ovl_manager->free_handle(ovl_manager, this->menu_handle); this->menu_handle = -1; for (i=0; i < MAX_STREAMS; i++) { if( this->spudec_stream_state[i].overlay_handle >= 0 ) ovl_manager->free_handle(ovl_manager, this->spudec_stream_state[i].overlay_handle); this->spudec_stream_state[i].overlay_handle = -1; this->spudec_stream_state[i].ra_seq.complete = 1; this->spudec_stream_state[i].ra_seq.broken = 0; } force_free_color_blocks(); /* by rwlin@avamax.com */ } static void spudec_discontinuity (spu_decoder_t *this_gen) { } static void spudec_dispose (spu_decoder_t *this_gen) { spudec_decoder_t *this = (spudec_decoder_t *) this_gen; int i; video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); if( this->menu_handle >= 0 ) ovl_manager->free_handle(ovl_manager, this->menu_handle); this->menu_handle = -1; for (i=0; i < MAX_STREAMS; i++) { if( this->spudec_stream_state[i].overlay_handle >= 0 ) ovl_manager->free_handle(ovl_manager, this->spudec_stream_state[i].overlay_handle); this->spudec_stream_state[i].overlay_handle = -1; free (this->spudec_stream_state[i].ra_seq.buf); } pthread_mutex_destroy(&this->nav_pci_lock); free (this->event.object.overlay); free (this); } /* gets the current already correctly processed nav_pci info */ /* This is not perfectly in sync with the display, but all the same, */ /* much closer than doing it at the input stage. */ /* returns a bool for error/success.*/ static int spudec_get_interact_info (spu_decoder_t *this_gen, void *data) { spudec_decoder_t *this = (spudec_decoder_t *) this_gen; /*printf("get_interact_info() called\n");*/ if (!this || !data) return 0; /*printf("get_interact_info() coping nav_pci\n");*/ pthread_mutex_lock(&this->nav_pci_lock); memcpy(data, &this->pci, sizeof(pci_t) ); pthread_mutex_unlock(&this->nav_pci_lock); return 1; } extern dvdspu_color_block_t *global_color_blocks; static void spudec_set_button (spu_decoder_t *this_gen, int32_t button, int32_t show) { spudec_decoder_t *this = (spudec_decoder_t *) this_gen; /* This function will move to video_overlay * when video_overlay does menus */ video_overlay_manager_t *ovl_manager; video_overlay_event_t *overlay_event = NULL; vo_overlay_t *overlay = NULL; overlay_event = xine_xmalloc (sizeof(video_overlay_event_t)); overlay = xine_xmalloc (sizeof(vo_overlay_t)); /* FIXME: Watch out for threads. We should really put a lock on this * because events is a different thread than decode_data */ if( this->menu_handle < 0 ) { if (this->stream->video_out) { ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); this->menu_handle = ovl_manager->get_handle(ovl_manager,1); } } #ifdef LOG_BUTTON printf ("libspudec:xine_decoder.c:spudec_event_listener:this=%p\n",this); printf ("libspudec:xine_decoder.c:spudec_event_listener:this->menu_handle=%d\n",this->menu_handle); #endif XINE_ASSERT(this->menu_handle >= 0, "Menu handle alloc failed. No more overlays objects available. Only 5 at once please."); if (show > 0) { #ifdef LOG_NAV fprintf (stderr,"libspudec:xine_decoder.c:spudec_event_listener:buttonN = %u show=%d\n", button, show); #endif this->buttonN = button; if (this->button_filter != 1) { #ifdef LOG_BUTTON fprintf (stdout,"libspudec:xine_decoder.c:spudec_event_listener:buttonN updates not allowed\n"); #endif /* Only update highlight is the menu will let us */ free(overlay_event); free(overlay); return; } if (show == 2) { // removed by rwlin@avamax.com to fix the lightbar disappearing problem //this->button_filter = 2; } pthread_mutex_lock(&this->nav_pci_lock); overlay_event->object.handle = this->menu_handle; overlay_event->object.pts = this->pci.hli.hl_gi.hli_s_ptm; overlay_event->object.overlay=overlay; overlay_event->event_type = OVERLAY_EVENT_MENU_BUTTON; #ifdef LOG_BUTTON fprintf(stderr, "libspudec:Button Overlay\n"); #endif spudec_copy_nav_to_overlay(&this->pci, this->state.clut, this->buttonN, show-1, overlay, &this->overlay ); /* by rwlin@avamax.com to clear chgcol information */ if(this->pci.hli.hl_gi.btn_ns>0) force_free_color_blocks(); pthread_mutex_unlock(&this->nav_pci_lock); } else { fprintf (stderr,"libspudec:xine_decoder.c:spudec_event_listener:HIDE ????\n"); XINE_ASSERT(0, "We dropped out here for some reason"); overlay_event->object.handle = this->menu_handle; overlay_event->event_type = OVERLAY_EVENT_HIDE; } overlay_event->vpts = 0; if (this->stream->video_out) { ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); #ifdef LOG_BUTTON fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n", overlay_event->event_type, this->stream->xine->clock->get_current_time(this->stream->xine->clock), overlay_event->vpts); #endif ovl_manager->add_event (ovl_manager, (void *)overlay_event); free(overlay_event); free(overlay); } else { free(overlay_event); free(overlay); } return; } static spu_decoder_t *open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { spudec_decoder_t *this ; int i; this = (spudec_decoder_t *) xine_xmalloc (sizeof (spudec_decoder_t)); this->spu_decoder.decode_data = spudec_decode_data; this->spu_decoder.reset = spudec_reset; this->spu_decoder.discontinuity = spudec_discontinuity; this->spu_decoder.dispose = spudec_dispose; this->spu_decoder.get_interact_info = spudec_get_interact_info; this->spu_decoder.set_button = spudec_set_button; this->stream = stream; this->class = (spudec_class_t *) class_gen; this->menu_handle = -1; this->buttonN = 1; this->event.object.overlay = malloc(sizeof(vo_overlay_t)); memset(this->event.object.overlay,0,sizeof(vo_overlay_t)); pthread_mutex_init(&(this->event.object.overlay->update_lock),NULL); pthread_mutex_init(&this->nav_pci_lock, NULL); this->ovl_caps = stream->video_out->get_capabilities(stream->video_out); this->output_open = 0; this->last_event_vpts = 0; for (i=0; i < MAX_STREAMS; i++) { this->spudec_stream_state[i].stream_filter = 1; /* So it works with non-navdvd plugins */ this->spudec_stream_state[i].ra_seq.complete = 1; this->spudec_stream_state[i].overlay_handle = -1; } /* FIXME:Do we really need a default clut? */ xine_fast_memcpy(this->state.clut, __default_clut, sizeof(this->state.clut)); this->state.need_clut = 1; return &this->spu_decoder; } static char *get_identifier (spu_decoder_class_t *this) { #ifdef LOG_DEBUG printf ("libspudec:get_identifier called\n"); #endif return "spudec"; } static char *get_description (spu_decoder_class_t *this) { #ifdef LOG_DEBUG printf ("libspudec:get_description called\n"); #endif return "DVD/VOB SPU decoder plugin"; } static void dispose_class (spu_decoder_class_t *this) { #ifdef LOG_DEBUG printf ("libspudec:dispose_class called\n"); #endif free (this); } static void *init_plugin (xine_t *xine, void *data) { spudec_class_t *this; this = (spudec_class_t *) malloc (sizeof (spudec_class_t)); this->decoder_class.open_plugin = open_plugin; this->decoder_class.get_identifier = get_identifier; this->decoder_class.get_description = get_description; this->decoder_class.dispose = dispose_class; printf ("libspudec:init_plugin called\n"); return this; } /* plugin catalog information */ static uint32_t supported_types[] = { BUF_SPU_DVD, 0 }; static decoder_info_t dec_info_data = { supported_types, /* supported types */ 5 /* priority */ }; plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_SPU_DECODER, 14, "spudec", XINE_VERSION_CODE, &dec_info_data, &init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };