#include "bapp.h" #include "dvb_subtitle.h" #include "bos.h" #include "bos_task_priorities.h" #include "bgfx.h" #include "bsettop_decode.h" #include "genericlist.h" BDBG_MODULE(dsub); #define MAX_DSUB_EVENT 10 #define DSUB_PARSER_STACK_SIZE 1024 #define DSUB_DRAW_STACK_SIZE 1024 #define DSUB_PES_HDR_LEN 6 #define DSUB_WIDTH 720 #define DSUB_HEIGHT 576 #define DSUB_NUM_WNDS 20 #define DSUB_MAX_WIN_HEIGHT 50 typedef struct surf_node_t { LINKS_T links; bgfx_surf_t surf; } surf_node_t; /* page composition segment */ struct dsub_page_region { uint8_t region_id; /* region_id */ uint16_t horizontal; /* region_horizontal_address */ uint16_t vertical; /* region_vertical_address */ // struct dsub_page_region *next; }; struct dsub_region_object { uint16_t obj_id; /* object_id */ uint8_t obj_type; /* object_type */ uint8_t obj_provider_flag; /* object_provider_flag */ uint16_t obj_h_pos; /* object_horizontal_position */ uint16_t obj_v_pos; /* object_vertical_position */ uint8_t fore_pixel; /* foreground_pixel_code */ uint8_t back_pixel; /* background_pixel_code */ struct dsub_region_object *next; }; /* region composition segment */ struct dsub_region { uint8_t region_id; /* region_id */ uint8_t region_version; /* region_version_number */ uint16_t region_width; /* region_width */ uint16_t region_height; /* region_height */ uint8_t *region_buffer; surf_node_t *region_surf; uint8_t clut_id; /* CLUT_id */ struct dsub_region_object *region_objects; struct dsub_region *next; }; #define MAX_DSUB_CLUT_ENTRY 16 /* CLUT definition segment */ struct dsub_clut { uint8_t clut_id; /* CLUT_id */ uint8_t clut_size; /* entry size */ uint8_t clut_version; /* CLUT_version_number */ uint8_t clut_flag; /* bit 2: 2bit, bit 3: 4bit, bit 4: 8bit */ uint32_t entry[MAX_DSUB_CLUT_ENTRY]; struct dsub_clut *next; }; #define MAX_ACTIVE_REGIONS 10 struct dsub_page { uint8_t page_id; uint8_t timeout; uint8_t page_version; uint8_t page_size; uint8_t state; uint8_t region_cnt; struct dsub_page_region active_regions[MAX_ACTIVE_REGIONS]; struct dsub_region *regions; struct dsub_clut *cluts; struct dsub_page *next; }; enum { DSUB_EVT_PARSE, /* start parse */ DSUB_EVT_FREE, /* free allocated memory */ DSUB_EVT_CLEAR /* clear screen */ }; typedef struct dsub_evt_t { unsigned int cmd; unsigned int data[4]; } dsub_evt_t; struct bapp_dsub { bapp_t *p_app; b_task_t p_thread; /* parser thread */ b_task_t d_thread; uint32_t *p_thread_stack; /* parser thread stack */ uint32_t *d_thread_stack; bool enabled; /* application will enable/disable depending on application's state */ bool pes_started; /* indicate PUSI message is delivered */ uint16_t pes_len; /* length of complete pes */ uint16_t pes_received; /* length of accumulated pes packet */ uint32_t start_ptr; /* current packet's start position in buffer */ uint32_t write_ptr; /* current write position in buffer */ b_mutex_t mutex; b_mutex_t p_mutex; b_queue_t packet_queue; b_event_t p_event[MAX_DSUB_EVENT]; LIST_T free_surf_list; dsub_evt_t packet_event; bgfx_surf_t *p_osd_surf; /* OSD frame buffer which is from application */ bgfx_surf_t surf; /* 720x576 subtitle surface */ uint16_t page_no; /* page number to be selected by application */ uint32_t timeout_tick; struct dsub_page *pages; /* list of pages */ } bapp_dsub; bapp_dsub_t s_p_subtitle = NULL; static void dsub_p_parser_thread(void *data); static void dsub_p_draw_thread(void *data); static bool dsub_p_find_pes_start_code(uint8_t *buf); static void dsub_p_copy_pes(uint8_t *pes_hdr, int len); static void dsub_p_post_complete_event(void); /* * Open DVB subtitle object */ bapp_dsub_t bapp_dsub_open(void *v_app) { bapp_t *p_app = (bapp_t *)v_app; b_task_params params; surf_node_t *surf_node; int i; BDBG_ASSERT(p_app); // BDBG_SetModuleLevel("dsub",BDBG_eMsg); s_p_subtitle = (bapp_dsub_t)BKNI_Malloc(sizeof(struct bapp_dsub)); if (!s_p_subtitle) { BDBG_ERR(("fail to create dvb subtitle object")); goto done; } BKNI_Memset(s_p_subtitle, 0, sizeof(struct bapp_dsub)); s_p_subtitle->p_app = p_app; s_p_subtitle->p_thread_stack = BKNI_Malloc(DSUB_PARSER_STACK_SIZE*sizeof(unsigned int)); if (!s_p_subtitle->p_thread_stack) { BDBG_ERR(("fail to create parser thread stack")); goto err; } s_p_subtitle->d_thread_stack = BKNI_Malloc(DSUB_DRAW_STACK_SIZE*sizeof(unsigned int)); s_p_subtitle->page_no = 0xFFFF; s_p_subtitle->p_osd_surf = &p_app->surf; /* create subtitle surface, which is 720x576 PAL size, AYCbCr32 format */ if (bgfx_create(&s_p_subtitle->surf,DSUB_WIDTH,DSUB_HEIGHT,NULL,0,NULL,BGFX_SURF_BPP(32)|BGFX_SURF_GRC)!=0) { BDBG_ERR(("bgfx_create failed")); goto err; } /* clear the subtitle suface buffer */ bgfx_fill_rect(&s_p_subtitle->surf, 0, 0, DSUB_WIDTH, DSUB_HEIGHT, 0); if (bos_create_mutex(&s_p_subtitle->mutex)!=b_ok) goto err; if (bos_create_mutex(&s_p_subtitle->p_mutex)!= b_ok) { bos_delete_mutex(&s_p_subtitle->mutex) ; goto err; } if (bos_create_queue(&s_p_subtitle->packet_queue, s_p_subtitle->p_event, MAX_DSUB_EVENT)!=b_ok) { bos_delete_mutex(&s_p_subtitle->mutex); bos_delete_mutex(&s_p_subtitle->p_mutex) ; goto gerr; } s_p_subtitle->timeout_tick = 0xFFFFFFFF; /* create dvb subtitle parser thread */ params.name = "dsub_parser"; params.priority = DSUB_PARSER_PRIORITY; params.stack_size = DSUB_PARSER_STACK_SIZE; params.stack = s_p_subtitle->p_thread_stack; bos_start_task(&s_p_subtitle->p_thread, ¶ms, dsub_p_parser_thread, s_p_subtitle); params.name = "dsub_draw"; params.priority = DSUB_DRAW_PRIORITY; params.stack_size = DSUB_DRAW_STACK_SIZE; params.stack = s_p_subtitle->d_thread_stack; bos_start_task(&s_p_subtitle->d_thread, ¶ms, dsub_p_draw_thread, s_p_subtitle); /* create surface list */ initl(&s_p_subtitle->free_surf_list); for (i=0; isurf, DSUB_WIDTH, DSUB_MAX_WIN_HEIGHT,NULL,0,NULL,BGFX_SURF_BPP(32)|BGFX_SURF_GRC)!=0) { BDBG_WRN(("fail to creaet surface")); continue; } inslt(&(s_p_subtitle->free_surf_list), surf_node); } goto done; gerr: bgfx_destroy(&s_p_subtitle->surf); err: if (s_p_subtitle->p_thread_stack) BKNI_Free(s_p_subtitle->p_thread_stack); BKNI_Free(s_p_subtitle); s_p_subtitle = NULL; done: return s_p_subtitle; } /* * enable/disable dvb subtitle */ void bapp_dsub_enable(bapp_dsub_t p_dsub, bool enable, uint16_t page_no) { dsub_evt_t *p_event; BDBG_ASSERT(p_dsub); BDBG_ASSERT(s_p_subtitle); BDBG_ASSERT(p_dsub==s_p_subtitle); BDBG_WRN(("%s: enable = %s (%d)", __FUNCTION__, enable?"true":"false", page_no)); if (p_dsub->enabled == enable) { return; } p_dsub->enabled = enable; if (!enable) { p_dsub->pes_received = 0; p_dsub->pes_len = 0; p_dsub->pes_started = false; p_dsub->timeout_tick = 0xFFFFFFFF; /* let parser thread clear previously allocated page entries */ p_event = (dsub_evt_t *)BKNI_Malloc(sizeof(dsub_evt_t)); p_event->cmd = DSUB_EVT_FREE; bos_post_event(s_p_subtitle->packet_queue, (b_event_t *)p_event); } else { p_dsub->page_no = page_no; } } /* * it's called when message callback is invoked. * parser thread will start parsing when whole section is delived */ void bapp_dsub_process(uint8_t *buf, size_t size) { uint8_t *pes_hdr; uint8_t adaptation, adaptation_len = 0, pusi = 0; int len; /* sync byte */ if (buf[0] != 0x47) { BDBG_WRN(("%s: invalid packet. discard", __FUNCTION__)); return; } pusi = (buf[1]&0x40)?1:0; adaptation = (buf[3]>>4)&0x3; if (adaptation == 0x3) { adaptation_len = buf[4]+1; } else if (adaptation == 2) return; pes_hdr = (uint8_t *)(buf+4+adaptation_len); len = size - (pes_hdr-buf); if (pusi) { /* find start code */ if (!dsub_p_find_pes_start_code(pes_hdr)) { s_p_subtitle->pes_started = false; BDBG_WRN(("invalid start code : %x %x %x %x, %d", *pes_hdr, *(pes_hdr+1), *(pes_hdr+2), *(pes_hdr+3), adaptation_len)); return; } s_p_subtitle->pes_started = true; /* put the packet into parser buffer */ if (bos_acquire_mutex(&(s_p_subtitle->mutex), 50) != b_ok) { BDBG_WRN(("%s:%d failed to acquire mutex", __FUNCTION__, __LINE__)); return; } /* complete pes length */ s_p_subtitle->pes_len = ((pes_hdr[4]<<8)|(pes_hdr[5]&0xFF)) + DSUB_PES_HDR_LEN; /* 3 start code, 1 stream id 2 len */ s_p_subtitle->pes_received = len; s_p_subtitle->start_ptr = s_p_subtitle->write_ptr; dsub_p_copy_pes(pes_hdr, len); if (s_p_subtitle->pes_len <= s_p_subtitle->pes_received) { dsub_p_post_complete_event(); } bos_release_mutex(&(s_p_subtitle->mutex)); } else if (s_p_subtitle->pes_started) { /* put it into list */ if (bos_acquire_mutex(&(s_p_subtitle->mutex), 50) != b_ok) { BDBG_WRN(("%s:%d failed to acquire mutex", __FUNCTION__, __LINE__)); return; } s_p_subtitle->pes_received += len; dsub_p_copy_pes(pes_hdr,len); if (s_p_subtitle->pes_received >= s_p_subtitle->pes_len) { /* complete the data */ dsub_p_post_complete_event(); } bos_release_mutex(&(s_p_subtitle->mutex)); } else { //BDBG_MSG(("discard : %x %x %x %x %x %x %x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6])); } } /* ---------------------------------------------------------------- */ #define DSUB_BUF_SIZE (0xFFFF*2) #define PRIVATE_STREAM_1 0xBD #define DSUB_PCKT_LEN_MSB_POS 4 #define DSUB_PCKT_LEN_LSB_POS 5 #define DSUB_PCKT_HEADER_LENGTH 45 #define DSUB_PCKT_LINENUM_MASK 0x1F /* line number mask */ #define DSUB_PCKT_ODD_FIELD_FLAG 0x20 /* on line number byte */ #define DSUB_PCKT_EBU_DATA 0x03 /* EBU subtitle data */ #define DSUB_PCKT_FRAMING_CODE 0xe4 /* framing code */ #define DSUB_DATA_IDENTIFIER 0x20 #define DSUB_STREAM_ID 0x00 #define DSUB_SYNC_BYTE 0x0F #define DSUB_END_MARKER 0xFF #define DSUB_SEGMENT_PAGE 0x10 #define DSUB_SEGMENT_REGION 0x11 #define DSUB_SEGMENT_CLUT 0x12 #define DSUB_SEGMENT_OBJECT 0x13 #define DSUB_ENDOF_DISPLAY 0x80 typedef enum dsub_page_state { PAGE_ST_NORMAL, PAGE_ST_ACQUISITION, PAGE_ST_MODE_CHANGE, PAGE_ST_MAX } dsub_page_state; static uint8_t dsub_msg_buff[DSUB_BUF_SIZE]; /* find the start code */ static bool dsub_p_find_pes_start_code(uint8_t *buf) { if ((buf[0]==0x00) && (buf[1]==0x00) && (buf[2]==0x01) && (buf[3]==PRIVATE_STREAM_1)) return true; return false; } /* whole messages are received, start parsing */ static void dsub_p_post_complete_event(void) { dsub_evt_t *p_event = (dsub_evt_t *)BKNI_Malloc(sizeof(dsub_evt_t)); s_p_subtitle->pes_started = false; p_event->cmd = DSUB_EVT_PARSE; p_event->data[0] = s_p_subtitle->start_ptr; p_event->data[1] = s_p_subtitle->pes_len; bos_post_event(s_p_subtitle->packet_queue, (b_event_t *)p_event); } static void dsub_p_copy_pes(uint8_t *pes_hdr, int len) { int remain = s_p_subtitle->write_ptr + len - DSUB_BUF_SIZE; if (!len) return; if (remain>0) { BKNI_Memcpy(&dsub_msg_buff[s_p_subtitle->write_ptr], pes_hdr, len-remain); BKNI_Memcpy(&dsub_msg_buff[0], pes_hdr+(len-remain), remain); s_p_subtitle->write_ptr = remain; } else { BKNI_Memcpy(&dsub_msg_buff[s_p_subtitle->write_ptr], pes_hdr, len); s_p_subtitle->write_ptr += len; } if (s_p_subtitle->write_ptr == DSUB_BUF_SIZE) { s_p_subtitle->write_ptr = 0; } else if (s_p_subtitle->write_ptr > DSUB_BUF_SIZE) { BDBG_WRN(("!!! error in handling write_ptr")); } } /* --------------------------------------- * Parser/Draw * ---------------------------------------*/ static void dsub_p_process_pes(bapp_dsub_t p_dsub, uint8_t *ptr, int len); static void dsub_p_free_pages(bapp_dsub_t p_dsub); static void dsub_p_clear_active_regions(struct dsub_page *page); static void dsub_p_free_regions(struct dsub_page *page); static void dsub_p_free_cluts(struct dsub_page *page); static void dsub_p_clear_screen(bapp_dsub_t p_dsub); static void dsub_p_parser_thread(void *data) { dsub_evt_t *p_event; dsub_evt_t *discard_event; bapp_dsub_t p_dsub = (bapp_dsub_t)data; uint8_t *ptr; int start, len, remain; bool alloced = false; while (1) { p_event = (dsub_evt_t *)bos_pend_event(p_dsub->packet_queue, -1); alloced = false; if (p_event) { switch (p_event->cmd) { case DSUB_EVT_PARSE: if (!p_dsub->enabled) break; start = p_event->data[0]; len = p_event->data[1]; if (start+len>DSUB_BUF_SIZE) { remain = start+len-DSUB_BUF_SIZE; ptr = (uint8_t *)BKNI_Malloc(len); BKNI_Memcpy(ptr, &dsub_msg_buff[start], len-remain); BKNI_Memcpy(ptr+(len-remain), &dsub_msg_buff[0], remain); alloced = true; } else { ptr = &dsub_msg_buff[start]; } if (!dsub_p_find_pes_start_code(ptr)) { BDBG_WRN(("invalid packet is received in parser thread...")); break; } else { dsub_p_process_pes(p_dsub, ptr, len); } break; case DSUB_EVT_FREE: { BDBG_WRN(("clear cached dsub page and clear event queue")); dsub_p_free_pages(p_dsub); while (1) { /* discard the current messages */ discard_event = (dsub_evt_t *)bos_pend_event(p_dsub->packet_queue,0); if(discard_event) { BKNI_Free(discard_event); continue; } break; } break; } case DSUB_EVT_CLEAR: { BDBG_WRN(("page timeout expired.. clear the screen...")); /* reset timeout_tick */ p_dsub->timeout_tick = 0xFFFFFFFF; if (p_dsub->enabled) { dsub_p_clear_screen(p_dsub); } } default: break; } } else { BKNI_Sleep(5); } if (p_event) { BKNI_Free(p_event); } if (alloced) { BKNI_Free(ptr); } } } /* draw pages */ static int redraw_page(bapp_dsub_t p_dsub, struct dsub_page *page) { struct dsub_region *region; int i, obj_cnt; if (page->page_id != p_dsub->page_no) { BDBG_WRN(("not drawing this page (%d %d)", page->page_id, p_dsub->page_no)); return 0; } obj_cnt = 0; for (i=0; iregion_cnt; i++) { region = page->regions; /* find the region for this active region */ while (region) { if (page->active_regions[i].region_id == region->region_id) break; region = region->next; } if (region) { if (region->region_objects) { obj_cnt++; } bgfx_blit_rect(®ion->region_surf->surf, &s_p_subtitle->surf, 0, 0, page->active_regions[i].horizontal, page->active_regions[i].vertical, region->region_width, region->region_height); } } return obj_cnt; } /* ETSI EN300 743 v1.2.1, 10.4/5/6 */ static int table_2_to_4_bit[4] = {0x00, 0x07, 0x08, 0x0F}; static int table_2_to_8_bit[4] = {0x00, 0x77, 0x88, 0xFF}; static int table_4_to_8_bit[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; static uint32_t get_color(struct dsub_clut *clut, int col) { return clut->entry[col]; } /* process pixel-data sub-block */ static int dsub_p_process_pixel_object( struct dsub_region *region, /* region of this pixel data */ struct dsub_region_object *obj, /* object for this pixel data */ struct dsub_clut *clut, /* clut which is assigned to region */ uint16_t object_id, /* object id */ int *y, int *x, /* [in,out] x/y offset in this object */ uint8_t *ptr, /* pixel buffer */ int tot_len, /* maximum length which is allocated for this pixel object */ bool copy /* copy top field data into bottom field if btm_field_data_block_length is zero*/ ) { struct bit_state_t bs; uint8_t type; uint8_t code; int i, len, color, height = 1; // static uint32_t line[DSUB_WIDTH]; bs.data = (unsigned char *)ptr; bs.bindex = 0; type = get_bits(8, &bs); if (copy) { height = 2; } switch (type) { case 0x10: /* 2-bit pixel coding string */ while (s_p_subtitle->enabled && (bs.bindex/8region_surf->surf, obj->obj_h_pos+*x, obj->obj_v_pos+*y, len, height, get_color(clut, color)); *x += len; if ((len == 0) || ((obj->obj_h_pos+(*x)) > DSUB_WIDTH)) { break; } } /* bytealigned */ if (bs.bindex%8) { get_bits(8-bs.bindex%8, &bs); } break; case 0x11: /* 4-bit pixel coding string */ while (s_p_subtitle->enabled && ((bs.bindex/8)region_surf->surf, obj->obj_h_pos+(*x), obj->obj_v_pos+(*y), len, height, get_color(clut, color)); *x += len; if ((len == 0) || ((obj->obj_h_pos+(*x)) > DSUB_WIDTH)) { break; } } /* byte aligned */ if (bs.bindex%8) { get_bits(8-bs.bindex%8, &bs); } break; case 0x12: /* 8bit pixel coding string */ while (s_p_subtitle->enabled && (bs.bindex/8>4; /* 8->4 bit reduction */ bgfx_fill_rect(®ion->region_surf->surf, obj->obj_h_pos+(*x), obj->obj_v_pos+(*y), len, height, get_color(clut, color)); *x += len; if ((len == 0) || ((obj->obj_h_pos+(*x)) > DSUB_WIDTH)) { break; } } break; case 0x20: /* 2 to 4 bit map_table data */ BDBG_MSG(("2 to 4 bit map_table data")); for (i=0; i<4; i++) { /* 4 entry numbers of 4-bits each */ table_2_to_4_bit[i] = get_bits(4, &bs); } break; case 0x21: /* 2 to 8 bit map_table data */ BDBG_MSG(("2 to 8 bit map_table data")); for (i=0; i<4; i++) { table_2_to_8_bit[i] = get_bits(8, &bs); } break; case 0x22: /* 4 to 8 bit map_table data */ BDBG_MSG(("4 to 8 bit map_table data")); for (i=0; i<16; i++) { table_4_to_8_bit[i] = get_bits(8, &bs); } break; case 0xf0: /* end of object line code */ /* object is assumed to be interlaced, with a top field and bottom field EN 300 743, 7.2.4 */ (*y) += 2; *x = 0; return bs.bindex/8; default: BDBG_WRN(("%s: unhandled code %x", __FUNCTION__, code)); return -1; break; } return bs.bindex/8; } /* process string object */ static void dsub_p_process_string_object(struct dsub_page *page, uint8_t object_id, uint8_t *ptr) { uint8_t num_codes = *ptr; int i; ptr++; /* character_code */ for (i=0; ipage_version == version) { /* same version.. */ BDBG_MSG(("same page delivered..")); break; } BDBG_MSG(("PAGE [%d] 0x%x -> 0x%x [timeout:%d]", page->page_id, page->page_version, version, timeout)); if ((page->page_version == 0xF && version == 0) || ((version-page->page_version) == 1)) discont = false; else { discont = true; } page->timeout = timeout; /* timeout should start when it's displayed */ page->page_version = version; state = get_bits(2, &bs); get_bits(2, &bs); /* skip reserved */ page->state = state; /* acquisition point: page refresh, mode change: new page */ if ((state == PAGE_ST_ACQUISITION) || (state == PAGE_ST_MODE_CHANGE)) { BDBG_MSG((" st_acquisition/mode_change(%d) : delete all page region", state)); /* delete all page regions */ dsub_p_free_regions(page); dsub_p_free_cluts(page); discont = false; } /* page is new or updated -> clear active regions */ dsub_p_clear_active_regions(page); while ((bs.bindex/8)active_regions[page->region_cnt].region_id = get_bits(8, &bs); get_bits(8, &bs); /* skip reserved field */ page->active_regions[page->region_cnt].horizontal = get_bits(16, &bs); page->active_regions[page->region_cnt].vertical = get_bits(16, &bs); BDBG_MSG((" region:%x, hor:%d ver:%d", page->active_regions[page->region_cnt].region_id, page->active_regions[page->region_cnt].horizontal, page->active_regions[page->region_cnt].vertical)); page->region_cnt++; /* TODO: should we increase the active region size?? */ if (page->region_cnt >= MAX_ACTIVE_REGIONS) { BDBG_WRN(("active region is full")); break; } } break; } /* region composition segment */ case DSUB_SEGMENT_REGION: { uint8_t region_id, region_version, region_flag; struct dsub_region *region, **pregion; struct dsub_region_object *objects, **pobject, *cur_object; uint8_t region_level_of_compatibility, region_depth; uint8_t pixel_code_8, pixel_code_4, pixel_code_2; bool new = false; if (page->page_version == 0xFF) { BDBG_WRN(("[REGION] page composition segment is not delivered yet")); break; } region_id = get_bits(8, &bs); region_version = get_bits(4, &bs); region_flag = get_bits(1, &bs); get_bits(3, &bs); /* skip reserved field */ region = page->regions; pregion = &page->regions; while (region) { if (region->region_id == region_id) break; pregion = ®ion->next; region = region->next; } if (!region) { new = true; *pregion = region = (struct dsub_region *)BKNI_Malloc(sizeof(struct dsub_region)); if (!region) { BDBG_WRN(("fail to allocate memory for region")); break; } BKNI_Memset(region, 0, sizeof(struct dsub_region)); region->region_version = 0xFF; } if (region->region_version == region_version) { BDBG_MSG(("same object version.. ")); break; } /* delete all region objects */ objects = region->region_objects; while (objects) { struct dsub_region_object *n = objects->next; BKNI_Free(objects); objects = n ; } region->region_objects = NULL; region->region_id = region_id; region->region_version = region_version; region->region_width = get_bits(16, &bs); region->region_height = get_bits(16, &bs); if (new == true) { if (region->region_height>DSUB_MAX_WIN_HEIGHT) { BDBG_MSG((" ** create surface : %d", region->region_id)); region->region_surf = (surf_node_t *)BKNI_Malloc(sizeof(surf_node_t)); if (!region->region_surf && bgfx_create(®ion->region_surf->surf,region->region_width,region->region_height,NULL,0,NULL, BGFX_SURF_BPP(32)|BGFX_SURF_GRC) != 0) { BDBG_WRN(("Fail to create surface for region")); break; } } else { region->region_surf = remlh(&(s_p_subtitle->free_surf_list)); } if (!region->region_surf) { BDBG_WRN(("no surface available for the region (%d)", region_id)); break; } /* clear the region surface, only when it's delivered first time */ bgfx_fill_rect(®ion->region_surf->surf,0,0,region->region_width,region->region_height,0); } else if (discont == true) { /* TODO:: how to handle discontinuous page */ // BDBG_MSG(("clear region's frame buffer for discontinuous page")); // bgfx_fill_rect(®ion->region_surf->surf,0,0,region->region_width,region->region_height,0); } region_level_of_compatibility = get_bits(3, &bs); region_depth = get_bits(3, &bs); get_bits(2, &bs); /* skip reserved field */ region->clut_id = get_bits(8, &bs); BDBG_MSG(("REGION [%d] width:%d, height:%d clut_id:%d", region->region_id, region->region_width, region->region_height, region->clut_id)); pixel_code_8 = get_bits(8, &bs); pixel_code_4 = get_bits(4, &bs); pixel_code_2 = get_bits(2, &bs); get_bits(2, &bs); /* skip reserved field */ if (region_flag) { #if 0 if (region_depth == 1) BKNI_Memset(region->region_buffer, pixel_code_2, region->region_width*region->region_height); else if (region_depth == 2) BKNI_Memset(region->region_buffer, pixel_code_4, region->region_width*region->region_height); else if (region_depth == 3) BKNI_Memset(region->region_buffer, pixel_code_8, region->region_width*region->region_height); else BDBG_WRN(("region depth is not valid: %d", region_depth)); #endif } pobject = ®ion->region_objects; while (bs.bindex/8next; cur_object->obj_id = get_bits(16, &bs); cur_object->obj_type = get_bits(2, &bs); cur_object->obj_provider_flag = get_bits(2, &bs); cur_object->obj_h_pos = get_bits(12, &bs); get_bits(4, &bs); cur_object->obj_v_pos = get_bits(12, &bs); /* character / composite_object(string of characters */ if (cur_object->obj_type == 1 || cur_object->obj_type == 2) { cur_object->fore_pixel = get_bits(8, &bs); /* character's foreground color */ cur_object->back_pixel = get_bits(8, &bs); /* character's background color */ } BDBG_MSG((" object (%d), type(%d), h_pos(%d), v_pos(%d)", cur_object->obj_id, cur_object->obj_type, cur_object->obj_h_pos, cur_object->obj_v_pos)); } break; } case DSUB_SEGMENT_CLUT: { uint8_t clut_id, clut_version; struct dsub_clut *clut, **pclut; if (page->page_version == 0xFF) { BDBG_WRN(("[CLUT] page composition segment is not delivered yet")); break; } clut_id = get_bits(8, &bs); clut_version = get_bits(4, &bs); get_bits(4, &bs); clut = page->cluts; pclut = &page->cluts; while (clut) { if (clut->clut_id == clut_id) break; pclut = &clut->next; clut = clut->next; } if (!clut) { *pclut = clut = (struct dsub_clut *)BKNI_Malloc(sizeof(struct dsub_clut)); BKNI_Memset(clut, 0, sizeof(struct dsub_clut)); clut->clut_version = 0xFF; } else if (clut->clut_version == clut_version) { BDBG_MSG(("same clut is received")); break; } clut->clut_id = clut_id; BDBG_MSG(("CLUT[%d] %d->%d (%d)", clut->clut_id, clut->clut_version, clut_version, segment_length)); clut->clut_version = clut_version; while (bs.bindex/8 < segment_length) { uint8_t clut_entry_id, clut_flag, full_range_flag; uint8_t y, cb, cr, t; clut_entry_id = get_bits(8, &bs); clut_flag = get_bits(3, &bs); /* 2bit, 4bit, 8bit */ get_bits(4, &bs); full_range_flag = get_bits(1, &bs); if (full_range_flag) { y = get_bits(8, &bs); cr = get_bits(8, &bs); cb = get_bits(8, &bs); t = get_bits(8, &bs); } else { y = get_bits(6, &bs); cr = get_bits(4, &bs); cb = get_bits(4, &bs); t = get_bits(2, &bs); } if (!(clut_flag & 0x6)) { BDBG_WRN(("8bit clut... !!!!!! need increase clut_entry...")); return; } if (y == 0) { /* full transparency */ clut->entry[clut_entry_id] = 0x00108080; } else { clut->entry[clut_entry_id] = ((0xFF-t)<<24)|(y<<16)|(cb<<8)|(cr); } } break; } case DSUB_SEGMENT_OBJECT: { uint16_t object_id; uint8_t object_version, object_coding, non_modifying_color_flag ; struct dsub_region *region; struct dsub_clut *clut; struct dsub_region_object *robj; if (!page) break; object_id = get_bits(16, &bs); object_version = get_bits(4, &bs); object_coding = get_bits(2, &bs); /* TODO:: how to handle if non_modifying_color_flag is set */ non_modifying_color_flag = get_bits(1, &bs); if (non_modifying_color_flag) { BDBG_MSG(("non_modifying_color_flag is set")); } get_bits(1, &bs); /* find the region which is for this object */ region = page->regions; while (region) { robj = region->region_objects; while (robj) { if (robj->obj_id == object_id) break; robj = robj->next; } if (robj) break; region = region->next; } if (region && robj) { clut = page->cluts; while (clut) { if (clut->clut_id == region->clut_id) break; clut = clut->next; } } else { BDBG_WRN(("region entri is not delivered yet. discard the object")); break; } if (!clut) { BDBG_WRN(("clut entry is not delivered yet.. discard the object entry")); break; } if (object_coding == 0) { uint16_t top_field_length, btm_field_length; int x, y, len, pos; uint8_t *line_buf; top_field_length = get_bits(16, &bs); btm_field_length = get_bits(16, &bs); BDBG_MSG(("OBJ[%d] top_len:%d btm_len:%d", object_id, top_field_length, btm_field_length)); pos = 0; line_buf = &ptr[bs.bindex/8]; /* first pixel of the first line of the top field is the top left pixel of the object */ x = y = 0; /* if btm_field_data_block_length is 0, pixel-data_sub-block shall apply for the bottom field also EN 300 743, 7.2.4 */ while ( s_p_subtitle->enabled && (pos< top_field_length)) { len = dsub_p_process_pixel_object(region, robj, clut, object_id, &y, &x, line_buf, (top_field_length-pos), btm_field_length?false:true); if (len < 0) { break; } line_buf += len; pos += len; } bs.bindex += top_field_length*8; /* first pixel of the first line of the bottom field is the most left pixel on the second line of the object */ y = 1; x = 0; pos = 0; line_buf = &ptr[bs.bindex/8]; while (s_p_subtitle->enabled && (posenabled)); if (s_p_subtitle->enabled) { struct dsub_page *page; int flush = 0; uint32_t timeout ; page = s_p_subtitle->pages; bgfx_fill_rect(&s_p_subtitle->surf, 0, 0, s_p_subtitle->surf.surface.width, s_p_subtitle->surf.surface.height, 0); while (page) { if (page->page_id == s_p_subtitle->page_no) { flush += redraw_page(s_p_subtitle, page); timeout = page->timeout; } page = page->next; } bgfx_blit(&s_p_subtitle->surf, s_p_subtitle->p_osd_surf, 0, 0); if (flush>0) { s_p_subtitle->timeout_tick = bos_getticks() + MS_TO_TICKS(timeout*1000); /* setup timeout */ bapp_flush_screen(s_p_subtitle->p_app); } } break; default: BDBG_WRN(("undefined segment type")); break; } } static void dsub_p_clear_screen(bapp_dsub_t p_dsub) { bgfx_fill_rect(p_dsub->p_osd_surf, 0, 0, p_dsub->p_osd_surf->surface.width, p_dsub->p_osd_surf->surface.height, 0); bapp_flush_screen(p_dsub->p_app); } static void dsub_p_process_pes(bapp_dsub_t p_dsub, uint8_t *ptr, int len) { int cur_len; int pes_hdr_len; bool pts_present = false; uint8_t *p = ptr; uint8_t data_identifier, stream_id, sync_byte, segment_type; uint16_t page_id, segment_length, processed; struct dsub_page *page, **ppage; struct bit_state_t bs; uint32_t pts; BDBG_MSG((">> %s: start", __FUNCTION__)); cur_len = p[4]<<8 | p[5]; if ((cur_len + DSUB_PES_HDR_LEN) != len) { BDBG_WRN(("incomplete packet.. discard.. %d %d", cur_len, len)); return; } /* check presentation time */ p += DSUB_PES_HDR_LEN; bs.bindex = 0; bs.data = p; get_bits(8, &bs); if (get_bits(1, &bs)) { /* retrieve PTS info */ pts_present = true; } get_bits(7, &bs); pes_hdr_len = get_bits(8, &bs); if (pts_present) { uint8_t flag; flag = get_bits(4, &bs); /* skip: b0010 or b0011*/ if ((flag != 0x2) && (flag != 0x3)) { BDBG_WRN(("pts field is not correct")); pts_present = false; } /* use 32 bit MSB out of 33 bits */ pts = get_bits(3, &bs)<<29; if (get_bits(1, &bs) != 1) { BDBG_WRN(("invalid pts field ")); pts_present = false; } pts += get_bits(15, &bs)<<14; if (get_bits(1, &bs) != 1) { BDBG_WRN(("invalid pts field ")); pts_present = false; } pts += get_bits(15, &bs)>>1; if (get_bits(1, &bs) != 1) { BDBG_WRN(("invalid pts field ")); pts_present = false; } } p += 2/*extension*/+1/*header length field*/+pes_hdr_len; if (pts_present) { bdecode_status status; int32_t diff; bdecode_get_status(s_p_subtitle->p_app->decode, &status); diff = (pts-status.video_stc)/90; /* convert to ms, 90khz resolution */ if ((diff > 500) && (diff<10000)) { BDBG_MSG(("*** PTS:%x, STC:%x, diff:%d", pts, status.video_stc, diff)); BKNI_Sleep(diff); } } /* * PES_data_field() { * data_identifier:8 - should be 0x20 * subtitle_stream_id - subtitle stream in this PES packet, it should be 0 * while (nextbits() == '0000 1111') { * subtitling_segment * } * end_of_PES_data_field_marker : '1111 1111' */ bs.bindex = 0; bs.data = p; data_identifier = get_bits(8, &bs); if (data_identifier != DSUB_DATA_IDENTIFIER) { BDBG_WRN(("invalid data identifier (0x%x)", data_identifier)); return; } stream_id = get_bits(8, &bs); if (stream_id != DSUB_STREAM_ID) { BDBG_WRN(("invalid stream id (0x%x)", stream_id)); return; } processed = len - DSUB_PES_HDR_LEN - 3 - pes_hdr_len; while (s_p_subtitle->enabled) { if (bs.bindex/8>processed) break; sync_byte = get_bits(8, &bs); if (sync_byte != DSUB_SYNC_BYTE) break; /* here's subtitling_segment */ segment_type = get_bits(8, &bs); page_id = get_bits(16, &bs); segment_length = get_bits(16, &bs); /* TODO::check page_id with p_dsub->page_id ... */ page = p_dsub->pages; ppage = &(p_dsub->pages); while (page) { if (page->page_id == page_id) break; ppage = &page->next; page = page->next; } if (!page) { page = BKNI_Malloc(sizeof(struct dsub_page)); if (!page) { BDBG_WRN(("fail to alloc for page")); return; } BDBG_WRN(("page is allocated : 0x%x", page)); BKNI_Memset(page, 0, sizeof(struct dsub_page)); page->page_id = page_id; page->page_version = 0xFF; *ppage = page; } dsub_p_process_segment(segment_type, segment_length, &p[bs.bindex/8], page); bs.bindex += segment_length*8; } if (sync_byte != DSUB_END_MARKER) { BDBG_WRN(("end_of_PES_data_field_marker is invalid : 0x%x", sync_byte)); return; } BDBG_MSG(("<< %s: done", __FUNCTION__)); } static void dsub_p_clear_active_regions(struct dsub_page *page) { int i; for (i=0; iactive_regions[i], 0, sizeof(struct dsub_page_region)); } page->region_cnt = 0; } static void dsub_p_free_regions(struct dsub_page *page) { struct dsub_region *p_reg; struct dsub_region_object *p_robj; surf_node_t *p_surf; while (page->regions) { p_reg = page->regions->next; while (page->regions->region_objects) { p_robj = page->regions->region_objects->next; BKNI_Free(page->regions->region_objects); page->regions->region_objects = p_robj; } if (page->regions->region_height>DSUB_MAX_WIN_HEIGHT) { BDBG_MSG(("destroy surface (%d) height:%d", page->regions->region_id, page->regions->region_height)); bgfx_destroy(&page->regions->region_surf->surf); BKNI_Free(page->regions->region_surf); } else { p_surf = page->regions->region_surf; /* clear the surface first */ bgfx_fill_rect(&p_surf->surf, 0, 0, p_surf->surf.surface.width, p_surf->surf.surface.height, 0); inslt(&(s_p_subtitle->free_surf_list), p_surf); } BKNI_Free(page->regions); page->regions = p_reg; } } static void dsub_p_free_cluts(struct dsub_page *page) { struct dsub_clut *p_clut; while (page->cluts) { p_clut = page->cluts->next; BKNI_Free(page->cluts); page->cluts = p_clut; } } static void dsub_p_free_pages(bapp_dsub_t p_dsub) { struct dsub_page *page; /* free the page */ page = p_dsub->pages; while (page) { struct dsub_page *p = page->next; dsub_p_clear_active_regions(page); dsub_p_free_regions(page); dsub_p_free_cluts(page); BKNI_Free(page); page = p; } p_dsub->pages = NULL; } static void dsub_p_draw_thread(void *data) { bapp_dsub_t p_dsub = (bapp_dsub_t)data; uint32_t current_tick; dsub_evt_t *p_event; while (1) { current_tick = bos_getticks(); if (current_tick > p_dsub->timeout_tick) { /* erase screen */ p_event = (dsub_evt_t *)BKNI_Malloc(sizeof(dsub_evt_t)); p_event->cmd = DSUB_EVT_CLEAR; bos_post_event(s_p_subtitle->packet_queue, (b_event_t *)p_event); } /* reaction accuracy of -0/+5 s is accurate enough */ BKNI_Sleep(500); } }