/*************************************************************************** * Copyright (c) 2009, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "scte_27.h" #include "bstd.h" #include "getbits.h" #include "bapp.h" #include "gist.h" #include "bxpt_pcr.h" #include "bapp_task_priorities.h" #include "bapp_palette.h" #include "bgfx_types.h" BDBG_MODULE(scte_27); /* function prototype */ void scte_27_handler(void *data); static bool scte_27_render_subtitle(pscte_27_handle pscte_27, scte_27_data *imme_pdata); //#define SUBTITLE_DRAW_TEST 1 #define SCTE_27_EVENT_TIME 25 /* 10 ms */ #define SCTE_27_MUTEX_TIME 100 #define BITMAP_BUFFER_SIZE 25920 /* (720 * 576 / 8 / 2) half of screen should be enough */ static scte_27_handle s_scte_27; static subtitle_message scte_27_msg[MAX_SUBTITLE_MESSAGE]; static scte_27_data scte_27_data_array[MAX_SUBTITLE_MESSAGE]; #define CLUT_MAX_ENTRIES 16 #define CLUT_BG_INDEX 12 #define CLUT_FG_INDEX 13 #define CLUT_OUTLINE_INDEX 14 #define CLUT_FRAME_INDEX 15 #define abs(n) __builtin_abs ((n)) static uint32_t clut_color_entries[CLUT_MAX_ENTRIES] = { 0 }; /* display standard */ static uint8_t display_standard_array[4] = { 30, /* DS_720_480_30 */ 25, /* DS_720_576_25 */ 60, /* DS_1280_720_60 */ 60 /* DS_1920_1080_60 */ }; #ifndef SCTE_ASSERT #define SCTE_ASSERT(expr) { \ if (!expr) \ printf("%s failed at %s:%d\n",#expr, __FILE__, __LINE__); \ } #endif #define insert_list(list, data) \ flags = bos_enter_critical(); \ inslt(&list, data); \ bos_exit_critical(flags) #define insert_list_head(list, data)\ flags = bos_enter_critical(); \ inslh(&list, data); \ bos_exit_critical(flags) #define remove_list(list) \ flags = bos_enter_critical(); \ pdata = remlh(&list); \ bos_exit_critical(flags) /* * compare given PTS against currrent stc clock * * Parameters: * pscte_27 pointer to scte_27 handle * Tp pts for in-cue * * Returns: * 0 if (Tp = Tc) * < 0 past * > 0 future */ static int scte_27_pts_stc_diff(pscte_27_handle pscte_27, uint32_t Tp) { BXPT_PCR_Handle hPcr; uint32_t Tc = 0, lo; hPcr = GetDPCR(); if (NULL == hPcr) { return 0; } BXPT_PCR_GetStc(hPcr, &Tc, &lo); Tc += PCRS_PER_MSEC * 800; if (Tp == Tc) return 0; /* 4.11 */ if (Tp < Tc) Tp = (uint32_t)Tp + 0xffffffff; /* 0x100000000 */ if ((Tp - Tc) >= 0x80000000) { return -1; /* past */ } else { if (Tp >= Tc) { if ((Tp - Tc) <= (PCRS_PER_MSEC * (PCRS_PER_MSEC + 5))) return 0; } return 1; /* future */ } } /* * This function is used to reset given list (which should be discard) * * Parameters: * pscte_27 pointer to scte_27 handle * which which list. * * Returns: * 1 if success * 0 otherwise */ static bool scte_27_reset_list(pscte_27_handle pscte_27, int which) { unsigned int flags; scte_27_data *pdata = NULL; BDBG_MSG(("%s: enter", __func__)); #ifdef DEBUG if (NULL == pscte_27) { BDBG_WRN(("%s: null pointer", __func__)); return false; } #endif if (SC_RENDER == which) { while (pscte_27->render_list.count > 0) { remove_list(pscte_27->render_list); if (pdata) { if (pdata->msg->bitmap_data) { free(pdata->msg->bitmap_data); pdata->msg->bitmap_data = NULL; pdata->msg->bitmap_size = 0; } /* put it back to free list */ insert_list(pscte_27->free_list, pdata); } } } if (SC_CLEAR == which) { while (pscte_27->clear_list.count > 0) { remove_list(pscte_27->clear_list); if (pdata) { /* put it back to free list */ insert_list(pscte_27->free_list, pdata); } } } BDBG_MSG(("%s: leave", __func__)); return true; } /* * This function is used to reset scte 27. * * Parameters: * pscte_27 pointer to scte_27 handle * * Returns: * 1 if success * 0 otherwise */ bool scte_27_reset(pscte_27_handle pscte_27) { BDBG_MSG(("%s: enter", __func__)); #ifdef DEBUG if (NULL == pscte_27) { BDBG_WRN(("%s: null pointer", __func__)); return false; } #endif scte_27_reset_list(pscte_27, SC_RENDER); scte_27_reset_list(pscte_27, SC_CLEAR); /* free segmented data buffer if any */ if (pscte_27->segment_data) { free(pscte_27->segment_data); pscte_27->segment_data = NULL; pscte_27->segment_data_size = 0; } pscte_27->pdata = NULL; pscte_27->segmented = false; BDBG_MSG(("%s: leave", __func__)); return true; } /* * This function is used to initialize scte 27 object * * Parameters: * v_app pointer to application structure * * Returns: * pointer to scte_27_handle if success * NULL otherwise */ pscte_27_handle scte_27_init(void *v_app) { pscte_27_handle pscte_27 = &s_scte_27; unsigned int *palette, palette_size; /* palette pointer */ b_task_params params; int i; scte_27_data *pdata = NULL; BDBG_MSG(("%s: enter", __func__)); #ifdef DEBUG if (!p_app) { BDBG_ERR(("%s: null ptr", __func__)); return NULL; } #endif memset(pscte_27, 0, sizeof(scte_27_handle)); /* keep application pointer to later use */ pscte_27->v_app = v_app; SCTE_ASSERT(b_ok == bos_create_mutex(&pscte_27->mutex)); pscte_27->bitmap = malloc(BITMAP_BUFFER_SIZE); SCTE_ASSERT(pscte_27->bitmap); SCTE_ASSERT(b_ok == bos_create_queue(&pscte_27->queue, pscte_27->event, MAX_SUBTITLE_EVENTS)); bapp_palette_get(ePALETTE_APP,(unsigned int **)&palette, &palette_size); /* last 4 in CLUT entried are used for subtitle, but we just set the values for all entries */ for (i = 0; i <= CLUT_FRAME_INDEX; i++) { clut_color_entries[i] = *(palette + i); } /* initialize all the list */ initl(&pscte_27->free_list); initl(&pscte_27->render_list); initl(&pscte_27->clear_list); /* free list */ for (i = 0; i < MAX_SUBTITLE_MESSAGE; i++) { pdata = &scte_27_data_array[i]; pdata->msg = &scte_27_msg[i]; inslt(&pscte_27->free_list, pdata); } scte_27_reset(pscte_27); pscte_27->cmd_array[SC_RENDER] = SC_RENDER; pscte_27->cmd_array[SC_CLEAR] = SC_CLEAR; params.priority = SCTE_27_PRIORITY; params.stack = pscte_27->stack; params.name = "scte_27_task"; params.stack_size = SCTE_27_STK_SIZE; /* enable scte 27 task */ bos_start_task(&pscte_27->task, ¶ms, scte_27_handler, pscte_27); BDBG_MSG(("%s: leave", __func__)); return pscte_27; } /* * This function is used to de-initialize scte 27 object initialized before * * Parameters: * pointer to scte 27 handle * * Returns: * NONE */ void scte_27_deinit(pscte_27_handle pscte_27) { BDBG_MSG(("%s: enter", __func__)); if (!pscte_27) { BDBG_MSG(("%s: null pointer", __func__)); return; } if (pscte_27->bitmap) free(pscte_27->bitmap); scte_27_reset(pscte_27); if (&pscte_27->mutex) bos_delete_mutex(&pscte_27->mutex); if (pscte_27->stack) free(pscte_27->stack); //free(pscte_27); BDBG_MSG(("%s: leave", __func__)); } /* * enable/disable SCTE 27 subtitle * * Parameters: * pscte_27 pointer to scte_27 handle * enable enable/disable SCTE 27 module * * Returns: * NONE */ void scte_27_enable(pscte_27_handle pscte_27, int enable) { if (bos_acquire_mutex(&pscte_27->mutex, 0) != b_ok) { BDBG_ERR(("%s bos_acquire_mutex failed (enable = %d)\n",__func__,enable)); return; } if (pscte_27->enabled == enable) { bos_release_mutex(&pscte_27->mutex); return; } pscte_27->enabled = enable; bos_release_mutex(&pscte_27->mutex); } /* * SCTE 27 subtitle message parser * * Parameters: * pscte_27 pointer to scte_27 handle * data pointer to scte_27 message data buffer * length data bufer length * hdr pointer to header that will hold the message header parsed * * Returns: * true if parse the data correctly * false if anything is wrong */ bool scte_27_parse_subtitle(pscte_27_handle pscte_27, unsigned char *data, int length, scte_27_data *pdata) { struct bit_state_t bits, *bs; subtitle_message *hdr; uint32_t flags; BXPT_PCR_Handle hPcr = NULL; uint32_t hi, lo; bs = &bits; bits.data = data; bits.bindex = 0; #ifdef DEBUG if (!pscte_27 || !data || (length < 10) || !pdata) { BDBG_WRN(("%s: null ptr or zero data", __func__)); return false; } #endif hdr = pdata->msg; hdr->table_ID = get_bits(8, bs); if (SUBTITLE_SECTION_ID != hdr->table_ID) { BDBG_WRN(("%s: invalid subtitle message", __func__)); return false; } get_bits(4, bs); /* skip zero and ISO reserved bits */ hdr->section_length = get_bits(12, bs); if (hdr->section_length > (length - 3)) { BDBG_WRN(("%s: section_length out of range", __func__)); return false; } get_bits(1, bs); hdr->segmentation_overlay_included = get_bits(1, bs); /* protocol version */ if (0 != get_bits(6, bs)) { BDBG_WRN(("%s: protocol version not zero", __func__)); return false; } if (hdr->segmentation_overlay_included) { hdr->table_extension = get_bits(16, bs); hdr->last_segment_number = get_bits(12, bs); hdr->segment_number = get_bits(12, bs); /* following packets of same table extension */ if (pscte_27->segmented) { if ((hdr->table_extension != pscte_27->table_extension) || ((pscte_27->segment_number + 1) != hdr->segment_number) || (hdr->segment_number > pscte_27->last_segment_number) || (pscte_27->segment_data_size != (hdr->section_length - 10))) { BDBG_WRN(("%s: segmented data not match. table=(0x%04x,0x%04x),sn=(%d,%d),lsn=(%d,%d),slen=(%d,%d)", __func__, pscte_27->table_extension,pscte_27->table_extension, hdr->segment_number, pscte_27->segment_number, pscte_27->last_segment_number, hdr->last_segment_number, pscte_27->segment_data_size, hdr->section_length - 10)); goto EXIT_PARSER; } bs->data = bs->data + (bs->bindex >> 3); /* should be data byte 9 */ memcpy(pscte_27->segment_data + (pscte_27->segment_data_size * hdr->segment_number), bs->data, pscte_27->segment_data_size); /* if last segment */ if (hdr->segment_number == pscte_27->last_segment_number) { BDBG_WRN(("%s: packets assembled and ready to render!", __func__)); pscte_27->segmented = false; pscte_27->pdata = NULL; /* ready to process subtitle body assembled */ bits.data = pscte_27->segment_data; bits.bindex = 0; } else { pscte_27->segment_number = hdr->segment_number; return true; } } else { int size; /* validate for first of segment packet */ if (0 != hdr->segment_number || (hdr->last_segment_number < 1)) { BDBG_WRN(("%s: not a valid segment packet. Discard!", __func__)); return false; } /* all coming message will have same size of body */ pscte_27->segment_data_size = (hdr->section_length - 10); /* allow buffer to hold all message body */ size = pscte_27->segment_data_size * (hdr->last_segment_number + 1); pscte_27->segment_data = malloc(size); if (!pscte_27->segment_data) { BDBG_WRN(("%s: out of memory. Discard all %d segmented packets", __func__, hdr->last_segment_number + 1)); goto EXIT_PARSER; } bs->data = bs->data + (bs->bindex >> 3); /* should be data byte 9 */ memcpy(pscte_27->segment_data, bs->data, pscte_27->segment_data_size); /* remember current segment for validating coming packets of same table extension */ pscte_27->table_extension = hdr->table_extension; pscte_27->last_segment_number = hdr->last_segment_number; pscte_27->segment_number = hdr->segment_number; pscte_27->segmented = true; /* need to reuse same data structure for coming packets of same table extension */ pscte_27->pdata = pdata; return true; } } /* should we check language match here? */ hdr->ISO_639_language_code = get_bits_aligned(24, bs); /* display modes */ hdr->pre_clear_display = get_bits(1, bs); hdr->immediate = get_bits(1, bs); #ifdef SUBTITLE_DRAW_TEST hdr->immediate = true; #endif get_bits(1, bs); /* skip reserved */ /* display stardards */ hdr->display_standard = get_bits(5, bs); if (hdr->display_standard >= 2) { BDBG_WRN(("%s: don't support given display format %d", __func__, hdr->display_standard)); goto EXIT_PARSER; } /* in-cue time */ hdr->display_in_PTS = get_bits_aligned(32, bs); hdr->display_in_PTS = (hdr->display_in_PTS >> 1); /* convert to 45KHz clock */ hdr->subtitle_type = get_bits(4, bs); if (SUBTITLE_TYPE_SIMPLE_BITMAP != hdr->subtitle_type) { BDBG_WRN(("%s: bitmap type %d is not simple bitmap", __func__, hdr->subtitle_type)); goto EXIT_PARSER; } get_bits(1, bs); /* skip reserved */ hdr->display_duration = get_bits(11, bs); hdr->block_length = get_bits(16, bs); /* simple bitmap, continue parse */ get_bits(5, bs); hdr->background_style = get_bits(1, bs); hdr->outline_style = get_bits(2, bs); hdr->Y_component = get_bits(5, bs); hdr->opaque_enable = get_bits(1, bs); hdr->Cr_component = get_bits(5, bs); hdr->Cb_component = get_bits(5, bs); /* character color */ hdr->bitmap_top_H_coordinate = get_bits(12, bs); hdr->bitmap_top_V_coordinate = get_bits(12, bs); hdr->bitmap_bottom_H_coordinate = get_bits(12, bs); hdr->bitmap_bottom_V_coordinate = get_bits(12, bs); /* Calculate the height and width from top and bottom coordinates */ if ((hdr->bitmap_bottom_V_coordinate < hdr->bitmap_top_V_coordinate) || (hdr->bitmap_bottom_H_coordinate < hdr->bitmap_top_H_coordinate)) { BDBG_WRN(("%s: bad bitmap coordinate", __func__)); goto EXIT_PARSER; } /* if framed background style */ if (hdr->background_style) { hdr->frame_top_H_coordinate = get_bits(12, bs); hdr->frame_top_V_coordinate = get_bits(12, bs); hdr->frame_bottom_H_coordinate = get_bits(12, bs); hdr->frame_bottom_V_coordinate = get_bits(12, bs); hdr->frame_Y_component = get_bits(5, bs); hdr->frame_opaque_enable = get_bits(1, bs); hdr->frame_Cr_component = get_bits(5, bs); hdr->frame_Cb_component = get_bits(5, bs); } /* if outlined outline style */ if (OS_OUTLINE == hdr->outline_style) { get_bits(4, bs); /* skip reserved */ hdr->outline_thickness = get_bits(4, bs); hdr->outline_Y_component = get_bits(5, bs); hdr->outline_opaque_enable = get_bits(1, bs); hdr->outline_Cr_component = get_bits(5, bs); hdr->outline_Cb_component = get_bits(5, bs); } else if (OS_DROP_SHADOW == hdr->outline_style) { hdr->shadow_right = get_bits(4, bs); hdr->shadow_bottom = get_bits(4, bs); hdr->shadow_Y_component = get_bits(5, bs); hdr->shadow_opaque_enable = get_bits(1, bs); hdr->shadow_Cr_component = get_bits(5, bs); hdr->shadow_Cb_component = get_bits(5, bs); } else if (OS_RESERVED == hdr->outline_style) { /* what should we do */ get_bits_aligned(24, bs); } else { BDBG_WRN(("%s: unsupported outline style %d", __func__, hdr->background_style)); goto EXIT_PARSER; } hdr->bitmap_length = get_bits(16, bs); if (hdr->bitmap_data) { BDBG_WRN(("%s: bitmap_data allocated?", __func__)); free(hdr->bitmap_data); } hdr->bitmap_data = malloc(hdr->bitmap_length); if (!hdr->bitmap_data) { BDBG_WRN(("%s: out of memory, %d", __func__, hdr->bitmap_length)); goto EXIT_PARSER; } /* position pointer to compressed data */ bs->data = bs->data + (bs->bindex >> 3); /* copy compressed data for later process */ memcpy(hdr->bitmap_data, bs->data, hdr->bitmap_length); hPcr = GetDPCR(); if (NULL == hPcr) { hi = 0; } else BXPT_PCR_GetStc(hPcr, &hi, &lo); /* if rendering immediaterly */ if (hdr->immediate) { /* discard any pending render subtitle if any per section 4.1 */ if (pscte_27->render_list.count) scte_27_reset_list(pscte_27, SC_RENDER); /* if subtitle already rendered, clear screen first */ if (pscte_27->clear_list.count) { scte_27_reset_list(pscte_27, SC_CLEAR); hdr->pre_clear_display = true; } #ifndef SUBTITLE_DRAW_TEST hdr->display_in_PTS = hi; #endif } hdr->display_out_PTS = hdr->display_in_PTS + ((hdr->display_duration * PCRS_PER_SEC) / display_standard_array[hdr->display_standard]); BDBG_MSG(("####### in=%lx, out=%lx, stc=%x, diff(out-in)=%d\n", hdr->display_in_PTS, hdr->display_out_PTS, hi, (hdr->display_out_PTS - hdr->display_in_PTS))); if (hdr->immediate) { scte_27_render_subtitle(pscte_27, pdata); } else { insert_list(pscte_27->render_list, pdata); /* post render event */ bos_post_event(pscte_27->queue, (b_event_t*)&pscte_27->cmd_array[SC_RENDER]); } /* free segment data buffer if any */ if (pscte_27->segment_data) { free(pscte_27->segment_data); pscte_27->segment_data = NULL; pscte_27->pdata = NULL; } return true; EXIT_PARSER: if (pscte_27->segment_data) { free(pscte_27->segment_data); pscte_27->segment_data = NULL; } if (hdr->bitmap_data) { free(hdr->bitmap_data); hdr->bitmap_data = NULL; } return false; } /* * Create an 32 bit AYUV color using the 5 bit yuv components and opaque */ static uint32_t scte_27_create_color(uint8_t y, uint8_t u, uint8_t v,bool opaque) { uint32_t a; y = y << 3; u = u << 3; v = v << 3; a = opaque ? 0xff : 0x80; a = (a << 24) | (y << 16) | (u << 8) | v; return a; } /* * Check given color against given CLUT index for the match */ static bool scte_27_check_color(int index, uint32_t color) { if (index < 0 || index > CLUT_MAX_ENTRIES) { BDBG_WRN(("%s index is out of range", __func__)); return false; } return(clut_color_entries[index] != color) ? true : false; } /* * fill block area of subtitle */ static void scte_27_block_fill( pscte_27_handle pscte_27, uint16_t x, /* x */ uint16_t y, /* y */ uint16_t width, /* width in pixels */ uint16_t height, /* height */ uint32_t color) /* color to fill, index for CLUT */ { bapp_t *p_app = (bapp_t*)pscte_27->v_app; bgfx_fill_rect(&p_app->surf,x,y,width,height,color); bapp_sync(p_app); bapp_flush_screen(p_app); } /* * draw subtitle */ static void scte_27_draw_subtitle( pscte_27_handle pscte_27, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t spacing, uint32_t bg, uint32_t fg, bool flush, bool update_bg) { bapp_t *p_app = (bapp_t*)pscte_27->v_app; /* make sure that the parse function do range check, so if we are here, all the data are valid */ bgfx_fill_box_subtitle(&p_app->surf, x, y, width, height, pscte_27->bitmap, pscte_27->bitmap_size, bg, fg, update_bg); if (flush) { bapp_sync(p_app); bapp_flush_screen(p_app); } } #define CHECKCOLOR(index, clr) \ if (scte_27_check_color(index, clr)) { \ changed = true; \ clut_color_entries[index] = clr; \ *(palette + index) = clr; \ } \ clr = index; /* * subtitle message render * * Parameters: * pscte_27 pointer to scte_27 handle * imme_pdata pointer to scte_27_data if immediately draw is requested * * Returns: * true if parse the data correctly * false if anything is wrong */ static bool scte_27_render_subtitle(pscte_27_handle pscte_27, scte_27_data *imme_pdata) { bapp_t *p_app = (bapp_t*)pscte_27->v_app; scte_27_data *pdata = NULL; subtitle_message *hdr = NULL; unsigned int *palette, palette_size; /* palette pointer */ int i, j, width, height, maxbytes, size = 0; bool ret = true, changed = false; uint8_t *bitmap_data = NULL, on, off; uint32_t flags, bitmap_length, bit_offset = 0, bit_offset_current = 0; uint32_t bitmapFgColor, bitmapBgColor, shadowColor, outlineColor; struct bit_state_t bits, *bs; #ifdef DEBUG if (!pscte_27) { BDBG_WRN(("%s: null ptr or zero data", __func__)); return false; } #endif if (!imme_pdata) { remove_list(pscte_27->render_list); if (!pdata) { /* since currently we poll it to check a) if has data, b) if it is time to render, so it is normal if no data */ BDBG_MSG(("%s: no data to render", __func__)); return false; } } else pdata = imme_pdata; /* now get subtitle data */ hdr = pdata->msg; if (!hdr) { BDBG_ERR(("%s: null hdr ptr, pdata=%p. Something is wrong\n", pdata)); return false; } /* check time for rendering */ if (!hdr->immediate) { i = scte_27_pts_stc_diff(pscte_27, hdr->display_in_PTS); /* if past, discard it */ if (i < 0) { BDBG_WRN(("%s: render time passed. Discard it", __func__)); ret = false; goto EXIT_RENDER; } if (i > 0) { /* if furture, requeue it and try later */ insert_list_head(pscte_27->render_list, pdata); /* post render event */ bos_post_event(pscte_27->queue, (b_event_t*)&pscte_27->cmd_array[SC_RENDER]); return true; } } if (!pscte_27->enabled) { BDBG_WRN(("%s: render is disabled", __func__)); ret = false; goto EXIT_RENDER; } width = hdr->bitmap_bottom_H_coordinate - hdr->bitmap_top_H_coordinate + 1; height = hdr->bitmap_bottom_V_coordinate - hdr->bitmap_top_V_coordinate + 1; maxbytes = width * height; size = (maxbytes + 7) >> 3; /* TODO will reallcate? */ if (size > BITMAP_BUFFER_SIZE) { BDBG_WRN(("%s: bitmap buffer is too big ", __func__)); ret = false; goto EXIT_RENDER; } if (size < hdr->bitmap_length) { BDBG_WRN(("%s: uncompress bitmap (%d) < compressed bitmap (%d)", __func__, size, hdr->bitmap_length)); ret = false; goto EXIT_RENDER; } /* clear bitmap buffer to all off */ memset(pscte_27->bitmap, 0, size); pscte_27->bitmap_size = size; /* prepare to decompress */ bitmap_data = hdr->bitmap_data; bitmap_length = hdr->bitmap_length; /* decompress data */ bs = &bits; bs->data = bitmap_data; bs->bindex = 0; /* decompress data */ while ((bs->bindex >> 3) < bitmap_length) { on = off = 0; i = get_bits(1, bs); if (1 == i) { /* 1xxxYYYYY */ on = get_bits(3, bs); if (0 == on) on = 8; off = get_bits(5, bs); if (0 == off) off = 32; } else { i = get_bits(1, bs); if (1 == i) { /* 01YYYYYY */ off = get_bits(6, bs); if (0 == off) off = 64; } else { i = get_bits(1, bs); /* 001XXXX */ if (1 == i) { on = get_bits(4, bs); if (0 == on) on = 16; } else { i = get_bits(2, bs); /* cmd */ if (1 == i) { /* end of line */ bit_offset = bit_offset_current + width; bit_offset_current = bit_offset; continue; } else { BDBG_MSG(("cmd=%d, bs->bindex=%d, offset=%d\n", i, bs->bindex, bit_offset)); if (0 == i) break; continue; } } } } if ((bit_offset >> 3) + (on + off) <= maxbytes) { while (on) { uint8_t *uncompress_byte = (uint8_t *)(pscte_27->bitmap + (bit_offset >> 3)); *uncompress_byte |= (1 << (7 - (bit_offset & 0x7))); bit_offset++; on--; } bit_offset += off; } else { BDBG_ERR(("%s: uncompress bitmap out of range, offset=%d, maxbytes=%d", __func__, (bit_offset >> 3) + (on + off), maxbytes)); ret = false; goto EXIT_RENDER; } } if ((bs->bindex >> 3) != bitmap_length) { BDBG_ERR(("s: uncompressed data not match", __func__)); ret = false; goto EXIT_RENDER; } hdr->top_H = hdr->bitmap_top_H_coordinate; hdr->top_V = hdr->bitmap_top_V_coordinate; hdr->bottom_H = hdr->bitmap_bottom_H_coordinate; hdr->bottom_V = hdr->bitmap_bottom_V_coordinate; bapp_palette_get(ePALETTE_APP,(unsigned int **)&palette, &palette_size); /* FG color */ bitmapFgColor = scte_27_create_color(hdr->Y_component, hdr->Cr_component, hdr->Cb_component, hdr->opaque_enable); CHECKCOLOR(CLUT_FG_INDEX, bitmapFgColor); /* BG color */ if (hdr->background_style) { if (hdr->frame_top_H_coordinate < hdr->top_H) { hdr->top_H = hdr->frame_top_H_coordinate; } if (hdr->frame_top_V_coordinate < hdr->top_V) { hdr->top_V = hdr->frame_top_V_coordinate; } if (hdr->frame_bottom_H_coordinate > hdr->bottom_H) { hdr->bottom_H = hdr->frame_bottom_H_coordinate; } if (hdr->frame_bottom_V_coordinate > hdr->bottom_V) { hdr->bottom_V = hdr->frame_bottom_V_coordinate; } bitmapBgColor = scte_27_create_color(hdr->frame_Y_component, hdr->frame_Cr_component, hdr->frame_Cb_component, hdr->opaque_enable); CHECKCOLOR(CLUT_BG_INDEX, bitmapBgColor); } else bitmapBgColor = eCOLOR_CLEAR; outlineColor = CLUT_OUTLINE_INDEX; shadowColor = CLUT_FRAME_INDEX; switch (hdr->outline_style) { case OS_OUTLINE: hdr->bottom_H += hdr->outline_thickness * 2; hdr->bottom_V += hdr->outline_thickness * 2; outlineColor = scte_27_create_color(hdr->outline_Y_component, hdr->outline_Cr_component, hdr->outline_Cb_component, hdr->outline_opaque_enable); CHECKCOLOR(CLUT_OUTLINE_INDEX, outlineColor); break; case OS_DROP_SHADOW: hdr->bottom_H += hdr->shadow_right; hdr->bottom_V += hdr->shadow_bottom; shadowColor = scte_27_create_color(hdr->shadow_Y_component, hdr->shadow_Cr_component, hdr->shadow_Cb_component, hdr->opaque_enable); CHECKCOLOR(CLUT_FRAME_INDEX, shadowColor); break; } if (changed) { /* update CLUT's last four entries which corresponding to subtitle */ bgraphics_load_palette(p_app->graphics); } if (pscte_27->clear_list.count > 0) { hdr->pre_clear_display = true; scte_27_reset_list(pscte_27, SC_CLEAR); } /* Draw the frame first */ if (hdr->pre_clear_display) { #if DISPLAY_PAL scte_27_block_fill(pscte_27, 0, 0, BFMT_PAL_WIDTH, BFMT_PAL_HEIGHT - 2, eCOLOR_CLEAR); #else scte_27_block_fill(pscte_27, 0, 0, BFMT_NTSC_WIDTH, BFMT_NTSC_HEIGHT - 2, eCOLOR_CLEAR); #endif } if (hdr->background_style) { scte_27_block_fill(pscte_27, hdr->frame_top_H_coordinate, hdr->frame_top_V_coordinate, hdr->frame_bottom_H_coordinate - hdr->frame_top_H_coordinate + 1, hdr->frame_bottom_V_coordinate - hdr->frame_top_V_coordinate + 1, bitmapBgColor); } switch (hdr->outline_style) { case OS_OUTLINE: hdr->bottom_H += hdr->outline_thickness; hdr->bottom_H += hdr->outline_thickness; for (i = -hdr->outline_thickness; i <= hdr->outline_thickness; i += 2) { for (j = -hdr->outline_thickness; j <= hdr->outline_thickness; j += 2) { if (i && j) { scte_27_draw_subtitle(pscte_27, hdr->top_H + i, hdr->top_V + j, width, height, 0, 0, outlineColor, false, false); } } } break; case OS_DROP_SHADOW: scte_27_draw_subtitle(pscte_27, hdr->top_H + hdr->shadow_right, hdr->top_V + hdr->shadow_bottom, width, height, 0, 0, shadowColor, false, false); break; } scte_27_draw_subtitle(pscte_27, hdr->top_H, hdr->top_V, width, height, 0, bitmapBgColor, bitmapFgColor, true, false); BDBG_MSG(("%s: pdata=%p", __func__, pdata)); BDBG_MSG(("subtitle x=%d, y=%d, w=%d, h=%d", hdr->top_H, hdr->top_V, width, height)); BDBG_MSG(("clear x=%d, y=%d, w=%d, h=%d", hdr->top_H, hdr->top_V, hdr->bottom_H - hdr->top_H, hdr->bottom_V - hdr->top_V)); /* passing through */ EXIT_RENDER: /* to free allocated memory */ if (hdr->bitmap_data) { free(hdr->bitmap_data); hdr->bitmap_data = NULL; hdr->bitmap_size = 0; } /* if rendering successfull, put it to the clear_list list */ if (pdata) { if (true == ret) { #ifdef SUBTITLE_DRAW_TEST insert_list(pscte_27->free_list, pdata); #else insert_list(pscte_27->clear_list, pdata); /* post render event */ bos_post_event(pscte_27->queue, (b_event_t*)&pscte_27->cmd_array[SC_CLEAR]); #endif } else { insert_list(pscte_27->free_list, pdata); } } return ret; } /* * clear subtitle * * Parameters: * pscte_27 pointer to scte_27 handle * * Returns: * true if parse the data correctly * false if anything is wrong */ static bool scte_27_clear_subtitle(pscte_27_handle pscte_27) { uint32_t flags; scte_27_data *pdata; if (bos_acquire_mutex(&pscte_27->mutex, SCTE_27_MUTEX_TIME) != b_ok) { return false; } remove_list(pscte_27->clear_list); bos_release_mutex(&pscte_27->mutex); if (pdata) { subtitle_message *hdr = pdata->msg; int i; i = scte_27_pts_stc_diff(pscte_27, hdr->display_out_PTS); /* if furture, requeue it and try later */ if (i > 0) { /* furture */ insert_list_head(pscte_27->clear_list, pdata); /* post render event */ bos_post_event(pscte_27->queue, (b_event_t*)&pscte_27->cmd_array[SC_CLEAR]); } else { BDBG_MSG(("%s: clear subtitle, pdata=%p, x=%d, y=%d, w=%d, h=%d\n", __func__, pdata, hdr->top_H, hdr->top_V, hdr->bottom_H - hdr->top_H + 1, hdr->bottom_V - hdr->top_V + 1)); if (pscte_27->enabled) { scte_27_block_fill(pscte_27, hdr->top_H, hdr->top_V, hdr->bottom_H - hdr->top_H + 1, hdr->bottom_V - hdr->top_V + 1, eCOLOR_CLEAR); } /* put it back to free list */ insert_list(pscte_27->free_list, pdata); } return true; } return false; } /* * scte 27 event handler * * Parameters: * data pointer to scte_27 handle * * Returns: * NONE */ void scte_27_handler(void *data) { b_event_t orig_event, *cur_event; pscte_27_handle pscte_27 = (pscte_27_handle)data; while (true) { orig_event = pscte_27->cmd_array[0]; switch (pscte_27->cmd_array[0]) { default: case SC_NONE: cur_event = (b_event_t *)bos_pend_event(pscte_27->queue, SCTE_27_EVENT_TIME); /* if no pending event */ if (NULL == cur_event) { if (pscte_27->render_list.count) scte_27_render_subtitle(pscte_27, (pscte_27_data)NULL); if (pscte_27->clear_list.count) scte_27_clear_subtitle(pscte_27); break; } pscte_27->cmd_array[0] = *cur_event; break; case SC_RENDER: scte_27_render_subtitle(pscte_27, (pscte_27_data)NULL); if (pscte_27->clear_list.count) scte_27_clear_subtitle(pscte_27); break; case SC_CLEAR: /* to clear subtitle */ scte_27_clear_subtitle(pscte_27); if (pscte_27->render_list.count) scte_27_render_subtitle(pscte_27, (pscte_27_data)NULL); break; case SC_IDLE: /* if idle, wait for longer time */ orig_event = SC_NONE; bos_sleep(SCTE_27_EVENT_TIME * 10); break; } } }