📄 dvbsub.c
字号:
{ encoder_t *p_enc = (encoder_t *)p_this; encoder_sys_t *p_sys; vlc_value_t val; if( ( p_enc->fmt_out.i_codec != VLC_FOURCC('d','v','b','s') ) && !p_enc->b_force ) { return VLC_EGENERIC; } /* Allocate the memory needed to store the decoder's structure */ if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) return VLC_ENOMEM; p_enc->p_sys = p_sys; p_enc->pf_encode_sub = Encode; p_enc->fmt_out.i_codec = VLC_FOURCC('d','v','b','s'); p_enc->fmt_out.subs.dvb.i_id = 1 << 16 | 1; config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg ); p_sys->i_page_ver = 0; p_sys->i_region_ver = 0; p_sys->i_clut_ver = 0; p_sys->i_regions = 0; p_sys->p_regions = 0; var_Create( p_this, ENC_CFG_PREFIX "x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Get( p_this, ENC_CFG_PREFIX "x", &val ); p_sys->i_offset_x = val.i_int; var_Create( p_this, ENC_CFG_PREFIX "y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Get( p_this, ENC_CFG_PREFIX "y", &val ); p_sys->i_offset_y = val.i_int; return VLC_SUCCESS;}/* FIXME: this routine is a hack to convert VLC_FOURCC('Y','U','V','A') * into VLC_FOURCC('Y','U','V','P') */static subpicture_t *YuvaYuvp( subpicture_t *p_subpic ){ subpicture_region_t *p_region = NULL; if( !p_subpic ) return NULL; for( p_region = p_subpic->p_region; p_region; p_region = p_region->p_next ) { video_format_t *p_fmt = &p_region->fmt; int i = 0, j = 0, n = 0, p = 0; int i_max_entries = 256;#ifdef RANDOM_DITHERING int i_seed = 0xdeadbeef; /* random seed */#else int *pi_delta;#endif int i_pixels = p_region->picture.p[0].i_visible_lines * p_region->picture.p[0].i_pitch; int i_iterator = p_region->picture.p[0].i_visible_lines * 3 / 4 * p_region->picture.p[0].i_pitch + p_region->picture.p[0].i_pitch * 1 / 3; int i_tolerance = 0;#ifdef DEBUG_DVBSUB msg_Dbg( p_enc, "YuvaYuvp: i_pixels=%d, i_iterator=%d", i_pixels, i_iterator );#endif p_fmt->i_chroma = VLC_FOURCC('Y','U','V','P'); p_fmt->p_palette = (video_palette_t *) malloc( sizeof( video_palette_t ) ); if( !p_fmt->p_palette ) break; p_fmt->p_palette->i_entries = 0; /* Find best iterator using Euclide’s algorithm */ for( ; i_iterator > 1 ; i_iterator-- ) { int a = i_pixels; int b = i_iterator; int c; while( b ) { c = a % b; a = b; b = c; } if( a == 1 ) { break; } } /* Count colors, build best palette */ for( i_tolerance = 0; i_tolerance < 128; i_tolerance++ ) { bool b_success = true; p_fmt->p_palette->i_entries = 0; for( i = 0; i < i_pixels ; ) { uint8_t y, u, v, a; y = p_region->picture.p[0].p_pixels[i]; u = p_region->picture.p[1].p_pixels[i]; v = p_region->picture.p[2].p_pixels[i]; a = p_region->picture.p[3].p_pixels[i]; for( j = 0; j < p_fmt->p_palette->i_entries; j++ ) { if( abs((int)p_fmt->p_palette->palette[j][0] - (int)y) <= i_tolerance && abs((int)p_fmt->p_palette->palette[j][1] - (int)u) <= i_tolerance && abs((int)p_fmt->p_palette->palette[j][2] - (int)v) <= i_tolerance && abs((int)p_fmt->p_palette->palette[j][3] - (int)a) <= i_tolerance / 2 ) { break; } } if( j == p_fmt->p_palette->i_entries ) { p_fmt->p_palette->palette[j][0] = y; p_fmt->p_palette->palette[j][1] = u; p_fmt->p_palette->palette[j][2] = v; p_fmt->p_palette->palette[j][3] = a; p_fmt->p_palette->i_entries++; } if( p_fmt->p_palette->i_entries >= i_max_entries ) { b_success = false; break; } i += i_iterator; if( i > i_pixels ) { i -= i_pixels; } } if( b_success ) { break; } }#ifdef DEBUG_DVBSUB msg_Dbg( p_enc, "best palette has %d colors", p_fmt->p_palette->i_entries );#endif#ifndef RANDOM_DITHERING pi_delta = malloc( ( p_region->picture.p[0].i_pitch + 1 ) * sizeof(int) * 4 ); for( i = 0; i < (p_region->picture.p[0].i_pitch + 1) * 4 ; i++ ) { pi_delta[ i ] = 0; }#endif /* Fill image with our new colours */ for( p = 0; p < p_region->picture.p[0].i_visible_lines ; p++ ) { int i_ydelta = 0, i_udelta = 0, i_vdelta = 0, i_adelta = 0; for( n = 0; n < p_region->picture.p[0].i_pitch ; n++ ) { int i_offset = p * p_region->picture.p[0].i_pitch + n; int y, u, v, a; int i_mindist, i_best; y = (int)p_region->picture.p[0].p_pixels[i_offset]; u = (int)p_region->picture.p[1].p_pixels[i_offset]; v = (int)p_region->picture.p[2].p_pixels[i_offset]; a = (int)p_region->picture.p[3].p_pixels[i_offset]; /* Add dithering compensation */#ifdef RANDOM_DITHERING y += ((i_seed & 0xff) - 0x80) * i_tolerance / 0x80; u += (((i_seed >> 8) & 0xff) - 0x80) * i_tolerance / 0x80; v += (((i_seed >> 16) & 0xff) - 0x80) * i_tolerance / 0x80; a += (((i_seed >> 24) & 0xff) - 0x80) * i_tolerance / 0x80;#else y += i_ydelta + pi_delta[ n * 4 ]; u += i_udelta + pi_delta[ n * 4 + 1 ]; v += i_vdelta + pi_delta[ n * 4 + 2 ]; a += i_adelta + pi_delta[ n * 4 + 3 ];#endif /* Find best colour in palette */ for( i_mindist = 99999999, i_best = 0, j = 0; j < p_fmt->p_palette->i_entries; j++ ) { int i_dist = 0; i_dist += abs((int)p_fmt->p_palette->palette[j][0] - y); i_dist += abs((int)p_fmt->p_palette->palette[j][1] - u); i_dist += abs((int)p_fmt->p_palette->palette[j][2] - v); i_dist += 2 * abs((int)p_fmt->p_palette->palette[j][3] - a); if( i_dist < i_mindist ) { i_mindist = i_dist; i_best = j; } } /* Set pixel to best color */ p_region->picture.p[0].p_pixels[i_offset] = i_best; /* Update dithering state */#ifdef RANDOM_DITHERING i_seed = (i_seed * 0x1283837) ^ 0x789479 ^ (i_seed >> 13);#else i_ydelta = y - (int)p_fmt->p_palette->palette[i_best][0]; i_udelta = u - (int)p_fmt->p_palette->palette[i_best][1]; i_vdelta = v - (int)p_fmt->p_palette->palette[i_best][2]; i_adelta = a - (int)p_fmt->p_palette->palette[i_best][3]; pi_delta[ n * 4 ] = i_ydelta * 3 / 8; pi_delta[ n * 4 + 1 ] = i_udelta * 3 / 8; pi_delta[ n * 4 + 2 ] = i_vdelta * 3 / 8; pi_delta[ n * 4 + 3 ] = i_adelta * 3 / 8; i_ydelta = i_ydelta * 5 / 8; i_udelta = i_udelta * 5 / 8; i_vdelta = i_vdelta * 5 / 8; i_adelta = i_adelta * 5 / 8;#endif } }#ifndef RANDOM_DITHERING free( pi_delta );#endif /* pad palette */ for( i = p_fmt->p_palette->i_entries; i < i_max_entries; i++ ) { p_fmt->p_palette->palette[i][0] = 0; p_fmt->p_palette->palette[i][1] = 0; p_fmt->p_palette->palette[i][2] = 0; p_fmt->p_palette->palette[i][3] = 0; } p_fmt->p_palette->i_entries = i_max_entries;#ifdef DEBUG_DVBSUB msg_Dbg( p_enc, "best palette has %d colors", p_fmt->p_palette->i_entries );#endif } return p_subpic;} /* End of hack *//**************************************************************************** * Encode: the whole thing ****************************************************************************/static block_t *Encode( encoder_t *p_enc, subpicture_t *p_subpic ){ subpicture_t *p_temp = NULL; subpicture_region_t *p_region = NULL; bs_t bits, *s = &bits; block_t *p_block; if( !p_subpic || !p_subpic->p_region ) return NULL; /* FIXME: this is a hack to convert VLC_FOURCC('Y','U','V','A') into * VLC_FOURCC('Y','U','V','P') */ p_region = p_subpic->p_region; if( p_region->fmt.i_chroma == VLC_FOURCC('Y','U','V','A') ) { p_temp = YuvaYuvp( p_subpic ); if( !p_temp ) { msg_Err( p_enc, "no picture in subpicture" ); return NULL; } p_region = p_subpic->p_region; } /* Sanity check */ if( !p_region ) return NULL; if( ( p_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) && ( p_region->fmt.i_chroma != VLC_FOURCC('Y','U','V','P') ) ) { char psz_fourcc[5]; memset( &psz_fourcc, 0, sizeof( psz_fourcc ) ); vlc_fourcc_to_char( p_region->fmt.i_chroma, &psz_fourcc ); msg_Err( p_enc, "chroma %4s not supported", psz_fourcc ); return NULL; } if( p_region->fmt.p_palette ) { switch( p_region->fmt.p_palette->i_entries ) { case 0: case 4: case 16: case 256: break; default: msg_Err( p_enc, "subpicture palette (%d) not handled", p_region->fmt.p_palette->i_entries ); return NULL; } } /* End of hack */#ifdef DEBUG_DVBSUB msg_Dbg( p_enc, "encoding subpicture" );#endif p_block = block_New( p_enc, 64000 ); bs_init( s, p_block->p_buffer, p_block->i_buffer ); bs_write( s, 8, 0x20 ); /* Data identifier */ bs_write( s, 8, 0x0 ); /* Subtitle stream id */ encode_page_composition( p_enc, s, p_subpic ); encode_region_composition( p_enc, s, p_subpic ); encode_clut( p_enc, s, p_subpic ); encode_object( p_enc, s, p_subpic ); /* End of display */ bs_write( s, 8, 0x0f ); /* Sync byte */ bs_write( s, 8, DVBSUB_ST_ENDOFDISPLAY ); /* Segment type */ bs_write( s, 16, 1 ); /* Page id */ bs_write( s, 16, 0 ); /* Segment length */ bs_write( s, 8, 0xff );/* End marker */ p_block->i_buffer = bs_pos( s ) / 8; p_block->i_pts = p_block->i_dts = p_subpic->i_start; if( !p_subpic->b_ephemer && ( p_subpic->i_stop > p_subpic->i_start ) ) { block_t *p_block_stop; p_block->i_length = p_subpic->i_stop - p_subpic->i_start; /* Send another (empty) subtitle to signal the end of display */ p_block_stop = block_New( p_enc, 64000 ); bs_init( s, p_block_stop->p_buffer, p_block_stop->i_buffer ); bs_write( s, 8, 0x20 ); /* Data identifier */ bs_write( s, 8, 0x0 ); /* Subtitle stream id */ encode_page_composition( p_enc, s, 0 ); bs_write( s, 8, 0x0f ); /* Sync byte */ bs_write( s, 8, DVBSUB_ST_ENDOFDISPLAY ); /* Segment type */ bs_write( s, 16, 1 ); /* Page id */ bs_write( s, 16, 0 ); /* Segment length */ bs_write( s, 8, 0xff );/* End marker */ p_block_stop->i_buffer = bs_pos( s ) / 8; p_block_stop->i_pts = p_block_stop->i_dts = p_subpic->i_stop; block_ChainAppend( &p_block, p_block_stop ); p_block_stop->i_length = 100000; /* p_subpic->i_stop - p_subpic->i_start; */ }#ifdef DEBUG_DVBSUB msg_Dbg( p_enc, "subpicture encoded properly" );#endif return p_block;}/***************************************************************************** * CloseEncoder: encoder destruction *****************************************************************************/static void CloseEncoder( vlc_object_t *p_this ){ encoder_t *p_enc = (encoder_t *)p_this; encoder_sys_t *p_sys = p_enc->p_sys; var_Destroy( p_this , ENC_CFG_PREFIX "x" ); var_Destroy( p_this , ENC_CFG_PREFIX "y" ); var_Destroy( p_this , ENC_CFG_PREFIX "timeout" ); if( p_sys->i_regions ) free( p_sys->p_regions ); free( p_sys );}static void encode_page_composition( encoder_t *p_enc, bs_t *s, subpicture_t *p_subpic ){ encoder_sys_t *p_sys = p_enc->p_sys; subpicture_region_t *p_region; bool b_mode_change = false; int i_regions, i_timeout; bs_write( s, 8, 0x0f ); /* Sync byte */ bs_write( s, 8, DVBSUB_ST_PAGE_COMPOSITION ); /* Segment type */ bs_write( s, 16, 1 ); /* Page id */ for( i_regions = 0, p_region = p_subpic ? p_subpic->p_region : 0; p_region; p_region = p_region->p_next, i_regions++ ) { if( i_regions >= p_sys->i_regions ) { encoder_region_t region; region.i_width = region.i_height = 0; p_sys->p_regions = realloc( p_sys->p_regions, sizeof(encoder_region_t) * (p_sys->i_regions + 1) ); p_sys->p_regions[p_sys->i_regions++] = region; } if( ( p_sys->p_regions[i_regions].i_width < (int)p_region->fmt.i_visible_width ) || ( p_sys->p_regions[i_regions].i_width > (int)p_region->fmt.i_visible_width ) ) { b_mode_change = true; msg_Dbg( p_enc, "region %i width change: %i -> %i", i_regions, p_sys->p_regions[i_regions].i_width, p_region->fmt.i_visible_width ); p_sys->p_regions[i_regions].i_width = p_region->fmt.i_visible_width; } if( p_sys->p_regions[i_regions].i_height < (int)p_region->fmt.i_visible_height ) { b_mode_change = true; msg_Dbg( p_enc, "region %i height change: %i -> %i", i_regions, p_sys->p_regions[i_regions].i_height, p_region->fmt.i_visible_height ); p_sys->p_regions[i_regions].i_height =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -