📄 subtitle.c
字号:
/***************************************************************************** * subtitle.c: Demux for subtitle text files. ***************************************************************************** * Copyright (C) 1999-2007 the VideoLAN team * $Id: 8883d124d5305d889df7cb55055d27fbfc44bb9c $ * * Authors: Laurent Aimar <fenrir@via.ecp.fr> * Derk-Jan Hartman <hartman at videolan dot org> * Jean-Baptiste Kempf <jb@videolan.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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include <vlc_common.h>#include <vlc_plugin.h>#include <vlc_input.h>#include <errno.h>#ifdef HAVE_SYS_TYPES_H# include <sys/types.h>#endif#include <ctype.h>#include <vlc_demux.h>#include <vlc_charset.h>/***************************************************************************** * Module descriptor *****************************************************************************/static int Open ( vlc_object_t *p_this );static void Close( vlc_object_t *p_this );#define SUB_DELAY_LONGTEXT \ N_("Apply a delay to all subtitles (in 1/10s, eg 100 means 10s).")#define SUB_FPS_LONGTEXT \ N_("Override the normal frames per second settings. " \ "This will only work with MicroDVD and SubRIP (SRT) subtitles.")#define SUB_TYPE_LONGTEXT \ N_("Force the subtiles format. Valid values are : \"microdvd\", " \ "\"subrip\", \"subviewer\", \"ssa1\", \"ssa2-4\", \"ass\", \"vplayer\", " \ "\"sami\", \"dvdsubtitle\", \"mpl2\", \"aqt\", \"pjs\", "\ "\"mpsub\", \"jacosub\", \"psb\", \"realtext\", \"dks\", \"subviewer1\", " \ " and \"auto\" (meaning autodetection, this should always work).")static const char *const ppsz_sub_type[] ={ "auto", "microdvd", "subrip", "subviewer", "ssa1", "ssa2-4", "ass", "vplayer", "sami", "dvdsubtitle", "mpl2", "aqt", "pjs", "mpsub", "jacosub", "psb", "realtext", "dks", "subviewer1"};vlc_module_begin(); set_shortname( N_("Subtitles")); set_description( N_("Text subtitles parser") ); set_capability( "demux", 0 ); set_category( CAT_INPUT ); set_subcategory( SUBCAT_INPUT_DEMUX ); add_float( "sub-fps", 0.0, NULL, N_("Frames per second"), SUB_FPS_LONGTEXT, true ); add_integer( "sub-delay", 0, NULL, N_("Subtitles delay"), SUB_DELAY_LONGTEXT, true ); add_string( "sub-type", "auto", NULL, N_("Subtitles format"), SUB_TYPE_LONGTEXT, true ); change_string_list( ppsz_sub_type, NULL, NULL ); 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, /* SUBVIEWER 2 */ SUB_TYPE_DVDSUBTITLE, /* Mplayer calls it subviewer2 */ SUB_TYPE_MPL2, SUB_TYPE_AQT, SUB_TYPE_PJS, SUB_TYPE_MPSUB, SUB_TYPE_JACOSUB, SUB_TYPE_PSB, SUB_TYPE_RT, SUB_TYPE_DKS, SUB_TYPE_SUBVIEW1 /* SUBVIEWER 1 - mplayer calls it subrip09, and Gnome subtitles SubViewer 1.0 */};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; char *psz_header; int i_subtitle; int i_subtitles; subtitle_t *subtitle; int64_t i_length; /* */ struct { bool b_inited; int i_comment; int i_time_resolution; int i_time_shift; } jss; struct { bool b_inited; float f_total; float f_factor; } mpsub;};static int ParseMicroDvd ( demux_t *, subtitle_t *, int );static int ParseSubRip ( demux_t *, subtitle_t *, int );static int ParseSubViewer ( demux_t *, subtitle_t *, int );static int ParseSSA ( demux_t *, subtitle_t *, int );static int ParseVplayer ( demux_t *, subtitle_t *, int );static int ParseSami ( demux_t *, subtitle_t *, int );static int ParseDVDSubtitle( demux_t *, subtitle_t *, int );static int ParseMPL2 ( demux_t *, subtitle_t *, int );static int ParseAQT ( demux_t *, subtitle_t *, int );static int ParsePJS ( demux_t *, subtitle_t *, int );static int ParseMPSub ( demux_t *, subtitle_t *, int );static int ParseJSS ( demux_t *, subtitle_t *, int );static int ParsePSB ( demux_t *, subtitle_t *, int );static int ParseRealText ( demux_t *, subtitle_t *, int );static int ParseDKS ( demux_t *, subtitle_t *, int );static int ParseSubViewer1 ( demux_t *, subtitle_t *, int );static const struct{ const char *psz_type_name; int i_type; const char *psz_name; int (*pf_read)( demux_t *, subtitle_t*, int );} 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 }, { "dvdsubtitle",SUB_TYPE_DVDSUBTITLE, "DVDSubtitle", ParseDVDSubtitle }, { "mpl2", SUB_TYPE_MPL2, "MPL2", ParseMPL2 }, { "aqt", SUB_TYPE_AQT, "AQTitle", ParseAQT }, { "pjs", SUB_TYPE_PJS, "PhoenixSub", ParsePJS }, { "mpsub", SUB_TYPE_MPSUB, "MPSub", ParseMPSub }, { "jacosub", SUB_TYPE_JACOSUB, "JacoSub", ParseJSS }, { "psb", SUB_TYPE_PSB, "PowerDivx", ParsePSB }, { "realtext", SUB_TYPE_RT, "RealText", ParseRealText }, { "dks", SUB_TYPE_DKS, "DKS", ParseDKS }, { "subviewer1", SUB_TYPE_SUBVIEW1, "Subviewer 1", ParseSubViewer1 }, { 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 ); int i, i_max; if( !p_demux->b_force ) { 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 ) ); if( p_sys == NULL ) return VLC_ENOMEM; p_sys->psz_header = NULL; p_sys->i_subtitle = 0; p_sys->i_subtitles = 0; p_sys->subtitle = NULL; p_sys->i_microsecperframe = 40000; p_sys->jss.b_inited = false; p_sys->mpsub.b_inited = false; /* Get the FPS */ f_fps = var_CreateGetFloat( p_demux, "sub-original-fps" ); /* FIXME */ if( f_fps >= 1.0 ) p_sys->i_microsecperframe = (int64_t)( (float)1000000 / f_fps ); msg_Dbg( p_demux, "Movie fps: %f", f_fps ); /* Check for override of 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 ); msg_Dbg( p_demux, "Override subtitle fps %f", f_fps ); } /* Get or probe the type */ p_sys->i_type = SUB_TYPE_UNKNOWN; psz_type = var_CreateGetString( p_demux, "sub-type" ); if( psz_type && *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; char p_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.%d %d:%d:%d", &i_dummy, &i_dummy, &i_dummy, &i_dummy, &i_dummy, &i_dummy, &i_dummy ) == 7 || sscanf( s, "@%d @%d", &i_dummy, &i_dummy) == 2) { p_sys->i_type = SUB_TYPE_JACOSUB; 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; } else if( sscanf( s, "{T %d:%d:%d:%d", &i_dummy, &i_dummy, &i_dummy, &i_dummy ) == 4 ) { p_sys->i_type = SUB_TYPE_DVDSUBTITLE; break; } else if( sscanf( s, "[%d:%d:%d]%c", &i_dummy, &i_dummy, &i_dummy, &p_dummy ) == 4 ) { p_sys->i_type = SUB_TYPE_DKS; break; } else if( strstr( s, "*** START SCRIPT" ) ) { p_sys->i_type = SUB_TYPE_SUBVIEW1; break; } else if( sscanf( s, "[%d][%d]", &i_dummy, &i_dummy ) == 2 || sscanf( s, "[%d][]", &i_dummy ) == 1) { p_sys->i_type = SUB_TYPE_MPL2; break; } else if( sscanf (s, "FORMAT=%d", &i_dummy) == 1 || ( sscanf (s, "FORMAT=TIM%c", &p_dummy) == 1 && p_dummy =='E' ) ) { p_sys->i_type = SUB_TYPE_MPSUB; break; } else if( sscanf( s, "-->> %d", &i_dummy) == 1 ) { p_sys->i_type = SUB_TYPE_AQT; break; } else if( sscanf( s, "%d,%d,", &i_dummy, &i_dummy ) == 2 ) { p_sys->i_type = SUB_TYPE_PJS; break; } else if( sscanf( s, "{%d:%d:%d}", &i_dummy, &i_dummy, &i_dummy ) == 3 ) { p_sys->i_type = SUB_TYPE_PSB; break; } else if( strcasestr( s, "<time" ) ) { p_sys->i_type = SUB_TYPE_RT; break; } free( s ); s = NULL; } free( s ); /* It will nearly always work even for non seekable stream thanks the * caching system, and if it fails we lose just a few sub */ if( stream_Seek( p_demux->s, 0 ) ) { msg_Warn( p_demux, "failed to rewind" ); } } /* Quit on unknown subtitles */ if( p_sys->i_type == SUB_TYPE_UNKNOWN ) { msg_Err( p_demux, "failed to recognize subtitle type" ); free( p_sys ); 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 ) ) ) { free( p_sys->subtitle ); TextUnload( &p_sys->txt ); free( p_sys ); return VLC_ENOMEM; } } if( pf_read( p_demux, &p_sys->subtitle[p_sys->i_subtitles], 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -