diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libspudvb/Makefile.am | 9 | ||||
-rw-r--r-- | src/libspudvb/xine_decoder.c | 678 |
2 files changed, 687 insertions, 0 deletions
diff --git a/src/libspudvb/Makefile.am b/src/libspudvb/Makefile.am new file mode 100644 index 000000000..c7b4b804e --- /dev/null +++ b/src/libspudvb/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/misc/Makefile.common + +libdir = $(XINE_PLUGINDIR) + +lib_LTLIBRARIES = xineplug_decode_spudvb.la + +xineplug_decode_spudvb_la_SOURCES = xine_decoder.c +xineplug_decode_spudvb_la_LIBADD = $(XINE_LIB) +xineplug_decode_spudvb_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ diff --git a/src/libspudvb/xine_decoder.c b/src/libspudvb/xine_decoder.c new file mode 100644 index 000000000..781837c4a --- /dev/null +++ b/src/libspudvb/xine_decoder.c @@ -0,0 +1,678 @@ + +/* + * DVB Subtitle decoder (ETS 300 743) + * (c) 2004 Mike Lampard <mlampard@users.sourceforge.net> + * based on the application dvbsub by Dave Chapman + * + * TODO: + * - Implement support for teletext based subtitles + * - Use transmitted CLUT rather than default + */ + +#include "xine_internal.h" +#include <xine/osd.h> +#define MAX_REGIONS 5 + +typedef struct { + int x,y; + unsigned char is_visible; +} visible_region_t; + +typedef struct { + int page_time_out; + int page_version_number; + int page_state; + visible_region_t regions[MAX_REGIONS]; +} page_t; + +typedef struct { + int width,height; + int depth; + int win; + int CLUT_id; + int objects_start,objects_end; + unsigned int object_pos[65536]; + unsigned char img[720*576]; +} region_t; + +typedef struct { +/* dvbsub stuff */ + int x; + int y; + unsigned int curr_obj; + unsigned int curr_reg[64]; + + uint8_t *buf; + int i; + int nibble_flag; + int in_scanline; + page_t page; + region_t regions[MAX_REGIONS]; + unsigned int sub_segment_size; +} sub_image_t; + +typedef struct dvb_spu_class_s { + spu_decoder_class_t class; + xine_t *xine; +} dvb_spu_class_t; + +typedef struct dvb_spu_decoder_s { + spu_decoder_t spu_decoder; + + dvb_spu_class_t *class; + xine_stream_t *stream; + + xine_event_queue_t *event_queue; + + spu_dvb_descriptor_t *spu_descriptor; + + osd_object_t *osd; + + char *pes_pkt; + char *pes_pkt_wrptr; + unsigned int pes_pkt_size; + unsigned int pes_pkt_complete; + unsigned int pes_pkt_rec; + + uint64_t pts; + uint64_t vpts; + + sub_image_t *dvbsub; + int timeout; + int show; +} dvb_spu_decoder_t; + +void create_region(dvb_spu_decoder_t *this, int region_id,int region_width,int region_height,int region_depth); +void do_plot(dvb_spu_decoder_t *this, int r,int x, int y, unsigned char pixel); +void plot(dvb_spu_decoder_t *this, int r,int run_length, unsigned char pixel); +unsigned char next_nibble (dvb_spu_decoder_t *this); +void set_clut(dvb_spu_decoder_t *this,int CLUT_id,int CLUT_entry_id,int Y_value, int Cr_value, int Cb_value, int T_value); +void decode_4bit_pixel_code_string(dvb_spu_decoder_t *this, int r, int object_id, int ofs, int n); +void process_pixel_data_sub_block(dvb_spu_decoder_t *this, int r, int o, int ofs, int n); +void process_page_composition_segment(dvb_spu_decoder_t *this); +void process_region_composition_segment(dvb_spu_decoder_t *this); +void process_CLUT_definition_segment(dvb_spu_decoder_t *this); +void process_object_data_segment(dvb_spu_decoder_t *this); +void draw_subtitles(dvb_spu_decoder_t *this); + +void create_region(dvb_spu_decoder_t *this, int region_id,int region_width,int region_height,int region_depth) { + + sub_image_t *dvbsub = this->dvbsub; + + dvbsub->regions[region_id].win=1; + dvbsub->regions[region_id].width=region_width; + dvbsub->regions[region_id].height=region_height; + + memset(dvbsub->regions[region_id].img,15,sizeof(dvbsub->regions[region_id].img)); +} + + +void do_plot(dvb_spu_decoder_t *this, int r,int x, int y, unsigned char pixel) { + int i; + sub_image_t *dvbsub = this->dvbsub; + + if ( ( y >= 0) && (y < dvbsub->regions[r].height)) { + i = (y * dvbsub->regions[r].width) + x; + dvbsub->regions[r].img[i]=pixel; + } +} + +void plot(dvb_spu_decoder_t *this, int r,int run_length, unsigned char pixel) { + + sub_image_t *dvbsub = this->dvbsub; + + int x2 = dvbsub->x + run_length; + + while (dvbsub->x < x2) { + do_plot(this, r,dvbsub->x,dvbsub->y,pixel); + dvbsub->x++; + } +} + +unsigned char next_nibble (dvb_spu_decoder_t *this) { + unsigned char x; + sub_image_t *dvbsub = this->dvbsub; + + if (dvbsub->nibble_flag==0) { + x=(dvbsub->buf[dvbsub->i]&0xf0)>>4; + dvbsub->nibble_flag=1; + } else { + x=(dvbsub->buf[dvbsub->i++]&0x0f); + dvbsub->nibble_flag=0; + } + return(x); +} + +void decode_4bit_pixel_code_string(dvb_spu_decoder_t *this, int r, int object_id, int ofs, int n) { + int next_bits, + switch_1, + switch_2, + switch_3, + run_length, + pixel_code; + + sub_image_t *dvbsub = this->dvbsub; + + int bits; + unsigned int data; + int j; + + if (dvbsub->in_scanline==0) { + dvbsub->in_scanline=1; + } + dvbsub->nibble_flag=0; + j=dvbsub->i + n; + while(dvbsub->i < j) { + + bits=0; + pixel_code=0; + next_bits=next_nibble(this); + + if (next_bits!=0) { + pixel_code=next_bits; + plot(this, r,1,pixel_code); + bits+=4; + } else { + bits+=4; + data=next_nibble(this); + switch_1=(data&0x08)>>3; + bits++; + if (switch_1==0) { + run_length=(data&0x07); + bits+=3; + if (run_length!=0) { + plot(this, r,run_length+2,pixel_code); + } else { + break; + } + } else { + switch_2=(data&0x04)>>2; + bits++; + if (switch_2==0) { + run_length=(data&0x03); + bits+=2; + pixel_code=next_nibble(this); + bits+=4; + plot(this, r,run_length+4,pixel_code); + } else { + switch_3=(data&0x03); + bits+=2; + switch (switch_3) { + case 0: plot(this, r,1,pixel_code); + break; + case 1: plot(this, r,2,pixel_code); + break; + case 2: run_length=next_nibble(this); + bits+=4; + pixel_code=next_nibble(this); + bits+=4; + plot(this, r,run_length+9,pixel_code); + break; + case 3: run_length=next_nibble(this); + run_length=(run_length<<4)|next_nibble(this); + bits+=8; + pixel_code=next_nibble(this); + bits+=4; + plot(this, r,run_length+25,pixel_code); + } + } + } + } + + } + if (dvbsub->nibble_flag==1) { + dvbsub->i++; + dvbsub->nibble_flag=0; + } +} + + +void process_pixel_data_sub_block(dvb_spu_decoder_t *this, int r, int o, int ofs, int n) { + int data_type; + int j; + + sub_image_t *dvbsub = this->dvbsub; + + j = dvbsub->i + n; + + dvbsub->x=(dvbsub->regions[r].object_pos[o])>>16; + dvbsub->y=((dvbsub->regions[r].object_pos[o])&0xffff)+ofs; + while (dvbsub->i < j) { + data_type=dvbsub->buf[dvbsub->i++]; + + switch(data_type) { + case 0: dvbsub->i++; + case 0x11: decode_4bit_pixel_code_string(this, r,o,ofs,n-1); + break; + case 0xf0: dvbsub->in_scanline=0; + dvbsub->x=(dvbsub->regions[r].object_pos[o])>>16; + dvbsub->y+=2; + break; + default: fprintf(stderr,"unimplemented data_type %02x in pixel_data_sub_block\n",data_type); + } + } + + dvbsub->i=j; +} + +void process_page_composition_segment(dvb_spu_decoder_t *this) { + int page_id, + segment_length, + page_time_out, + page_version_number, + page_state; + int region_id,region_x,region_y; + int j; + int r; + sub_image_t *dvbsub = this->dvbsub; + + page_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + segment_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + + j=dvbsub->i+segment_length; + + page_time_out = dvbsub->buf[dvbsub->i++]; + this->timeout=page_time_out; + + page_version_number = (dvbsub->buf [dvbsub->i] & 0xf0)>>4; + page_state=(dvbsub->buf[dvbsub->i]&0x0c)>>2; + dvbsub->i++; + if ((page_state!=2) && (page_state!=1)) { + return; + } else { + if(page_state==1 && page_id==this->spu_descriptor->comp_page_id) + draw_subtitles(this); + } + + for (r=0;r<MAX_REGIONS;r++) { + dvbsub->page.regions[r].is_visible=0; + } + while (dvbsub->i<j) { + region_id=dvbsub->buf[dvbsub->i++]; + dvbsub->i++; /* reserved */ + region_x=(dvbsub->buf[dvbsub->i]<<8) | dvbsub->buf[dvbsub->i+1]; + dvbsub->i+=2; + region_y=(dvbsub->buf[dvbsub->i]<<8) | dvbsub->buf[dvbsub->i+1]; + dvbsub->i+=2; + + dvbsub->page.regions[region_id].x=region_x; + dvbsub->page.regions[region_id].y=region_y; + dvbsub->page.regions[region_id].is_visible=1; + + } + +} + + +void process_region_composition_segment(dvb_spu_decoder_t *this) { + int page_id, + segment_length, + region_id, + region_version_number, + region_fill_flag, + region_width, + region_height, + region_level_of_compatibility, + region_depth, + CLUT_id, + region_8_bit_pixel_code, + region_4_bit_pixel_code, + region_2_bit_pixel_code; + int object_id, + object_type, + object_provider_flag, + object_x, + object_y, + foreground_pixel_code, + background_pixel_code; + int j; + int o; + sub_image_t *dvbsub = this->dvbsub; + + page_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + segment_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + j=dvbsub->i+segment_length; + + region_id=dvbsub->buf[dvbsub->i++]; + region_version_number=(dvbsub->buf[dvbsub->i]&0xf0)>>4; + region_fill_flag=(dvbsub->buf[dvbsub->i]&0x08)>>3; + dvbsub->i++; + region_width=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + region_height=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + region_level_of_compatibility=(dvbsub->buf[dvbsub->i]&0xe0)>>5; + region_depth=(dvbsub->buf[dvbsub->i]&0x1c)>>2; + dvbsub->i++; + CLUT_id=dvbsub->buf[dvbsub->i++]; + region_8_bit_pixel_code=dvbsub->buf[dvbsub->i++]; + region_4_bit_pixel_code=(dvbsub->buf[dvbsub->i]&0xf0)>>4; + region_2_bit_pixel_code=(dvbsub->buf[dvbsub->i]&0x0c)>>2; + dvbsub->i++; + + if (dvbsub->regions[region_id].win < 0) { + /* If the region doesn't exist, then open it. */ + create_region(this, region_id,region_width,region_height,region_depth); + dvbsub->regions[region_id].CLUT_id=CLUT_id; + } + + dvbsub->regions[region_id].width=region_width; + dvbsub->regions[region_id].height=region_height; + + if (region_fill_flag==1) { + memset(dvbsub->regions[region_id].img,region_4_bit_pixel_code,sizeof(dvbsub->regions[region_id].img)); + } + + dvbsub->regions[region_id].objects_start=dvbsub->i; + dvbsub->regions[region_id].objects_end=j; + + for (o=0;o<65536;o++) { + dvbsub->regions[region_id].object_pos[o]=0xffffffff; + } + + while (dvbsub->i < j) { + object_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + object_type=(dvbsub->buf[dvbsub->i]&0xc0)>>6; + object_provider_flag=(dvbsub->buf[dvbsub->i]&0x30)>>4; + object_x=((dvbsub->buf[dvbsub->i]&0x0f)<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + object_y=((dvbsub->buf[dvbsub->i]&0x0f)<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + + dvbsub->regions[region_id].object_pos[object_id]=(object_x<<16)|object_y; + + if ((object_type==0x01) || (object_type==0x02)) { + foreground_pixel_code=dvbsub->buf[dvbsub->i++]; + background_pixel_code=dvbsub->buf[dvbsub->i++]; + } + } +} + +void process_object_data_segment(dvb_spu_decoder_t *this) { + int page_id, + segment_length, + object_id, + object_version_number, + object_coding_method, + non_modifying_colour_flag; + + int top_field_data_block_length, + bottom_field_data_block_length; + + sub_image_t *dvbsub = this->dvbsub; + + int j; + int old_i; + int r; + + page_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + segment_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + j=dvbsub->i+segment_length; + + object_id=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + dvbsub->curr_obj=object_id; + object_version_number=(dvbsub->buf[dvbsub->i]&0xf0)>>4; + object_coding_method=(dvbsub->buf[dvbsub->i]&0x0c)>>2; + non_modifying_colour_flag=(dvbsub->buf[dvbsub->i]&0x02)>>1; + dvbsub->i++; + + old_i=dvbsub->i; + for (r=0;r<MAX_REGIONS;r++) { + /* If this object is in this region... */ + if (dvbsub->regions[r].win >= 0) { + if (dvbsub->regions[r].object_pos[object_id]!=0xffffffff) { + dvbsub->i=old_i; + if (object_coding_method==0) { + top_field_data_block_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + bottom_field_data_block_length=(dvbsub->buf[dvbsub->i]<<8)|dvbsub->buf[dvbsub->i+1]; dvbsub->i+=2; + + process_pixel_data_sub_block(this, r,object_id,0,top_field_data_block_length); + + process_pixel_data_sub_block(this, r,object_id,1,bottom_field_data_block_length); + } + } + } + } +} + +void draw_subtitles(dvb_spu_decoder_t *this) { + int r; + int x,y,out_y; + int v; + int count; + + /* update clipping area */ + this->osd->x1 = 1; + this->osd->x2 = 720; + this->osd->y1 = 1; + this->osd->y2 = 576; + /* clear it */ + memset(this->osd->area,0,720*576); + + out_y=0; + count=0; + for (r=0;r<MAX_REGIONS;r++) { + if (this->dvbsub->regions[r].win >= 0) { + if (this->dvbsub->page.regions[r].is_visible) { + count++; + out_y=this->dvbsub->page.regions[r].y*720; + + for (y=0;y<this->dvbsub->regions[r].height;y++) { + for (x=0;x<this->dvbsub->regions[r].width;x++) { + v=this->dvbsub->regions[r].img[(y*this->dvbsub->regions[r].width)+x]; + this->osd->area[out_y+x+this->dvbsub->page.regions[r].x]=v*this->dvbsub->regions[r].CLUT_id; + } + out_y+=720; + } + } + } + } + /* display immediately */ + this->stream->osd_renderer->show (this->osd, 0); +} + + +static void spudec_decode_data (spu_decoder_t *this_gen, buf_element_t *buf) { + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + + if(buf->decoder_flags & BUF_FLAG_SPECIAL){ + if(buf->decoder_info[1]==BUF_SPECIAL_SPU_DVB_DESCRIPTOR) { + if(buf->decoder_info[2]==0) { + this->stream->osd_renderer->clear(this->osd); + this->stream->osd_renderer->hide(this->osd,0); + this->show=0; + } else { + if(this->spu_descriptor) + free(this->spu_descriptor); + this->show=1; + this->spu_descriptor = malloc(buf->decoder_info[2]); + memcpy(this->spu_descriptor,buf->decoder_info_ptr[2],buf->decoder_info[2]); + } + } + + } else { + if(buf->type & 0xffff){ + if(this->pes_pkt) + free(this->pes_pkt); + this->pes_pkt = malloc (buf->type & 0xffff); + memset(this->pes_pkt,0xff,buf->type & 0xffff); + + this->pes_pkt_wrptr = this->pes_pkt; + this->pes_pkt_size = (buf->type & 0xffff); + this->pts = buf->pts; + + memcpy(this->pes_pkt, buf->content, buf->size); + this->pes_pkt_wrptr+=buf->size; + this->pes_pkt_rec=1; + + }else{ + if(this->pes_pkt){ + memcpy(this->pes_pkt_wrptr, buf->content, buf->size); + this->pes_pkt_wrptr+=buf->size; + } + } + } + /* inform metronom we've received the package */ + if (buf->pts) { + metronom_t *metronom = this->stream->metronom; + this->vpts = metronom->got_spu_packet(metronom, buf->pts); + } + + /* process the pes section */ + if(this->show) + if(((this->pes_pkt_wrptr - this->pes_pkt) == this->pes_pkt_size) && this->pes_pkt_rec) + { + char *buf=this->pes_pkt; + int page_id; + int new_i; + int data_identifier,subtitle_stream_id; + + int segment_length, segment_type; + + int PES_header_data_length; + + int PES_packet_length=this->pes_pkt_size; + + this->dvbsub->buf = this->pes_pkt; + + PES_header_data_length=0; + this->dvbsub->i=0; + + data_identifier = buf[this->dvbsub->i++]; + subtitle_stream_id = buf[this->dvbsub->i++]; + + while(this->dvbsub->i <= (PES_packet_length)) { + /* SUBTITLING SEGMENT */ + this->dvbsub->i++; + segment_type=buf[this->dvbsub->i++]; + + page_id=(buf[this->dvbsub->i]<<8)|buf[this->dvbsub->i+1]; + segment_length=(buf[this->dvbsub->i+2]<<8)|buf[this->dvbsub->i+3]; + new_i=this->dvbsub->i+segment_length+4; + + /* SEGMENT_DATA_FIELD */ + switch(segment_type & 0xff) { + case 0x10: process_page_composition_segment(this); + break; + case 0x11: process_region_composition_segment(this); + break; + case 0x12: /* process_CLUT_definition_segment(this); */ + break; + case 0x13: process_object_data_segment(this); + break; + case 0x80: /* we have enough data to decode */ + break; + default: + return; + } + + this->dvbsub->i=new_i; + } + + } + return; +} + +static void spudec_reset (spu_decoder_t *this_gen) { + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + + if(this->osd) + this->stream->osd_renderer->hide(this->osd,0); + +} + +static void spudec_discontinuity (spu_decoder_t *this_gen) { + /* do nothing */ +} + +static void spudec_dispose (spu_decoder_t *this_gen) { + dvb_spu_decoder_t *this = (dvb_spu_decoder_t *) this_gen; + + if (this->osd) { + this->stream->osd_renderer->free_object (this->osd); + this->osd = NULL; + } + + if(this->pes_pkt) + free(this->pes_pkt); + + if(this->dvbsub) + free(this->dvbsub); + + free(this); +} + +static spu_decoder_t *dvb_spu_class_open_plugin (spu_decoder_class_t *class_gen, xine_stream_t *stream) { + + int i; + dvb_spu_decoder_t *this ; + dvb_spu_class_t *class = (dvb_spu_class_t *)class_gen; + + this = (dvb_spu_decoder_t *) xine_xmalloc (sizeof (dvb_spu_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 = NULL; + this->spu_decoder.set_button = NULL; + + this->class = class; + this->stream = stream; + + this->event_queue = NULL; + + this->dvbsub=malloc(sizeof(sub_image_t)); + + for (i=0;i<MAX_REGIONS;i++) { + this->dvbsub->page.regions[i].is_visible=0; + this->dvbsub->regions[i].win=-1; + } + + this->osd = this->stream->osd_renderer->new_object(this->stream->osd_renderer, 720, 600); + this->stream->osd_renderer->set_position(this->osd, 1, 1); + this->stream->osd_renderer->set_font(this->osd, "cetus", 26); + this->stream->osd_renderer->set_encoding(this->osd, NULL); + this->stream->osd_renderer->set_text_palette(this->osd, TEXTPALETTE_WHITE_BLACK_TRANSPARENT, OSD_TEXT1); + + return (spu_decoder_t *) this; +} + +static void dvb_spu_class_dispose (spu_decoder_class_t *this) { + free (this); +} + +static char *dvb_spu_class_get_identifier (spu_decoder_class_t *this) { + return "spudvb"; +} + +static char *dvb_spu_class_get_description (spu_decoder_class_t *this) { + return "DVB subtitle decoder plugin"; +} + +static void *init_spu_decoder_plugin (xine_t *xine, void *data) { + + dvb_spu_class_t *this; + this = (dvb_spu_class_t *) xine_xmalloc (sizeof (dvb_spu_class_t)); + + this->class.open_plugin = dvb_spu_class_open_plugin; + this->class.get_identifier = dvb_spu_class_get_identifier; + this->class.get_description = dvb_spu_class_get_description; + this->class.dispose = dvb_spu_class_dispose; + + this->xine = xine; + + return &this->class; +} + + +/* plugin catalog information */ +static uint32_t supported_types[] = { BUF_SPU_DVB, 0 }; + +static decoder_info_t spudec_info = { + supported_types, /* supported types */ + 1 /* priority */ +}; + +plugin_info_t xine_plugin_info[] = { +/* type, API, "name", version, special_info, init_function */ + { PLUGIN_SPU_DECODER, 16, "spudvb", XINE_VERSION_CODE, &spudec_info, &init_spu_decoder_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; + |