static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt, bool b_text, int i_columns, int i_rows, int i_align, mtime_t i_pts ) { video_format_t fmt; subpicture_t *p_spu=NULL; /* If there is a page or sub to render, then we do that here */ /* Create the subpicture unit */ if( b_text ) p_spu = decoder_NewSubpictureText( p_dec ); else p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); return NULL; } video_format_Init(&fmt, b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA); if( b_text ) { fmt.i_bits_per_pixel = 0; } else { fmt.i_width = fmt.i_visible_width = i_columns * 12; fmt.i_height = fmt.i_visible_height = i_rows * 10; fmt.i_bits_per_pixel = 32; fmt.i_sar_num = fmt.i_sar_den = 0; /* let the vout set the correct AR */ } fmt.i_x_offset = fmt.i_y_offset = 0; p_spu->p_region = subpicture_region_New( &fmt ); if( p_spu->p_region == NULL ) { msg_Err( p_dec, "cannot allocate SPU region" ); subpicture_Delete( p_spu ); return NULL; } p_spu->p_region->i_x = 0; p_spu->p_region->i_y = 0; p_spu->i_start = i_pts; p_spu->i_stop = b_text ? i_pts + (10*CLOCK_FREQ): 0; p_spu->b_ephemer = true; p_spu->b_absolute = b_text ? false : true; if( !b_text ) p_spu->p_region->i_align = i_align; p_spu->i_original_picture_width = fmt.i_width; p_spu->i_original_picture_height = fmt.i_height; /* */ *p_fmt = fmt; return p_spu; }
/***************************************************************************** * ParseText: parse an text subtitle packet and send it to the video output *****************************************************************************/ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) return NULL; /* We cannot display a subpicture with no date */ if( p_block->i_pts <= VLC_TS_INVALID ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; } /* Check validity of packet data */ /* An "empty" line containing only \0 can be used to force and ephemer picture from the screen */ if( p_block->i_buffer < 1 ) { msg_Warn( p_dec, "no subtitle data" ); return NULL; } /* Should be resiliant against bad subtitles */ psz_subtitle = malloc( p_block->i_buffer + 1 ); if( psz_subtitle == NULL ) return NULL; memcpy( psz_subtitle, p_block->p_buffer, p_block->i_buffer ); psz_subtitle[p_block->i_buffer] = '\0'; if( p_sys->iconv_handle == (vlc_iconv_t)-1 ) { if (EnsureUTF8( psz_subtitle ) == NULL) { msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); } } else { if( p_sys->b_autodetect_utf8 ) { if( IsUTF8( psz_subtitle ) == NULL ) { msg_Dbg( p_dec, "invalid UTF-8 sequence: " "disabling UTF-8 subtitles autodetection" ); p_sys->b_autodetect_utf8 = false; } } if( !p_sys->b_autodetect_utf8 ) { size_t inbytes_left = strlen( psz_subtitle ); size_t outbytes_left = 6 * inbytes_left; char *psz_new_subtitle = xmalloc( outbytes_left + 1 ); char *psz_convert_buffer_out = psz_new_subtitle; const char *psz_convert_buffer_in = psz_subtitle; size_t ret = vlc_iconv( p_sys->iconv_handle, &psz_convert_buffer_in, &inbytes_left, &psz_convert_buffer_out, &outbytes_left ); *psz_convert_buffer_out++ = '\0'; free( psz_subtitle ); if( ( ret == (size_t)(-1) ) || inbytes_left ) { free( psz_new_subtitle ); msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); return NULL; } psz_subtitle = realloc( psz_new_subtitle, psz_convert_buffer_out - psz_new_subtitle ); if( !psz_subtitle ) psz_subtitle = psz_new_subtitle; } } /* Create the subpicture unit */ p_spu = decoder_NewSubpictureText( p_dec ); if( !p_spu ) { free( psz_subtitle ); return NULL; } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align; p_spu_sys->region.p_segments = ParseSubtitles( &p_spu_sys->region.inner_align, psz_subtitle ); free( psz_subtitle ); return p_spu; }
/***************************************************************************** * Decode: *****************************************************************************/ static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block ) { block_t *p_block; subpicture_t *p_spu = NULL; if( ( pp_block == NULL ) || ( *pp_block == NULL ) ) return NULL; p_block = *pp_block; *pp_block = NULL; if( ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) || p_block->i_buffer < sizeof(uint16_t) ) { block_Release( p_block ); return NULL; } uint8_t *p_buf = p_block->p_buffer; /* Read our raw string and create the styled segment for HTML */ uint16_t i_psz_length = GetWBE( p_buf ); char *psz_subtitle = malloc( i_psz_length + 1 ); if ( !psz_subtitle ) return NULL; memcpy( psz_subtitle, p_block->p_buffer + sizeof(uint16_t), i_psz_length ); psz_subtitle[ i_psz_length ] = '\0'; p_buf += i_psz_length + sizeof(uint16_t); for( uint16_t i=0; i < i_psz_length; i++ ) if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n'; segment_t *p_segment = calloc( 1, sizeof(segment_t) ); if ( !p_segment ) { free( psz_subtitle ); return NULL; } p_segment->psz_string = strdup( psz_subtitle ); p_segment->i_size = strlen( psz_subtitle ); if ( p_dec->fmt_in.subs.p_style ) { p_segment->styles.i_color = p_dec->fmt_in.subs.p_style->i_font_color; p_segment->styles.i_color |= p_dec->fmt_in.subs.p_style->i_font_alpha << 24; if ( p_dec->fmt_in.subs.p_style->i_style_flags ) p_segment->styles.i_flags = p_dec->fmt_in.subs.p_style->i_style_flags; p_segment->styles.i_fontsize = p_dec->fmt_in.subs.p_style->i_font_size; } if ( !p_segment->psz_string ) { SegmentFree( p_segment ); free( psz_subtitle ); return NULL; } /* Create the subpicture unit */ p_spu = decoder_NewSubpictureText( p_dec ); if( !p_spu ) { free( psz_subtitle ); SegmentFree( p_segment ); return NULL; } subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; /* Parse our styles */ while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer ) { uint32_t i_atomsize = GetDWBE( p_buf ); vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]); p_buf += 8; switch( i_atomtype ) { case VLC_FOURCC('s','t','y','l'): { if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break; uint16_t i_nbrecords = GetWBE(p_buf); uint16_t i_cur_record = 0; p_buf += 2; while( i_cur_record++ < i_nbrecords ) { if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break; uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_length - 1 ); uint16_t i_end = __MIN( GetWBE(p_buf + 2), i_psz_length - 1 ); segment_style_t style; style.i_flags = ConvertFlags( p_buf[6] ); style.i_fontsize = p_buf[7]; style.i_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB style.i_color |= (GetDWBE(p_buf+8) & 0xFF) << 24; ApplySegmentStyle( &p_segment, i_start, i_end, &style ); if ( i_nbrecords == 1 ) { if ( p_buf[6] ) { p_spu_sys->style_flags.i_value = ConvertFlags( p_buf[6] ); p_spu_sys->style_flags.b_set = true; } p_spu_sys->i_font_height_abs_to_src = p_buf[7]; p_spu_sys->font_color.i_value = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB p_spu_sys->font_color.i_value |= (GetDWBE(p_buf+8) & 0xFF) << 24; p_spu_sys->font_color.b_set = true; } p_buf += 12; } } break; case VLC_FOURCC('d','r','p','o'): if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break; p_spu_sys->i_drop_shadow = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) ); break; case VLC_FOURCC('d','r','p','t'): if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break; p_spu_sys->i_drop_shadow_alpha = GetWBE(p_buf); break; default: break; } p_buf += i_atomsize; } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; p_spu_sys->align = SUBPICTURE_ALIGN_BOTTOM; p_spu_sys->text = psz_subtitle; p_spu_sys->p_htmlsegments = p_segment; block_Release( p_block ); return p_spu; }
static subpicture_t *render( decoder_t *p_dec, arib_parser_t *p_parser, arib_decoder_t *p_arib_decoder, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; size_t i_data_size; const unsigned char *psz_data = arib_parser_get_data( p_parser, &i_data_size ); if( !psz_data || !i_data_size ) return NULL; size_t i_subtitle_size = i_data_size * 4; psz_subtitle = (char*) calloc( i_subtitle_size + 1, sizeof(*psz_subtitle) ); if( psz_subtitle == NULL ) { return NULL; } if( p_sys->b_a_profile ) arib_initialize_decoder_a_profile( p_arib_decoder ); else arib_initialize_decoder_c_profile( p_arib_decoder ); i_subtitle_size = arib_decode_buffer( p_arib_decoder, psz_data, i_data_size, psz_subtitle, i_subtitle_size ); #ifdef DEBUG_ARIBSUB msg_Dbg( p_dec, "psz_subtitle [%s]", psz_subtitle ); unsigned const char* start = psz_data; unsigned const char* end = psz_data + i_data_size; char* psz_subtitle_data_hex = (char*) calloc( i_data_size * 3 + 1, sizeof(char) ); char* psz_subtitle_data_hex_idx = psz_subtitle_data_hex; while( start < end ) { sprintf(psz_subtitle_data_hex_idx, "%02x ", *start++); psz_subtitle_data_hex_idx += 3; } msg_Dbg( p_dec, "psz_subtitle_data [%s]", psz_subtitle_data_hex); free( psz_subtitle_data_hex ); #endif p_spu = decoder_NewSubpictureText( p_dec ); if( p_spu == NULL ) { goto decoder_NewSubpictureText_failed; } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + arib_decoder_get_time( p_arib_decoder ); p_spu->b_ephemer = (p_spu->i_start == p_spu->i_stop); p_spu->b_absolute = true; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; arib_text_region_t *p_region = p_spu_sys->p_region = (arib_text_region_t*) calloc( 1, sizeof(arib_text_region_t) ); if( p_region == NULL ) { goto malloc_failed; } for( const arib_buf_region_t *p_buf_region = arib_decoder_get_regions( p_arib_decoder ); p_buf_region; p_buf_region = p_buf_region->p_next ) { if( p_sys->b_ignore_ruby && p_buf_region->i_fontheight == 18 ) { continue; } int i_size = p_buf_region->p_end - p_buf_region->p_start; char *psz_text = (char*) calloc( i_size + 1, sizeof(char) ); if( psz_text == NULL ) { goto malloc_failed; } strncpy(psz_text, p_buf_region->p_start, i_size); psz_text[i_size] = '\0'; #ifdef DEBUG_ARIBSUB msg_Dbg( p_dec, "psz_text [%s]", psz_text ); #endif p_region->psz_text = strdup( psz_text ); free( psz_text ); p_region->psz_fontname = NULL; p_region->i_font_color = p_buf_region->i_foreground_color; p_region->i_planewidth = p_buf_region->i_planewidth; p_region->i_planeheight = p_buf_region->i_planeheight; p_region->i_fontwidth = p_buf_region->i_fontwidth; p_region->i_fontheight = p_buf_region->i_fontheight; p_region->i_verint = p_buf_region->i_verint; p_region->i_horint = p_buf_region->i_horint; p_region->i_charleft = p_buf_region->i_charleft; p_region->i_charbottom = p_buf_region->i_charbottom; p_region->i_charleft_adj = 0; p_region->i_charbottom_adj = 0; if( !p_sys->b_ignore_position_adjustment ) { p_region->i_charleft_adj = p_buf_region->i_horadj; p_region->i_charbottom_adj = p_buf_region->i_veradj; } p_region->p_next = NULL; if( p_buf_region->p_next != NULL ) { p_region = p_region->p_next = (arib_text_region_t*) calloc( 1, sizeof(arib_text_region_t) ); if( p_region == NULL ) { goto malloc_failed; } } } decoder_NewSubpictureText_failed: malloc_failed: arib_finalize_decoder( p_arib_decoder ); free( psz_subtitle ); return p_spu; }
/***************************************************************************** * Decode: *****************************************************************************/ static int Decode( decoder_t *p_dec, block_t *p_block ) { subpicture_t *p_spu = NULL; if( p_block == NULL ) /* No Drain */ return VLCDEC_SUCCESS; if( ( p_block->i_flags & (BLOCK_FLAG_CORRUPTED) ) || p_block->i_buffer < sizeof(uint16_t) ) { block_Release( p_block ); return VLCDEC_SUCCESS; } uint8_t *p_buf = p_block->p_buffer; /* Read our raw string and create the styled segment for HTML */ uint16_t i_psz_bytelength = GetWBE( p_buf ); const uint8_t *p_pszstart = p_block->p_buffer + sizeof(uint16_t); char *psz_subtitle; if ( i_psz_bytelength > 2 && ( !memcmp( p_pszstart, "\xFE\xFF", 2 ) || !memcmp( p_pszstart, "\xFF\xFE", 2 ) ) ) { psz_subtitle = FromCharset( "UTF-16", p_pszstart, i_psz_bytelength ); if ( !psz_subtitle ) return VLCDEC_SUCCESS; } else { psz_subtitle = malloc( i_psz_bytelength + 1 ); if ( !psz_subtitle ) return VLCDEC_SUCCESS; memcpy( psz_subtitle, p_pszstart, i_psz_bytelength ); psz_subtitle[ i_psz_bytelength ] = '\0'; } p_buf += i_psz_bytelength + sizeof(uint16_t); for( uint16_t i=0; i < i_psz_bytelength; i++ ) if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n'; tx3g_segment_t *p_segment3g = tx3g_segment_New( psz_subtitle ); p_segment3g->i_size = str8len( psz_subtitle ); if ( p_dec->fmt_in.subs.p_style ) p_segment3g->s->style = text_style_Duplicate( p_dec->fmt_in.subs.p_style ); free( psz_subtitle ); if ( !p_segment3g->s->psz_text ) { text_segment_Delete( p_segment3g->s ); free( p_segment3g ); return VLCDEC_SUCCESS; } /* Create the subpicture unit */ p_spu = decoder_NewSubpictureText( p_dec ); if( !p_spu ) { text_segment_Delete( p_segment3g->s ); free( p_segment3g ); return VLCDEC_SUCCESS; } subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; /* Parse our styles */ while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer ) { uint32_t i_atomsize = GetDWBE( p_buf ); vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]); p_buf += 8; switch( i_atomtype ) { case VLC_FOURCC('s','t','y','l'): { if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break; uint16_t i_nbrecords = GetWBE(p_buf); uint16_t i_cur_record = 0; p_buf += 2; while( i_cur_record++ < i_nbrecords ) { if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break; uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_bytelength - 1 ); uint16_t i_end = __MIN( GetWBE(p_buf + 2), i_psz_bytelength - 1 ); text_style_t style; memset( &style, 0, sizeof(text_style_t) ); style.i_style_flags = ConvertFlags( p_buf[6] ); style.i_font_size = p_buf[7]; style.i_font_color = GetDWBE(p_buf+8) >> 8;// RGBA -> RGB style.i_font_alpha = GetDWBE(p_buf+8) & 0xFF; style.i_features = STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA; ApplySegmentStyle( &p_segment3g, i_start, i_end, &style ); if ( i_nbrecords == 1 ) { if ( p_buf[6] ) { if( (p_spu_sys->p_default_style->i_style_flags = ConvertFlags( p_buf[6] )) ) p_spu_sys->p_default_style->i_features |= STYLE_HAS_FLAGS; } p_spu_sys->p_default_style->i_font_size = p_buf[7]; p_spu_sys->p_default_style->i_font_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB p_spu_sys->p_default_style->i_font_alpha = (GetDWBE(p_buf+8) & 0xFF) << 24; p_spu_sys->p_default_style->i_features |= (STYLE_HAS_FONT_COLOR | STYLE_HAS_FONT_ALPHA); } p_buf += 12; } } break; case VLC_FOURCC('d','r','p','o'): if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break; p_spu_sys->p_default_style->i_shadow_width = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) ); break; case VLC_FOURCC('d','r','p','t'): if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break; p_spu_sys->p_default_style->i_shadow_alpha = GetWBE(p_buf); p_spu_sys->p_default_style->i_features |= STYLE_HAS_SHADOW_ALPHA; break; default: break; } p_buf += i_atomsize; } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_BOTTOM; FontSizeConvert( p_dec->fmt_in.subs.p_style, p_spu_sys->p_default_style ); /* Unwrap */ text_segment_t *p_text_segments = p_segment3g->s; text_segment_t *p_cur = p_text_segments; while( p_segment3g ) { FontSizeConvert( p_dec->fmt_in.subs.p_style, p_segment3g->s->style ); tx3g_segment_t * p_old = p_segment3g; p_segment3g = p_segment3g->p_next3g; free( p_old ); if( p_segment3g ) p_cur->p_next = p_segment3g->s; p_cur = p_cur->p_next; } p_spu_sys->region.p_segments = p_text_segments; block_Release( p_block ); decoder_QueueSub( p_dec, p_spu ); return VLCDEC_SUCCESS; }