/**************************************************************************** * DecodeBlock: the whole thing **************************************************************************** * This function must be fed with complete subtitles units. ****************************************************************************/ static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { subpicture_t *p_spu; if( !pp_block || *pp_block == NULL ) { return NULL; } ParseText( p_dec, *pp_block ); block_Release( *pp_block ); *pp_block = NULL; /* allocate an empty subpicture to return. the actual subpicture * displaying is done in the DisplayAnchor function in intf.c (called from * DisplayPendingAnchor, which in turn is called from the main RunIntf * loop). */ p_spu = decoder_NewSubpicture( p_dec ); if( !p_spu ) { msg_Dbg( p_dec, "couldn't allocate new subpicture" ); return NULL; } return p_spu; }
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; /* If there is a page or sub to render, then we do that here */ /* Create the subpicture unit */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); return NULL; } memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA; fmt.i_sar_num = 0; fmt.i_sar_den = 1; 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_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" ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } p_spu->p_region->i_x = 0; p_spu->p_region->i_y = 0; p_spu->p_region->i_align = i_align; p_spu->i_start = i_pts; p_spu->i_stop = 0; p_spu->b_ephemer = true; p_spu->b_absolute = true; if( !b_text ) { p_spu->i_original_picture_width = fmt.i_width; p_spu->i_original_picture_height = fmt.i_height; } /* */ *p_fmt = fmt; return p_spu; }
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; /* 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 = strndup( (const char *)p_block->p_buffer, p_block->i_buffer ); if( psz_subtitle == NULL ) return NULL; /* USF Subtitles are mandated to be UTF-8 -- make sure it is */ if (EnsureUTF8( psz_subtitle ) == NULL) { msg_Err( p_dec, "USF subtitles must be in UTF-8 format.\n" "This stream contains USF subtitles which aren't." ); } /* Create the subpicture unit */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( psz_subtitle ); return NULL; } /* Decode USF strings */ p_spu->p_region = ParseUSFString( p_dec, psz_subtitle ); 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->i_original_picture_width = p_sys->i_original_width; p_spu->i_original_picture_height = p_sys->i_original_height; free( psz_subtitle ); return p_spu; }
static subpicture_t *Decode(decoder_t *dec, block_t **block) { if (block == NULL || *block == NULL) return NULL; subpicture_t *sub = NULL; block_t *b = *block; *block = NULL; if (b->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) goto exit; if (b->i_buffer < 128) goto exit; int payload_size = (b->i_buffer / 128) * 112; uint8_t *payload = malloc(payload_size); if (!payload) goto exit; for (unsigned i = 0; i < b->i_buffer / 128; i++) memcpy(&payload[112 * i], &b->p_buffer[128 * i + 16], 112); sub = decoder_NewSubpicture(dec, NULL); if (!sub) { free(payload); goto exit; } sub->i_start = b->i_pts; sub->i_stop = b->i_pts + b->i_length; sub->b_ephemer = b->i_length == 0; sub->b_absolute = false; //sub->i_original_picture_width = 0; //sub->i_original_picture_height = 0; video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_TEXT); sub->p_region = subpicture_region_New(&fmt); video_format_Clean(&fmt); if (sub->p_region) { sub->p_region->psz_text = ParseText(payload, payload_size, cct_nums[dec->p_sys->cct - CCT_BEGIN].str); sub->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM; sub->p_region->psz_html = NULL; } free(payload); exit: block_Release(b); return sub; }
/**************************************************************************** * DecodeBlock: ****************************************************************************/ static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; block_t *p_block; if( !pp_block || *pp_block == NULL ) return NULL; p_block = *pp_block; *pp_block = NULL; if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) { Flush( p_dec ); block_Release( p_block ); return NULL; } if( p_block->i_buffer == 0 || p_block->p_buffer[0] == '\0' ) { block_Release( p_block ); return NULL; } subpicture_updater_sys_t *p_spu_sys = malloc( sizeof(*p_spu_sys) ); if( !p_spu_sys ) { block_Release( p_block ); return NULL; } subpicture_updater_t updater = { .pf_validate = SubpictureValidate, .pf_update = SubpictureUpdate, .pf_destroy = SubpictureDestroy, .p_sys = p_spu_sys, }; p_spu = decoder_NewSubpicture( p_dec, &updater ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( p_spu_sys ); block_Release( p_block ); return NULL; } p_spu_sys->p_img = NULL; p_spu_sys->p_dec_sys = p_sys; p_spu_sys->i_subs_len = p_block->i_buffer; p_spu_sys->p_subs_data = malloc( p_block->i_buffer ); p_spu_sys->i_pts = p_block->i_pts; if( !p_spu_sys->p_subs_data ) { subpicture_Delete( p_spu ); block_Release( p_block ); return NULL; } memcpy( p_spu_sys->p_subs_data, p_block->p_buffer, p_block->i_buffer ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = __MAX( p_sys->i_max_stop, p_block->i_pts + p_block->i_length ); p_spu->b_ephemer = true; p_spu->b_absolute = true; p_sys->i_max_stop = p_spu->i_stop; vlc_mutex_lock( &p_sys->lock ); if( p_sys->p_track ) { ass_process_chunk( p_sys->p_track, p_spu_sys->p_subs_data, p_spu_sys->i_subs_len, p_block->i_pts / 1000, p_block->i_length / 1000 ); } vlc_mutex_unlock( &p_sys->lock ); DecSysHold( p_sys ); /* Keep a reference for the returned subpicture */ block_Release( p_block ); return p_spu; }
/***************************************************************************** * DecodePacket: parse and decode an subtitle packet ***************************************************************************** * This function parses and decodes an SPU packet and, if valid, returns a * subpicture. *****************************************************************************/ static subpicture_t *DecodePacket( decoder_t *p_dec, block_t *p_data ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu; subpicture_region_t *p_region; video_format_t fmt; video_palette_t palette; int i; /* Allocate the subpicture internal data. */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) return NULL; p_spu->i_start = p_data->i_pts; p_spu->i_stop = p_data->i_pts + p_sys->i_duration; p_spu->b_ephemer = true; /* Create new subtitle region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_YUVP; /** The video on which the subtitle sits, is scaled, probably 4:3. However subtitle bitmaps assume an 1:1 aspect ratio. FIXME: We should get the video aspect ratio from somewhere. Two candidates are the video and the other possibility would be the access module. */ fmt.i_sar_num = p_sys->i_height; fmt.i_sar_den = p_sys->i_width; fmt.i_width = fmt.i_visible_width = p_sys->i_width; fmt.i_height = fmt.i_visible_height = p_sys->i_height; fmt.i_x_offset = fmt.i_y_offset = 0; fmt.p_palette = &palette; fmt.p_palette->i_entries = 4; for( i = 0; i < fmt.p_palette->i_entries; i++ ) { fmt.p_palette->palette[i][0] = p_sys->p_palette[i][0]; fmt.p_palette->palette[i][1] = p_sys->p_palette[i][1]; fmt.p_palette->palette[i][2] = p_sys->p_palette[i][2]; fmt.p_palette->palette[i][3] = p_sys->p_palette[i][3]; } p_region = subpicture_region_New( &fmt ); if( !p_region ) { msg_Err( p_dec, "cannot allocate SVCD subtitle region" ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } p_spu->p_region = p_region; p_region->i_x = p_sys->i_x_start; p_region->i_y = p_sys->i_y_start; SVCDSubRenderImage( p_dec, p_data, p_region ); 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; video_format_t fmt; /* 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_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( psz_subtitle ); return NULL; } /* Create a new subpicture region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_TEXT; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = fmt.i_y_offset = 0; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { msg_Err( p_dec, "cannot allocate SPU region" ); free( psz_subtitle ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } /* Decode and format the subpicture unit */ if( p_dec->fmt_in.i_codec != VLC_CODEC_SSA ) { /* Normal text subs, easy markup */ p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align; p_spu->p_region->i_x = p_sys->i_align ? 20 : 0; p_spu->p_region->i_y = 10; /* Remove formatting from string */ p_spu->p_region->psz_text = StripTags( psz_subtitle ); if( var_InheritBool( p_dec, "subsdec-formatted" ) ) { p_spu->p_region->psz_html = CreateHtmlSubtitle( &p_spu->p_region->i_align, psz_subtitle ); } 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; } else { /* Decode SSA/USF strings */ ParseSSAString( p_dec, psz_subtitle, p_spu ); 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->i_original_picture_width = p_sys->i_original_width; p_spu->i_original_picture_height = p_sys->i_original_height; } free( psz_subtitle ); return p_spu; }