📄 subtitle.c
字号:
/***************************************************************************** * subtitle.c: Demux for subtitle text files. ***************************************************************************** * Copyright (C) 1999-2004 VideoLAN * $Id: subtitle.c 11159 2005-05-24 19:52:57Z hartman $ * * Authors: Laurent Aimar <fenrir@via.ecp.fr> * Derk-Jan Hartman <hartman at videolan dot org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#include <stdlib.h>#include <errno.h>#ifdef HAVE_SYS_TYPES_H# include <sys/types.h>#endif#include <ctype.h>#include <vlc/vlc.h>#include <vlc/input.h>#include "vlc_video.h"/***************************************************************************** * Module descriptor *****************************************************************************/static int Open ( vlc_object_t *p_this );static void Close( vlc_object_t *p_this );#define SUB_DELAY_LONGTEXT \ "Delay subtitles (in 1/10s)"#define SUB_FPS_LONGTEXT \ "Override frames per second. " \ "It will only work with MicroDVD subtitles."#define SUB_TYPE_LONGTEXT \ "One from \"microdvd\", \"subrip\", \"ssa1\", \"ssa2-4\", \"ass\", \"vplayer\" " \ "\"sami\" (auto for autodetection, it should always work)."static char *ppsz_sub_type[] ={ "auto", "microdvd", "subrip", "subviewer", "ssa1", "ssa2-4", "ass", "vplayer", "sami"};vlc_module_begin(); set_shortname( _("Subtitles")); set_description( _("Text subtitles demux") ); set_capability( "demux2", 0 ); set_category( CAT_INPUT ); set_subcategory( SUBCAT_INPUT_DEMUX ); add_float( "sub-fps", 0.0, NULL, N_("Frames per second"), SUB_FPS_LONGTEXT, VLC_TRUE ); add_integer( "sub-delay", 0, NULL, N_("Subtitles delay"), SUB_DELAY_LONGTEXT, VLC_TRUE ); add_string( "sub-type", "auto", NULL, "Subtitles fileformat", SUB_TYPE_LONGTEXT, VLC_TRUE ); change_string_list( ppsz_sub_type, 0, 0 ); set_callbacks( Open, Close ); add_shortcut( "subtitle" );vlc_module_end();/***************************************************************************** * Prototypes: *****************************************************************************/enum{ SUB_TYPE_UNKNOWN = -1, SUB_TYPE_MICRODVD, SUB_TYPE_SUBRIP, SUB_TYPE_SSA1, SUB_TYPE_SSA2_4, SUB_TYPE_ASS, SUB_TYPE_VPLAYER, SUB_TYPE_SAMI, SUB_TYPE_SUBVIEWER,};typedef struct{ int i_line_count; int i_line; char **line;} text_t;static int TextLoad( text_t *, stream_t *s );static void TextUnload( text_t * );typedef struct{ int64_t i_start; int64_t i_stop; char *psz_text;} subtitle_t;struct demux_sys_t{ int i_type; text_t txt; es_out_id_t *es; int64_t i_next_demux_date; int64_t i_microsecperframe; int64_t i_original_mspf; char *psz_header; int i_subtitle; int i_subtitles; subtitle_t *subtitle; int64_t i_length;};static int ParseMicroDvd ( demux_t *, subtitle_t * );static int ParseSubRip ( demux_t *, subtitle_t * );static int ParseSubViewer( demux_t *, subtitle_t * );static int ParseSSA ( demux_t *, subtitle_t * );static int ParseVplayer ( demux_t *, subtitle_t * );static int ParseSami ( demux_t *, subtitle_t * );static struct{ char *psz_type_name; int i_type; char *psz_name; int (*pf_read)( demux_t *, subtitle_t* );} sub_read_subtitle_function [] ={ { "microdvd", SUB_TYPE_MICRODVD, "MicroDVD", ParseMicroDvd }, { "subrip", SUB_TYPE_SUBRIP, "SubRIP", ParseSubRip }, { "subviewer", SUB_TYPE_SUBVIEWER, "SubViewer",ParseSubViewer }, { "ssa1", SUB_TYPE_SSA1, "SSA-1", ParseSSA }, { "ssa2-4", SUB_TYPE_SSA2_4, "SSA-2/3/4",ParseSSA }, { "ass", SUB_TYPE_ASS, "SSA/ASS", ParseSSA }, { "vplayer", SUB_TYPE_VPLAYER, "VPlayer", ParseVplayer }, { "sami", SUB_TYPE_SAMI, "SAMI", ParseSami }, { NULL, SUB_TYPE_UNKNOWN, "Unknown", NULL }};static int Demux( demux_t * );static int Control( demux_t *, int, va_list );static void Fix( demux_t * );/***************************************************************************** * Module initializer *****************************************************************************/static int Open ( vlc_object_t *p_this ){ demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; es_format_t fmt; float f_fps; char *psz_type; int (*pf_read)( demux_t *, subtitle_t* ); int i, i_max; if( strcmp( p_demux->psz_demux, "subtitle" ) ) { msg_Dbg( p_demux, "subtitle demux discarded" ); return VLC_EGENERIC; } p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); p_sys->psz_header = NULL; p_sys->i_subtitle = 0; p_sys->i_subtitles= 0; p_sys->subtitle = NULL; /* Get the FPS */ f_fps = var_CreateGetFloat( p_demux, "sub-fps" ); if( f_fps >= 1.0 ) { p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps ); } else { p_sys->i_microsecperframe = 0; } f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" ); if( f_fps >= 1.0 ) { p_sys->i_original_mspf = (int64_t)( (float)1000000 / f_fps ); } else { p_sys->i_original_mspf = 0; } /* Get or probe the type */ p_sys->i_type = SUB_TYPE_UNKNOWN; psz_type = var_CreateGetString( p_demux, "sub-type" ); if( *psz_type ) { int i; for( i = 0; ; i++ ) { if( sub_read_subtitle_function[i].psz_type_name == NULL ) break; if( !strcmp( sub_read_subtitle_function[i].psz_type_name, psz_type ) ) { p_sys->i_type = sub_read_subtitle_function[i].i_type; break; } } } free( psz_type ); /* Probe if unknown type */ if( p_sys->i_type == SUB_TYPE_UNKNOWN ) { int i_try; char *s = NULL; msg_Dbg( p_demux, "autodetecting subtitle format" ); for( i_try = 0; i_try < 256; i_try++ ) { int i_dummy; if( ( s = stream_ReadLine( p_demux->s ) ) == NULL ) break; if( strcasestr( s, "<SAMI>" ) ) { p_sys->i_type = SUB_TYPE_SAMI; break; } else if( sscanf( s, "{%d}{%d}", &i_dummy, &i_dummy ) == 2 || sscanf( s, "{%d}{}", &i_dummy ) == 1) { p_sys->i_type = SUB_TYPE_MICRODVD; break; } else if( sscanf( s, "%d:%d:%d,%d --> %d:%d:%d,%d", &i_dummy,&i_dummy,&i_dummy,&i_dummy, &i_dummy,&i_dummy,&i_dummy,&i_dummy ) == 8 ) { p_sys->i_type = SUB_TYPE_SUBRIP; break; } else if( !strncasecmp( s, "!: This is a Sub Station Alpha v1", 33 ) ) { p_sys->i_type = SUB_TYPE_SSA1; break; } else if( !strncasecmp( s, "ScriptType: v4.00+", 18 ) ) { p_sys->i_type = SUB_TYPE_ASS; break; } else if( !strncasecmp( s, "ScriptType: v4.00", 17 ) ) { p_sys->i_type = SUB_TYPE_SSA2_4; break; } else if( !strncasecmp( s, "Dialogue: Marked", 16 ) ) { p_sys->i_type = SUB_TYPE_SSA2_4; break; } else if( !strncasecmp( s, "Dialogue:", 9 ) ) { p_sys->i_type = SUB_TYPE_ASS; break; } else if( strcasestr( s, "[INFORMATION]" ) ) { p_sys->i_type = SUB_TYPE_SUBVIEWER; /* I hope this will work */ break; } else if( sscanf( s, "%d:%d:%d:", &i_dummy, &i_dummy, &i_dummy ) == 3 || sscanf( s, "%d:%d:%d ", &i_dummy, &i_dummy, &i_dummy ) == 3 ) { p_sys->i_type = SUB_TYPE_VPLAYER; break; } free( s ); s = NULL; } if( s ) free( s ); /* It will nearly always work even for non seekable stream thanks the * caching system, and if it fails we loose just a few sub */ if( stream_Seek( p_demux->s, 0 ) ) { msg_Warn( p_demux, "failed to rewind" ); } } if( p_sys->i_type == SUB_TYPE_UNKNOWN ) { msg_Err( p_demux, "failed to recognize subtitle type" ); return VLC_EGENERIC; } for( i = 0; ; i++ ) { if( sub_read_subtitle_function[i].i_type == p_sys->i_type ) { msg_Dbg( p_demux, "detected %s format", sub_read_subtitle_function[i].psz_name ); pf_read = sub_read_subtitle_function[i].pf_read; break; } } msg_Dbg( p_demux, "loading all subtitles..." ); /* Load the whole file */ TextLoad( &p_sys->txt, p_demux->s ); /* Parse it */ for( i_max = 0;; ) { if( p_sys->i_subtitles >= i_max ) { i_max += 500; if( !( p_sys->subtitle = realloc( p_sys->subtitle, sizeof(subtitle_t) * i_max ) ) ) { msg_Err( p_demux, "out of memory"); return VLC_ENOMEM; } } if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles] ) ) break; p_sys->i_subtitles++; } /* Unload */ TextUnload( &p_sys->txt ); msg_Dbg(p_demux, "loaded %d subtitles", p_sys->i_subtitles ); /* Fix subtitle (order and time) *** */ p_sys->i_subtitle = 0; p_sys->i_length = 0; if( p_sys->i_subtitles > 0 ) { p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_stop; /* +1 to avoid 0 */ if( p_sys->i_length <= 0 ) p_sys->i_length = p_sys->subtitle[p_sys->i_subtitles-1].i_start+1; } /* *** add subtitle ES *** */ if( p_sys->i_type == SUB_TYPE_SSA1 || p_sys->i_type == SUB_TYPE_SSA2_4 || p_sys->i_type == SUB_TYPE_ASS ) { es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','s','a',' ' ) ); } else { es_format_Init( &fmt, SPU_ES, VLC_FOURCC( 's','u','b','t' ) ); } if( p_sys->psz_header != NULL ) { fmt.i_extra = strlen( p_sys->psz_header ) + 1; fmt.p_extra = strdup( p_sys->psz_header ); } p_sys->es = es_out_Add( p_demux->out, &fmt ); return VLC_SUCCESS;}/***************************************************************************** * Close: Close subtitle demux *****************************************************************************/static void Close( vlc_object_t *p_this ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -