/* 2004.02.01 first released source code for IOMP */ /* * * Copyright (C) James Courtier-Dutton James@superbug.demon.co.uk - July 2001 * * spu.c - converts DVD subtitles to an XPM image * * Mostly based on hard work by: * * Copyright (C) 2000 Samuel Hocevar * and Michel Lespinasse * * Lots of rearranging by: * Aaron Holtzman * Thomas Mirlacher * implemented reassembling * cleaner implementation of SPU are saving * overlaying (proof of concept for now) * ... and yes, it works now with oms * added tranparency (provided by the SPU hdr) * changed structures for easy porting to MGAs DVD mode * This file is part of xine * This file was originally part of the OMS program. * * This program 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, or (at your option) * any later version. * * This program 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; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: spu.c,v 1.20 2004/02/18 11:25:19 rwlin Exp $ * */ #include #include #include #include #include #include #include #include #include #include "xine_internal.h" #include "video_out/alphablend.h" #include "xineutils.h" #include "spu.h" #include "buffer.h" #include "xine-engine/bswap.h" #ifdef HAVE_DVDNAV # include # include #else # include "nav_read.h" # include "nav_print.h" #endif /* #define LOG_DEBUG 1 #define LOG_BUTTON 1 #define LOG_NAV 1 */ void spudec_reassembly (spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len); void spudec_process( spudec_decoder_t *this, int stream_id); void spudec_decode_nav( spudec_decoder_t *this, buf_element_t *buf); void spudec_copy_nav_to_overlay(pci_t* nav_pci, uint32_t* clut, int32_t button, int32_t mode, vo_overlay_t * overlay, vo_overlay_t * base ); static void spudec_do_commands (spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl); static void spudec_draw_picture (spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl); static void spudec_discover_clut (spudec_state_t *state, vo_overlay_t *ovl); #ifdef LOG_DEBUG static void spudec_print_overlay( vo_overlay_t *overlay ); #endif void spudec_decode_nav(spudec_decoder_t *this, buf_element_t *buf) { uint8_t *p; uint32_t packet_len; uint32_t stream_id; uint32_t header_len; pci_t pci; dsi_t dsi; video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); p = buf->content; if (p[0] || p[1] || (p[2] != 1)) { printf("libspudec:spudec_decode_nav:nav demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]); return; } packet_len = p[4] << 8 | p[5]; stream_id = p[3]; header_len = 6; p += header_len; if (stream_id == 0xbf) { /* Private stream 2 */ /* int i; * for(i=0;i<80;i++) { * printf("%02x ",p[i]); * } * printf("\n p[0]=0x%02x\n",p[0]); */ if(p[0] == 0x00) { #ifdef LOG_NAV int btngr_ns = 0, btn_ns = 0; printf("libspudec:nav_PCI\n"); #endif navRead_PCI(&pci, p+1); /* printf("libspudec:nav:hli_ss=%u, hli_s_ptm=%u, hli_e_ptm=%u, btn_sl_e_ptm=%u pts=%lli\n", pci.hli.hl_gi.hli_ss, pci.hli.hl_gi.hli_s_ptm, pci.hli.hl_gi.hli_e_ptm, pci.hli.hl_gi.btn_se_e_ptm, buf->pts); printf("libspudec:nav:btn_sn/ofn=%u, btn_ns=%u, fosl_btnn=%u, foac_btnn=%u\n", pci.hli.hl_gi.btn_ofn, pci.hli.hl_gi.btn_ns, pci.hli.hl_gi.fosl_btnn, pci.hli.hl_gi.foac_btnn); printf("btngr_ns %d\n", pci.hli.hl_gi.btngr_ns); printf("btngr%d_dsp_ty 0x%02x\n", 1, pci.hli.hl_gi.btngr1_dsp_ty); printf("btngr%d_dsp_ty 0x%02x\n", 2, pci.hli.hl_gi.btngr2_dsp_ty); printf("btngr%d_dsp_ty 0x%02x\n", 3, pci.hli.hl_gi.btngr3_dsp_ty); //navPrint_PCI(&pci); */ #ifdef LOG_NAV navPrint_PCI_GI(&pci.pci_gi); navPrint_NSML_AGLI(&pci.nsml_agli); //navPrint_HLI(&pci.hli); navPrint_HL_GI(&pci.hli.hl_gi, & btngr_ns, & btn_ns); #endif } p += packet_len; /* We should now have a DSI packet. */ /* We don't need anything from the DSI packet here. */ if(p[6] == 0x01) { packet_len = p[4] << 8 | p[5]; p += 6; #ifdef LOG_NAV printf("NAV DSI packet\n"); #endif navRead_DSI(&dsi, p+1); // self->vobu_start = self->dsi.dsi_gi.nv_pck_lbn; // self->vobu_length = self->dsi.dsi_gi.vobu_ea; } } pthread_mutex_lock(&this->nav_pci_lock); switch (pci.hli.hl_gi.hli_ss) { case 0: /* No Highlight information for this VOBU */ if ( this->pci.hli.hl_gi.hli_ss == 1) { /* Hide menu spu between menus */ #ifdef LOG_BUTTON printf("libspudec:nav:SHOULD HIDE SPU here\n"); #endif if( this->menu_handle < 0 ) { this->menu_handle = ovl_manager->get_handle(ovl_manager,1); } if( this->menu_handle >= 0 ) { this->event.object.handle = this->menu_handle; this->event.event_type = OVERLAY_EVENT_HIDE; /* hide menu right now */ this->event.vpts = 0; ovl_manager->add_event(ovl_manager, (void *)&this->event); } else { printf("libspudec: No video_overlay handles left for menu\n"); } } xine_fast_memcpy(&this->pci, &pci, sizeof(pci_t)); /* incoming SPUs will be plain subtitles */ this->event.object.object_type = 0; if (this->button_filter) { /* we possibly had buttons before, so we update the UI info */ xine_event_t event; xine_ui_data_t data; event.type = XINE_EVENT_UI_NUM_BUTTONS; event.data = &data; event.data_length = sizeof(data); data.num_buttons = 0; xine_event_send(this->stream, &event); } this->button_filter=0; break; case 1: /* All New Highlight information for this VOBU */ xine_fast_memcpy(&this->pci, &pci, sizeof(pci_t)); /* incoming SPUs will be menus */ this->event.object.object_type = 1; if (!this->button_filter) { /* we possibly entered a menu, so we update the UI button info */ xine_event_t event; xine_ui_data_t data; event.type = XINE_EVENT_UI_NUM_BUTTONS; event.data = &data; event.data_length = sizeof(data); data.num_buttons = pci.hli.hl_gi.btn_ns; xine_event_send(this->stream, &event); } this->button_filter=1; /******************************* * We should do something about fosl_btnn, but * until we can send the info to dvdnav, ignore it. * if( pci.hli.hl_gi.fosl_btnn) { * this->buttonN = pci.hli.hl_gi.fosl_btnn; * } *******************************/ break; case 2: /* Use Highlight information from previous VOBU */ this->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; this->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; this->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; break; case 3: /* Use Highlight information from previous VOBU except commands, which come from this VOBU */ this->pci.pci_gi.vobu_s_ptm = pci.pci_gi.vobu_s_ptm; this->pci.pci_gi.vobu_e_ptm = pci.pci_gi.vobu_e_ptm; this->pci.pci_gi.vobu_se_e_ptm = pci.pci_gi.vobu_se_e_ptm; /* FIXME: Add command copying here */ break; default: printf("libspudec: unknown pci.hli.hl_gi.hli_ss = %d\n", pci.hli.hl_gi.hli_ss ); break; } pthread_mutex_unlock(&this->nav_pci_lock); return; } void spudec_reassembly (spudec_seq_t *seq, uint8_t *pkt_data, u_int pkt_len) { #ifdef LOG_DEBUG printf ("libspudec: seq->complete = %d\n", seq->complete); printf("libspudec:1: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n", seq->ra_offs, seq->seq_len, seq->buf_len, seq->buf); #endif if (seq->complete) { seq->seq_len = (((uint32_t)pkt_data[0])<<8) | pkt_data[1]; seq->cmd_offs = (((uint32_t)pkt_data[2])<<8) | pkt_data[3]; if (seq->cmd_offs >= seq->seq_len) { printf("libspudec:faulty stream\n"); seq->broken = 1; } if (seq->buf_len < seq->seq_len) { seq->buf_len = seq->seq_len; #ifdef LOG_DEBUG printf ("spu: MALLOC1: seq->buf %p, len=%d\n", seq->buf,seq->buf_len); #endif if (seq->buf) { free(seq->buf); seq->buf = NULL; } seq->buf = malloc(seq->buf_len); #ifdef LOG_DEBUG printf ("spu: MALLOC2: seq->buf %p, len=%d\n", seq->buf,seq->buf_len); #endif } seq->ra_offs = 0; #ifdef LOG_DEBUG printf ("spu: buf_len: %d\n", seq->buf_len); printf ("spu: cmd_off: %d\n", seq->cmd_offs); #endif } #ifdef LOG_DEBUG printf("libspudec:2: seq->ra_offs = %d, seq->seq_len = %d, seq->buf_len = %d, seq->buf=%p\n", seq->ra_offs, seq->seq_len, seq->buf_len, seq->buf); #endif if (seq->ra_offs < seq->seq_len) { if (seq->ra_offs + pkt_len > seq->seq_len) pkt_len = seq->seq_len - seq->ra_offs; memcpy (seq->buf + seq->ra_offs, pkt_data, pkt_len); seq->ra_offs += pkt_len; } else { printf("libspudec:faulty stream\n"); seq->broken = 1; } if (seq->ra_offs == seq->seq_len) { seq->finished = 0; seq->complete = 1; return; /* sequence ready */ } seq->complete = 0; return; } extern dvdspu_color_block_t *global_color_blocks; /* in xine-engine */ extern dvdspu_color_block_t *global_travel_cb; extern int osd_is_showing; extern int spu_current_enable; static dvdspu_color_block_t *TAIL = NULL; /* added by rwlin@avamax.com */ static void check_and_free_color_blocks() { dvdspu_color_block_t *cb; dvdspu_vert_block_t *vb,*vbtmp; cb = global_color_blocks; while(cb!=NULL) { if(cb->signature!=0x879A) { printf("cb->signature = %d\n", cb->signature); printf("CLEAR !!!!\n"); global_color_blocks=NULL; TAIL = NULL; global_travel_cb = NULL; break; } if(!cb->displayed) break; /* if(cb->next==NULL) { cb->displayed=0; printf("should not happend, cb->next==NULL but displayed\n"); break; }*/ global_color_blocks=cb->next; //printf("reset global_color_blocks=%p\n", global_color_blocks); /* deallocate it */ for(vb=cb->vert_blocks;vb;vb=vbtmp) { vbtmp=vb->next; if(vb->hori_blocks) free(vb->hori_blocks); free(vb); } cb->signature=0; /* in case we may reuse them after deallocating it */ if (global_travel_cb==cb) { global_travel_cb = NULL; printf("CLEAR2 !!\n"); } if (TAIL==cb) TAIL = NULL; /* clear TAIL */ free(cb); cb = global_color_blocks; } //pthread_mutex_unlock(&ovl->update_lock); } static int IS_SHOW; static int IS_HIDE; static int IS_WIPE = 0; /* kevin */ static int FORCE_CLEAR_FLAG = 0; extern int previous_timestamp; extern int osd_frame_counter; extern int forced_display; extern int enable_alpha; extern int enable_color; void force_free_color_blocks() { dvdspu_color_block_t *cb,*tmp; dvdspu_vert_block_t *vb,*vbtmp; previous_timestamp = 0; /* clear the time stamp, for video_out/alphablend.c */ for(cb=global_color_blocks;cb && cb->signature==0x879A;cb=tmp) { tmp=cb->next; /* deallocate it */ for(vb=cb->vert_blocks;vb;vb=vbtmp) { vbtmp=vb->next; if(vb->hori_blocks) free(vb->hori_blocks); free(vb); } cb->signature=0; /* in case we may reuse them after deallocating it */ free(cb); } global_color_blocks = NULL; TAIL = NULL; global_travel_cb = NULL; FORCE_CLEAR_FLAG = 1; osd_frame_counter = -1; /* reset to default */ osd_is_showing = 1; /* reset osd showing flag */ forced_display = 1; /* global variable in xine-engine/osd.c */ enable_alpha = 0; enable_color = 0; spu_current_enable = 1; // printf("CLEAR ALL CB global_color_blocks=NULL\n"); } #define OSD_CMD_COLORBLOCK 0 #define OSD_CMD_SHOW 1 #define OSD_CMD_HIDE 2 #define OSD_CMD_ALPHA 3 #define OSD_CMD_COLOR 4 extern int default_active_button; static spudec_decoder_t *active_spu_decoder=NULL; static long foac_end_time=0; static void* foac_btnn_thread(void *arg) { xine_event_t event; int buttonN; struct timeval tt; if(active_spu_decoder==NULL) return NULL; while(default_active_button>=0) { gettimeofday(&tt,NULL); if(tt.tv_sec>=(foac_end_time-1) && tt.tv_usec>800000) break; xine_usec_sleep(50000); } foac_end_time=0; if(default_active_button<0) return NULL; buttonN = default_active_button; event.stream = active_spu_decoder->stream; event.data = &buttonN; event.data_length = sizeof(buttonN); if(buttonN<63) { event.type = XINE_EVENT_INPUT_BUTTON_FORCE; xine_event_send(event.stream, &event); } event.type = XINE_EVENT_INPUT_SELECT; xine_event_send(event.stream, &event); default_active_button=-1; return NULL; } void spudec_process (spudec_decoder_t *this, int stream_id) { int FIRST_SHOW_HIDE = 1; int SPU_COUNTER = 0; int FIRST_SHOW_HIDE_COUNTER = 0; spudec_seq_t *cur_seq; video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager (this->stream->video_out); int pending = 1; cur_seq = &this->spudec_stream_state[stream_id].ra_seq; #ifdef LOG_DEBUG printf ("spu: Found SPU from stream %d pts=%lli vpts=%lli\n",stream_id, this->spudec_stream_state[stream_id].pts, this->spudec_stream_state[stream_id].vpts); #endif this->state.cmd_ptr = cur_seq->buf + cur_seq->cmd_offs; this->state.modified = 1; /* Only draw picture if = 1 on first event of SPU */ this->state.visible = OVERLAY_EVENT_SHOW; this->state.forced_display = 0; /* 0 - No value, 1 - Forced Display. */ this->state.delay = 0; cur_seq->finished=0; //printf("DISABLE FORCED DISPLAY\n"); forced_display = 0; /* global variable in xine-engine/osd.c */ // printf("spudec_process called\n"); do { check_and_free_color_blocks(); if (!(cur_seq->finished) ) { /* spu_channel is now set based on whether we are in the menu or not. */ /* Bit 7 is set if only forced display SPUs should be shown */ if ( (this->stream->spu_channel & 0x1f) != stream_id ) { #ifdef LOG_DEBUG printf ("spu: Dropping SPU channel %d. Not selected stream_id\n", stream_id); #endif return; } /* parse SPU command sequence, this will update forced_display, so it must come * before the check for it */ spudec_do_commands(&this->state, cur_seq, &this->overlay); /* FIXME: Check for Forced-display or subtitle stream * For subtitles, open event. * For menus, store it for later. */ if (cur_seq->broken) { printf("libspudec: dropping broken SPU\n"); cur_seq->broken = 0; return; } if ( (this->state.forced_display == 0) && (this->stream->spu_channel & 0x80) ) { #ifdef LOG_DEBUG printf ("spu: Dropping SPU channel %d. Only allow forced display SPUs\n", stream_id); #endif //return; spu_current_enable = 0; } else { spu_current_enable = 1; } #ifdef LOG_DEBUG /* spudec_print_overlay( &this->overlay ); */ printf ("spu: forced display:%s\n", this->state.forced_display ? "Yes" : "No" ); #endif pthread_mutex_lock(&this->nav_pci_lock); if (this->pci.hli.hl_gi.hli_s_ptm == this->spudec_stream_state[stream_id].pts) { if(this->pci.hli.hl_gi.foac_btnn>0 && foac_end_time<=0) { pthread_t *th; struct timeval tt; default_active_button=this->pci.hli.hl_gi.foac_btnn; active_spu_decoder=this; gettimeofday(&tt,NULL); foac_end_time=tt.tv_sec+(this->pci.hli.hl_gi.btn_se_e_ptm-this->pci.hli.hl_gi.hli_s_ptm)/90000; //printf("foac_button=%d after %d seconds\n",this->pci.hli.hl_gi.foac_btnn,(this->pci.hli.hl_gi.btn_se_e_ptm-this->pci.hli.hl_gi.hli_s_ptm)/90000); pthread_create(&th,NULL,foac_btnn_thread,NULL); } if (this->state.visible == OVERLAY_EVENT_HIDE) { /* menus are hidden via nav packet decoding, not here */ /* FIXME: James is not sure about this solution and may want to look this over. * I'm commiting it, because I haven't found a disc it breaks, but it fixes * some instead. Michael Roitzsch */ pthread_mutex_unlock(&this->nav_pci_lock); continue; } if ( this->pci.hli.hl_gi.fosl_btnn > 0) { int buttonN; xine_event_t event; this->buttonN = this->pci.hli.hl_gi.fosl_btnn ; event.type = XINE_EVENT_INPUT_BUTTON_FORCE; event.stream = this->stream; event.data = &buttonN; event.data_length = sizeof(buttonN); buttonN = this->buttonN; xine_event_send(this->stream, &event); } #ifdef LOG_BUTTON fprintf(stderr, "libspudec:Full Overlay\n"); #endif spudec_copy_nav_to_overlay(&this->pci, this->state.clut, this->buttonN, 0, &this->overlay, &this->overlay); } else { /* Subtitle and not a menu button */ int i; for (i = 0;i < 4; i++) { this->overlay.clip_color[i] = this->overlay.color[i]; this->overlay.clip_trans[i] = this->overlay.trans[i]; } } pthread_mutex_unlock(&this->nav_pci_lock); if ((this->state.modified) ) { spudec_draw_picture(&this->state, cur_seq, &this->overlay); } if (this->state.need_clut) { spudec_discover_clut(&this->state, &this->overlay); } /* Subtitle */ if( this->menu_handle < 0 ) { this->menu_handle = ovl_manager->get_handle(ovl_manager,1); } if( this->menu_handle < 0 ) { printf("libspudec: No video_overlay handles left for menu\n"); return; } this->event.object.handle = this->menu_handle; this->event.object.pts = this->spudec_stream_state[stream_id].pts; xine_fast_memcpy(this->event.object.overlay, &this->overlay, sizeof(vo_overlay_t)); #if 0 /* by kevin , copy an rle */ if ((this->overlay.data_size>0)&&(IS_SHOW_HIDE==0)&&this->event.object.overlay->rle!=NULL) { this->overlay.rle = malloc(this->overlay.data_size); xine_fast_memcpy(this->overlay.rle, this->event.object.overlay->rle, this->overlay.data_size); } else { this->overlay.rle = NULL; } #else this->overlay.rle = NULL; #endif //printf("CLEAR RLE this->overlay=%p\n", &this->overlay); /* For force display menus */ //if ( !(this->state.visible) ) { // this->state.visible = OVERLAY_EVENT_SHOW; //} this->event.event_type = this->state.visible; /* printf("spu event %d handle: %d vpts: %lli\n", this->event.event_type, this->event.object.handle, this->event.vpts ); */ this->event.vpts = this->spudec_stream_state[stream_id].vpts+(this->state.delay*1000); /* Keep all the events in the correct order. */ /* This corrects for errors during estimation around discontinuity */ //printf("VPTS NORMAL !!! this->event.vpts=%lld, this->last_event_vpts=%lld\n", if (FORCE_CLEAR_FLAG==1) { this->last_event_vpts = 0; // kevin FORCE_CLEAR_FLAG = 0; } // this->event.vpts, this->last_event_vpts); if( this->event.vpts < this->last_event_vpts ) { // printf("VPTS ERROR !!! this->event.vpts=%lld, this->last_event_vpts=%lld\n", // this->event.vpts, this->last_event_vpts); //this->last_event_vpts = this->event.vpts; /* kevin for test disc */ this->event.vpts = this->last_event_vpts + 1; } this->last_event_vpts = this->event.vpts; #ifdef LOG_BUTTON fprintf(stderr, "libspudec: add_event type=%d : current time=%lld, spu vpts=%lli\n", this->event.event_type, this->stream->xine->clock->get_current_time(this->stream->metronom), this->event.vpts); #endif if (SPU_COUNTER==0) { add_osd_command(OSD_CMD_SHOW, this->state.delay, 0); /* must be this->state.delay */ } SPU_COUNTER++; if (IS_SHOW!=0||IS_HIDE!=0) { #if 0 if (SPU_COUNTER<0) { this->state.modified = 1; ovl_manager->add_event(ovl_manager, (void *)&this->event); } else #endif if (FIRST_SHOW_HIDE) { this->state.modified = 1; // kevin ovl_manager->add_event(ovl_manager, (void *)&this->event); FIRST_SHOW_HIDE = 0; FIRST_SHOW_HIDE_COUNTER = SPU_COUNTER; } else { if (IS_SHOW) { //printf("receive show !! delay=%d\n", this->state.delay); add_osd_command(OSD_CMD_SHOW, this->state.delay, 0); } else { //printf("receive hide !! delay=%d\n", this->state.delay); add_osd_command(OSD_CMD_HIDE, this->state.delay, 0); } } } else { if (!IS_WIPE) ovl_manager->add_event(ovl_manager, (void *)&this->event); } } else { pending = 0; } } while (pending); } #define CMD_SPU_FORCE_DISPLAY 0x00 #define CMD_SPU_SHOW 0x01 #define CMD_SPU_HIDE 0x02 #define CMD_SPU_SET_PALETTE 0x03 #define CMD_SPU_SET_ALPHA 0x04 #define CMD_SPU_SET_SIZE 0x05 #define CMD_SPU_SET_PXD_OFFSET 0x06 #define CMD_SPU_WIPE 0x07 /* Not currently implemented */ #define CMD_SPU_EOF 0xff dvdspu_color_block_t * duplicate_cb(dvdspu_color_block_t *in_cb) { dvdspu_color_block_t *cb; dvdspu_vert_block_t *vb, *new_vb, *tail_vb; cb = (dvdspu_color_block_t *)malloc(sizeof(dvdspu_color_block_t)); memcpy(cb, in_cb, sizeof(dvdspu_color_block_t)); if (in_cb->vert_blocks!=NULL) { tail_vb = NULL; vb = in_cb->vert_blocks; while (vb) { new_vb = (dvdspu_vert_block_t *)malloc(sizeof(dvdspu_vert_block_t)); memcpy(new_vb, vb, sizeof(dvdspu_vert_block_t)); if (vb->hori_blocks!=NULL) { new_vb->hori_blocks = (dvdspu_hori_block_t *) malloc(sizeof(dvdspu_hori_block_t)*vb->num_hori_blocks); memcpy(new_vb->hori_blocks, vb->hori_blocks, sizeof(dvdspu_hori_block_t)* vb->num_hori_blocks); } if (tail_vb==NULL) { cb->vert_blocks = new_vb; } else { tail_vb->next = new_vb; } tail_vb = new_vb; vb = vb->next; } if (tail_vb!=NULL) tail_vb->next = NULL; } return cb; } static void add_osd_command(int osd_cmd, int delay, uint32_t value) /* 0 -> show */ { dvdspu_color_block_t *cb, *dcb; cb=(dvdspu_color_block_t *)malloc(sizeof(dvdspu_color_block_t)); cb->time_stamp = delay; cb->displayed=0; cb->signature=0x879A; cb->next=NULL; cb->osd_cmd = osd_cmd; /* show */ cb->vert_blocks=NULL; cb->alpha_color = value; if (TAIL==NULL) { //printf("reset global_color_blocks=%p\n", cb); global_color_blocks = cb; TAIL = cb; } else { if (TAIL->vert_blocks!=NULL) { dcb = duplicate_cb(TAIL); dcb->time_stamp = delay; cb->next = dcb; TAIL->next = cb; TAIL = dcb; } else { TAIL->next = cb; TAIL = cb; } } } uint32_t pack_trans(uint8_t *trans) { uint32_t v0, v1, v2, v3; v0 = trans[0]; v1 = trans[1]; v2 = trans[2]; v3 = trans[3]; return ((v0<<24)|(v1<<16)|(v2<<8)|(v3)); } static void spudec_do_commands(spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl) { uint8_t *buf = state->cmd_ptr; uint8_t *next_seq; int32_t param_length; uint32_t value; dvdspu_color_block_t *cb; #ifdef LOG_DEBUG printf ("spu: SPU DO COMMANDS\n"); #endif state->delay = (buf[0] << 8) + buf[1]; #ifdef LOG_DEBUG printf ("spu: \tdelay=%d\n",state->delay); #endif next_seq = seq->buf + (buf[2] << 8) + buf[3]; buf += 4; #ifdef LOG_DEBUG printf ("spu: \tnext_seq=%d\n",next_seq - seq->buf); #endif /* if next equals current, this is the last one */ if (state->cmd_ptr >= next_seq) next_seq = seq->buf + seq->seq_len; /* allow to run until end */ state->cmd_ptr = next_seq; IS_SHOW = 0; IS_HIDE = 0; IS_WIPE = 0; while (buf < next_seq && *buf != CMD_SPU_EOF) { switch (*buf) { case CMD_SPU_SHOW: /* show subpicture */ //printf("CMD_SPU_SHOW <-------------------------\n"); #ifdef LOG_DEBUG printf ("spu: \tshow subpicture\n"); #endif state->visible = OVERLAY_EVENT_SHOW; buf++; IS_SHOW = 1; //printf("SPU_IS_SHOW delay=%d\n", state->delay); break; case CMD_SPU_HIDE: /* hide subpicture */ //printf("CMD_SPU_HIDE <-------------------------\n"); #ifdef LOG_DEBUG printf ("spu: \thide subpicture\n"); #endif state->visible = OVERLAY_EVENT_HIDE; buf++; IS_HIDE = 1; //printf("SPU_IS_HIDE delay=%d\n", state->delay); break; case CMD_SPU_SET_PALETTE: { /* CLUT */ spudec_clut_t *clut = (spudec_clut_t *) (buf+1); state->cur_colors[3] = clut->entry0; state->cur_colors[2] = clut->entry1; state->cur_colors[1] = clut->entry2; state->cur_colors[0] = clut->entry3; /* This is a bit out of context for now */ ovl->color[3] = state->clut[clut->entry0]; ovl->color[2] = state->clut[clut->entry1]; ovl->color[1] = state->clut[clut->entry2]; ovl->color[0] = state->clut[clut->entry3]; #if 0 printf("CMD_SPU_SET_COLOR (%d,%d,%d,%d)\n", ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); #endif //value = pack_trans(ovl->color); //add_osd_command(OSD_CMD_COLOR, state->delay, value); #ifdef LOG_DEBUG printf ("spu: \tclut [%x %x %x %x]\n", ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); printf ("spu: \tclut base [%x %x %x %x]\n", clut->entry0, clut->entry1, clut->entry2, clut->entry3); #endif state->modified = 1; buf += 3; break; } case CMD_SPU_SET_ALPHA: { /* transparency palette */ spudec_clut_t *trans = (spudec_clut_t *) (buf+1); /* This should go into state for now */ if (state->delay==0) { ovl->trans[3] = trans->entry0; ovl->trans[2] = trans->entry1; ovl->trans[1] = trans->entry2; ovl->trans[0] = trans->entry3; enable_alpha = 0; #if 0 printf("CMD_SPU_SET_ALPHA delay=[%d](%d,%d,%d,%d)\n", state->delay, ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); #endif } else { static uint8_t my_trans[4]; my_trans[3] = trans->entry0; my_trans[2] = trans->entry1; my_trans[1] = trans->entry2; my_trans[0] = trans->entry3; value = pack_trans(my_trans); add_osd_command(OSD_CMD_ALPHA, state->delay, value); #if 0 printf("CMD_SPU_SET_ALPHA delay=[%d](%d,%d,%d,%d)\n", state->delay, my_trans[0], my_trans[1], my_trans[2], my_trans[3]); #endif } #ifdef LOG_DEBUG printf ("spu: \ttrans [%d %d %d %d]\n", ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); #endif //state->modified = 1; /* mark by kevin */ buf += 3; break; } case CMD_SPU_SET_SIZE: /* image coordinates */ /* state->o_left = (buf[1] << 4) | (buf[2] >> 4); state->o_right = (((buf[2] & 0x0f) << 8) | buf[3]); state->o_top = (buf[4] << 4) | (buf[5] >> 4); state->o_bottom = (((buf[5] & 0x0f) << 8) | buf[6]); */ ovl->x = (buf[1] << 4) | (buf[2] >> 4); ovl->y = (buf[4] << 4) | (buf[5] >> 4); ovl->width = (((buf[2] & 0x0f) << 8) | buf[3]) - ovl->x + 1; ovl->height = (((buf[5] & 0x0f) << 8) | buf[6]) - ovl->y + 1; ovl->clip_top = -1; ovl->clip_bottom = ovl->height - 1; ovl->clip_left = 0; ovl->clip_right = ovl->width - 1; #ifdef LOG_DEBUG printf ("spu: \tx = %d y = %d width = %d height = %d\n", ovl->x, ovl->y, ovl->width, ovl->height ); #endif state->modified = 1; buf += 7; break; case CMD_SPU_SET_PXD_OFFSET: /* image top[0] field / image bottom[1] field*/ state->field_offs[0] = (((u_int)buf[1]) << 8) | buf[2]; state->field_offs[1] = (((u_int)buf[3]) << 8) | buf[4]; #ifdef LOG_DEBUG printf ("spu: \toffset[0] = %d offset[1] = %d\n", state->field_offs[0], state->field_offs[1]); #endif if ((state->field_offs[0] >= seq->seq_len) || (state->field_offs[1] >= seq->seq_len)) { printf("libspudec:faulty stream\n"); seq->broken = 1; } state->modified = 1; buf += 5; break; case CMD_SPU_WIPE: #ifdef LOG_DEBUG printf ("libspudec: \tSPU_WIPE not implemented yet\n"); #endif IS_WIPE = 1; param_length = (buf[1] << 8) | (buf[2]); { /* added by rwlin@avamax.com */ //dvdspu_color_block_t *cb,*cbprev; dvdspu_vert_block_t *vb,*vbtail; spudec_clut_t *clut,*trans; int i,n; unsigned char *sp; /* append each verticle block */ for(vbtail=NULL,sp=buf+3;*(int *)sp!=0xFFFFFF0F;vbtail=vb) { vb=(dvdspu_vert_block_t *)malloc(sizeof(dvdspu_vert_block_t)); if(vb==NULL) break; vb->next=NULL; vb->y=(sp[0] & 0x3) << 8 | sp[1]; vb->height=(sp[2] & 0x3) << 8 | sp[3]; vb->height-=vb->y; vb->num_hori_blocks=n=sp[2] >> 4; vb->hori_blocks=(dvdspu_hori_block_t *)malloc(sizeof(dvdspu_hori_block_t)*n); sp+=4; for(i=0;ihori_blocks[i].x=(sp[0] & 0x3) << 8 | sp[1]; clut=(spudec_clut_t *)(sp+2); trans=(spudec_clut_t *)(sp+4); vb->hori_blocks[i].color[3] = state->clut[clut->entry0]; vb->hori_blocks[i].color[2] = state->clut[clut->entry1]; vb->hori_blocks[i].color[1] = state->clut[clut->entry2]; vb->hori_blocks[i].color[0] = state->clut[clut->entry3]; vb->hori_blocks[i].trans[3] = trans->entry0; vb->hori_blocks[i].trans[2] = trans->entry1; vb->hori_blocks[i].trans[1] = trans->entry2; vb->hori_blocks[i].trans[0] = trans->entry3; } if(vbtail==NULL) { cb=(dvdspu_color_block_t *)malloc(sizeof(dvdspu_color_block_t)); cb->time_stamp=state->delay; cb->displayed=0; cb->signature=0x879A; cb->next=NULL; cb->osd_cmd = OSD_CMD_COLORBLOCK; /* wiping show */ cb->vert_blocks=vb; if (TAIL==NULL) { //printf("reset global_color_blocks=%p\n", cb); global_color_blocks = cb; } else { TAIL->next = cb; } TAIL = cb; //printf("1 color block added cb->time_stamp=%d\n", state->delay); } else vbtail->next=vb; } //pthread_mutex_unlock(&ovl->update_lock); #if 0 if(ovl->rle) { printf("\trle= "); for(i=0;inum_rle;i++) { printf("(%d,%d)",ovl->rle[i].color,ovl->rle[i].len); } printf("\n"); } else printf("\tthere's no rles in ovl %x\n",ovl); #endif } //state->visible = OVERLAY_EVENT_SHOW; //state->modified = 1; // mark by kevin buf += 1 + param_length; break; case CMD_SPU_FORCE_DISPLAY: #ifdef LOG_DEBUG printf ("libspudec: \tForce Display/Menu\n"); #endif //printf("SPU FORCED DISPLAY delay=%d\n", state->delay); state->forced_display = 1; forced_display = 1; /* global variable in xine-engine/osd.c */ buf++; break; default: printf("libspudec: unknown seqence command (%02x)\n", buf[0]); /* FIXME: SPU should be dropped, and buffers resynced */ buf = next_seq; seq->broken = 1; break; } } if (next_seq >= seq->buf + seq->seq_len) seq->finished = 1; /* last sub-sequence */ state->cmd_ptr = next_seq; } /* FIXME: Get rid of all these static values */ static uint8_t *bit_ptr[2]; static int field; // which field we are currently decoding static int put_x, put_y; static u_int get_bits (u_int bits) { static u_int data; static u_int bits_left; u_int ret = 0; if (!bits) { /* for realignment to next byte */ bits_left = 0; } while (bits) { if (bits > bits_left) { ret |= data << (bits - bits_left); bits -= bits_left; data = *bit_ptr[field]++; bits_left = 8; } else { bits_left -= bits; ret |= data >> (bits_left); data &= (1 << bits_left) - 1; bits = 0; } } return ret; } static int spudec_next_line (vo_overlay_t *spu) { get_bits (0); // byte align rle data put_x = 0; put_y++; field ^= 1; // Toggle fields if (put_y >= spu->height) { #ifdef LOG_DEBUG printf ("spu: put_y >= spu->height\n"); #endif return -1; } return 0; } static void spudec_draw_picture (spudec_state_t *state, spudec_seq_t* seq, vo_overlay_t *ovl) { rle_elem_t *rle; field = 0; bit_ptr[0] = seq->buf + state->field_offs[0]; bit_ptr[1] = seq->buf + state->field_offs[1]; put_x = put_y = 0; get_bits (0); /* Reset/init bit code */ /* ovl->x = state->o_left; * ovl->y = state->o_top; * ovl->width = state->o_right - state->o_left + 1; * ovl->height = state->o_bottom - state->o_top + 1; * ovl->clip_top = 0; * ovl->clip_bottom = ovl->height - 1; * ovl->clip_left = 0; * ovl->clip_right = ovl->width - 1; */ ovl->data_size = seq->cmd_offs * 2 * sizeof(rle_elem_t); if (ovl->rle) { //printf ("libspudec: spudec_draw_picture: ovl->rle is not empty!!!! It should be!!! You should never see this message.\n"); free(ovl->rle); ovl->rle=NULL; } ovl->rle = malloc(ovl->data_size); state->modified = 0; /* mark as already processed */ rle = ovl->rle; #ifdef LOG_DEBUG printf ("libspudec: Draw RLE=%p,ovl=%x\n",rle,ovl); #endif while (bit_ptr[1] < seq->buf + seq->cmd_offs) { u_int len; u_int vlc; vlc = get_bits (4); if (vlc < 0x0004) { vlc = (vlc << 4) | get_bits (4); if (vlc < 0x0010) { vlc = (vlc << 4) | get_bits (4); if (vlc < 0x0040) { vlc = (vlc << 4) | get_bits (4); } } } len = vlc >> 2; /* if len == 0 -> end sequence - fill to end of line */ if (len == 0) len = ovl->width - put_x; /* debug code if(vlc>0) vlc++; */ rle->len = len; rle->color = vlc & 0x03; rle++; put_x += len; if (put_x >= ovl->width) { if (spudec_next_line (ovl) < 0) break; } } ovl->num_rle = rle - ovl->rle; ovl->rgb_clut = 0; #ifdef LOG_DEBUG printf ("spu: Num RLE=%d\n",ovl->num_rle); printf ("spu: Date size=%d\n",ovl->data_size); printf ("spu: sizeof RLE=%d\n",sizeof(rle_elem_t)); #endif //printf("ovl->rle=%p\n", ovl->rle); } /* Heuristic to discover the colors used by the subtitles and assign a "readable" pallete to them. Currently looks for sequence of border-fg-border or border1-border2-fg-border2-border1. MINFOUND is the number of ocurrences threshold. */ #define MINFOUND 20 static void spudec_discover_clut(spudec_state_t *state, vo_overlay_t *ovl) { int bg,c; int seqcolor[10]; int n,i; rle_elem_t *rle; int found[2][16]; static clut_t text_clut[] = { CLUT_Y_CR_CB_INIT(0x80, 0x90, 0x80), CLUT_Y_CR_CB_INIT(0x00, 0x90, 0x00), CLUT_Y_CR_CB_INIT(0xff, 0x90, 0x00) }; memset(found,0,sizeof(found)); rle = ovl->rle; /* this seems to be a problem somewhere else, why rle is null? */ if( !rle ) return; /* suppose the first and last pixels are bg */ if( rle[0].color != rle[ovl->num_rle-1].color ) return; bg = rle[0].color; i = 0; for( n = 0; n < ovl->num_rle; n++ ) { c = rle[n].color; if( c == bg ) { if( i == 3 && seqcolor[1] == seqcolor[3] ) { found[0][seqcolor[2]]++; if( found[0][seqcolor[2]] > MINFOUND ) { memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[1], sizeof(clut_t)); memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[2], sizeof(clut_t)); ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]]; ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]]; state->need_clut = 0; break; } } if( i == 5 && seqcolor[1] == seqcolor[5] && seqcolor[2] == seqcolor[4] ) { found[1][seqcolor[3]]++; if( found[1][seqcolor[3]] > MINFOUND ) { memcpy(&state->clut[state->cur_colors[seqcolor[1]]], &text_clut[0], sizeof(clut_t)); memcpy(&state->clut[state->cur_colors[seqcolor[2]]], &text_clut[1], sizeof(clut_t)); memcpy(&state->clut[state->cur_colors[seqcolor[3]]], &text_clut[2], sizeof(clut_t)); ovl->color[seqcolor[1]] = state->clut[state->cur_colors[seqcolor[1]]]; ovl->color[seqcolor[2]] = state->clut[state->cur_colors[seqcolor[2]]]; ovl->color[seqcolor[3]] = state->clut[state->cur_colors[seqcolor[3]]]; state->need_clut = 0; break; } } i = 0; seqcolor[i] = c; } else if ( i < 6 ) { i++; seqcolor[i] = c; } } } #ifdef LOG_DEBUG static void spudec_print_overlay( vo_overlay_t *ovl ) { printf ("spu: OVERLAY to show\n"); printf ("spu: \tx = %d y = %d width = %d height = %d\n", ovl->x, ovl->y, ovl->width, ovl->height ); printf ("spu: \tclut [%x %x %x %x]\n", ovl->color[0], ovl->color[1], ovl->color[2], ovl->color[3]); printf ("spu: \ttrans [%d %d %d %d]\n", ovl->trans[0], ovl->trans[1], ovl->trans[2], ovl->trans[3]); printf ("spu: \tclip top=%d bottom=%d left=%d right=%d\n", ovl->clip_top, ovl->clip_bottom, ovl->clip_left, ovl->clip_right); printf ("spu: \tclip_clut [%x %x %x %x]\n", ovl->clip_color[0], ovl->clip_color[1], ovl->clip_color[2], ovl->clip_color[3]); printf ("spu: \tclip_trans [%d %d %d %d]\n", ovl->clip_trans[0], ovl->clip_trans[1], ovl->clip_trans[2], ovl->clip_trans[3]); return; } #endif void spudec_copy_nav_to_overlay(pci_t* nav_pci, uint32_t* clut, int32_t button, int32_t mode, vo_overlay_t * overlay, vo_overlay_t * base ) { btni_t *button_ptr = NULL; unsigned int btns_per_group; int i; /* FIXME: Need to communicate with dvdnav vm to get/set "self->vm->state.HL_BTNN_REG" info. now done via button events from dvdnav. * * if ( this->pci.hli.hl_gi.fosl_btnn > 0) { * button = this->pci.hli.hl_gi.fosl_btnn ; * } */ if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) return; btns_per_group = 36 / nav_pci->hli.hl_gi.btngr_ns; /* choose button group: we can always use a normal 4:3 or widescreen button group * as long as xine blends the overlay before scaling the image to its aspect */ if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 1 && !(nav_pci->hli.hl_gi.btngr1_dsp_ty & 6)) button_ptr = &nav_pci->hli.btnit[0 * btns_per_group + button - 1]; if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 2 && !(nav_pci->hli.hl_gi.btngr2_dsp_ty & 6)) button_ptr = &nav_pci->hli.btnit[1 * btns_per_group + button - 1]; if (!button_ptr && nav_pci->hli.hl_gi.btngr_ns >= 3 && !(nav_pci->hli.hl_gi.btngr3_dsp_ty & 6)) button_ptr = &nav_pci->hli.btnit[2 * btns_per_group + button - 1]; if (!button_ptr) { printf("libspudec: No suitable menu button group found, using group 1.\n"); button_ptr = &nav_pci->hli.btnit[button - 1]; } /* button areas in the nav packet are in screen coordinates, * overlay clipping areas are in overlay coordinates; * therefore we must subtract the display coordinates of the underlying overlay */ overlay->clip_left = (button_ptr->x_start > base->x) ? (button_ptr->x_start - base->x) : 0; overlay->clip_top = (button_ptr->y_start > base->y) ? (button_ptr->y_start - base->y) : 0; overlay->clip_right = (button_ptr->x_end > base->x) ? (button_ptr->x_end - base->x) : 0; overlay->clip_bottom = (button_ptr->y_end > base->y) ? (button_ptr->y_end - base->y) : 0; if(button_ptr->btn_coln != 0) { #ifdef LOG_BUTTON fprintf(stderr, "libspudec: normal button clut\n"); #endif for (i = 0;i < 4; i++) { overlay->clip_color[i] = clut[0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (16 + 4*i))]; overlay->clip_trans[i] = 0xf & (nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode] >> (4*i)); } } else { #ifdef LOG_BUTTON fprintf(stderr, "libspudec: abnormal button clut\n"); #endif for (i = 0;i < 4; i++) { #ifdef LOG_BUTTON printf("libspudec:btn_coln = 0, clip_color = color\n"); #endif overlay->clip_color[i] = overlay->color[i]; overlay->clip_trans[i] = overlay->trans[i]; } } /* spudec_print_overlay( overlay ); */ #ifdef LOG_BUTTON printf("libspudec:xine_decoder.c:NAV to SPU pts match!\n"); #endif }