VB编写程序实现视频外挂SRT字幕

      在DVDRip、MKV等720P高清视频中,srt、ssa、ass、idx、sub等外挂字幕非常流行,采用的显示技术毫不例外都是VSFilter(早期版本是VobSub)和AviSynth外挂字幕插件。显示的点阵汉字非常漂亮!现在流行的多媒体播放器,如KMPlayer、暴风影音等都采用了上述DirectVobSub技术。虽然DirectVobSub技术具有开放性强,支持众多语言编写的播放器,但经常有挂不上字幕的问题。于是,想到用VB来编写外挂字幕程序。那VB能否实现外挂字幕呢?答案是肯定的!对于idx、sub等图形字幕来说,VB无能为力;但对于文本字幕srt、ssa、ass,VB还是可以解决的。虽不能和DirectVobSub技术相比,但基本功能还是可以实现的。请看下面VB编写的srt字幕解析代码:

 

'********************************************************************************
'* 模块名称:Srttitle.bas
'* 调用srt字幕文件成功后:SRTFileAnalysis返回字幕个数(每一个srt字幕可能有多行显示文本)
'* 调用失败后SRTFileAnalysis返回一个长整型数0
'* 特别声明:转载请用IP地址,严禁原文转载!
'* 作者:Chenjl1031(东方之珠) 
'********************************************************************************

Option Explicit

 

Public SRTtitle() As String 'srt字幕数组

 

'srt字幕提取
Public Function SRTFileAnalysis(ByVal SrtFileName As String) As Long
       Dim LineCount As Long, FileNumber As Long, TimeLenth As Long, TimeNumber As Long
       Dim Stitle As String, TimeLabel(1 To 2) As String, St As String, StC As String, sSrt() As String
       Dim GotTime As Boolean
       Dim sHour As Long, sMunite As Long, sSecond As Long, s1 As Long, s2 As Long, s3 As Long
      
       On Error Resume Next
      
       If Dir(SrtFileName) = "" Then SRTFileAnalysis = 0: Exit Function
       '扫描srt字幕个数并定义字幕数组
       FileNumber = FreeFile: LineCount = 1
       Open SrtFileName For Input Lock Read As #FileNumber
       Do While Not EOF(FileNumber)
          Line Input #FileNumber, Stitle '提取srt字幕标签
          If IsNumeric(Stitle) Then LineCount = LineCount + 1
       Loop
       Close #FileNumber
       SRTFileAnalysis = LineCount - 1
       ReDim SRTtitle(1 To LineCount - 1)
      
       '提取字幕内容和显示时间
       FileNumber = FreeFile: LineCount = 1: GotTime = False
       Open SrtFileName For Input Lock Read As #FileNumber

       Do While Not EOF(FileNumber)
          Line Input #FileNumber, Stitle '提取srt字幕标签
          Stitle = Trim(Stitle)
         
          If GotTime = True Then '已经取到时间标签
             If IsNumeric(Stitle) = False Then
                If Len(Stitle) <> 0 Then St = St & (Stitle & "-CRLF-") 'CRLF回车换行
             Else '进入下一个字幕
                GotTime = False '已经提取完第LineCount个字幕
                If Len(St) = 0 Then St = Space(10) ': Debug.Print Stitle
                SRTtitle(LineCount) = TimeLabel(1) & " [StartTime] " & TimeLabel(2) & " [EndTime] " & St '加上时间标签头
                SRTtitle(LineCount) = Trim(Left(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - 6))
                St = "": LineCount = LineCount + 1 '记录字幕序号
             End If
          End If

          '判断并提取时间标签
          TimeLenth = InStr(1, Stitle, "-->")
          If TimeLenth > 0 Then
             TimeLabel(1) = Trim(Left(Stitle, TimeLenth - 1)) '字幕显示开始时间
             s1 = InStr(1, TimeLabel(1), ":")
             s2 = InStr(s1 + 1, TimeLabel(1), ":")
             sHour = CLng(Left(TimeLabel(1), s1 - 1)) * 3600000
             sMunite = CLng(Mid(TimeLabel(1), s1 + 1, s2 - s1 - 1)) * 60000
             sSecond = CLng(Mid(TimeLabel(1), s2 + 1, Len(TimeLabel(1)) - s2))
             TimeLabel(1) = CStr(sHour + sMunite + sSecond)
            
             TimeLabel(2) = Trim(Right(Stitle, Len(Stitle) - TimeLenth - 3)) '字幕显示结束时间
             s1 = InStr(1, TimeLabel(2), ":")
             s2 = InStr(s1 + 1, TimeLabel(2), ":")
             sHour = CLng(Left(TimeLabel(2), s1 - 1)) * 3600000
             sMunite = CLng(Mid(TimeLabel(2), s1 + 1, s2 - s1 - 1)) * 60000
             sSecond = CLng(Mid(TimeLabel(2), s2 + 1, Len(TimeLabel(2)) - s2))
             TimeLabel(2) = CStr(sHour + sMunite + sSecond)
            
             GotTime = True
          End If
          DoEvents
       Loop
       '提取最后一个字幕
       SRTtitle(LineCount) = TimeLabel(1) & " [StartTime] " & TimeLabel(2) & " [EndTime] " & SRTtitle(LineCount) & St '加上时间标签头
       SRTtitle(LineCount) = Trim(Left(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - 6))
       Close #FileNumber
      
       '检查SRT字幕特效代码
       For LineCount = 1 To UBound(SRTtitle)
           '检查换行符:
,/N;空格符:/n,/h
           s1 = InStr(1, SRTtitle(LineCount), "/N")
           If s1 > 0 Then SRTtitle(LineCount) = Left$(SRTtitle(LineCount), s1 - 1) & " -CRLF- " & Right$(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - s1 - 2)
           s2 = InStr(1, SRTtitle(LineCount), "
")
           If s2 > 0 Then SRTtitle(LineCount) = Left$(SRTtitle(LineCount), s2 - 1) & " -CRLF- " & Right$(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - s2 - 3)
           s1 = InStr(1, SRTtitle(LineCount), "/n")
           If s1 > 0 Then SRTtitle(LineCount) = Left$(SRTtitle(LineCount), s1 - 1) & Right$(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - s1 - 1)
           s2 = InStr(1, SRTtitle(LineCount), "/h")
           If s2 > 0 Then SRTtitle(LineCount) = Left$(SRTtitle(LineCount), s2 - 1) & Right$(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - s2 - 1)
                     
           '初始化:fn字体类型,fs字体大小,fc字体颜色,fp字体位置,fi斜体,fu下划线,fb粗体。这些特效代码符合ASS字幕特效规范。其余特效代码一律扔掉。
           If InStr(1, SRTtitle(LineCount), "-CRLF-") > 0 Then
              sSrt = Split(SRTtitle(LineCount), "-CRLF-")
              s2 = InStr(1, sSrt(0), "[EndTime]")
              St = Left$(sSrt(0), s2 + 8) & " [fn=黑体][fs=24][fc=&HFFFFFF][fp=2][fi=0][fu=0][fb=0] " & Right$(sSrt(0), Len(sSrt(0)) - s2 - 8)
              For s1 = 1 To UBound(sSrt)
                  St = St & (" -CRLF- " & "[fn=黑体][fs=24][fc=&HFFFFFF][fp=2][fi=0][fu=0][fb=0] " & sSrt(s1)) '& " -CRLF- ")
              Next
           Else
               s2 = InStr(1, SRTtitle(LineCount), "[EndTime]")
               If Len(SRTtitle(LineCount)) - s2 - 8 >= 0 Then
                  St = Left$(SRTtitle(LineCount), s2 + 8) & " [fn=黑体][fs=24][fc=&HFFFFFF][fp=2][fi=0][fu=0][fb=0] " & Right$(SRTtitle(LineCount), Len(SRTtitle(LineCount)) - s2 - 8)
               End If
           End If
           SRTtitle(LineCount) = St: Erase sSrt
          
           '检查所用字体、字号、颜色、位置等信息
           If InStr(1, SRTtitle(LineCount), "-CRLF-") > 0 Then
             
              sSrt = Split(SRTtitle(LineCount), "-CRLF-")
              For s1 = 0 To UBound(sSrt)
                  St = "/fn": StC = "[fn="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 5): GoSub LinkSrt
                 
                  St = "/fs": StC = "[fs="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 5): GoSub LinkSrt
                 
                  St = "/c": StC = "[fc="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 11): GoSub LinkSrt
                 
                  St = "/a": StC = "[fp="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 4): GoSub LinkSrt
                 
                  St = "/i": StC = "[fi="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 4): GoSub LinkSrt
                 
                  St = "/u": StC = "[fu="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 4): GoSub LinkSrt
                 
                  St = "/b": StC = "[fb="
                  sSrt(s1) = Insert_String(sSrt(s1), St, StC, 4): GoSub LinkSrt
                 
                  Exit For
                 
LinkSrt:          St = sSrt(0)
                  For s3 = 1 To UBound(sSrt) '合并字幕
                      St = St & (" -CRLF- " & sSrt(s3))
                  Next
                  SRTtitle(LineCount) = St
                  Return
              Next
           Else
             
              St = "/fn": StC = "[fn="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 5)
             
              St = "/fs": StC = "[fs="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 5)
             
              St = "/c": StC = "[fc="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 11)
             
              St = "/a": StC = "[fp="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 4)
             
              St = "/i": StC = "[fi="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 4)
             
              St = "/u": StC = "[fu="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 4)
             
              St = "/b": StC = "[fb="
              SRTtitle(LineCount) = Insert_String(SRTtitle(LineCount), St, StC, 4)
             
           End If
           Erase sSrt
           SRTtitle(LineCount) = GetXMLCodeValue(SRTtitle(LineCount), ByVal "")
           SRTtitle(LineCount) = GetXMLCodeValue(SRTtitle(LineCount), ByVal "")
           SRTtitle(LineCount) = GetXMLCodeValue(SRTtitle(LineCount), ByVal "")
           SRTtitle(LineCount) = GetXMLCodeValue(SRTtitle(LineCount), ByVal "color=")
           SRTtitle(LineCount) = DeleEffectCode(SRTtitle(LineCount), ByVal "<", ByVal ">")
           SRTtitle(LineCount) = DeleEffectCode(SRTtitle(LineCount), ByVal "{", ByVal "}")     
           
       Next

End Function

 

'取出字符串中SSA、ASS特效字符串、插入Srt特效字符串
Private Function Insert_String(ByVal SourceSrtTitle As String, ByVal SrtEffectCode As String, ByVal SrtCode As String, ByVal LEffect As Long) As String
        Dim s1 As Long, s2 As Long, s3 As Long, St As String
        If Len(Trim(SourceSrtTitle)) = 0 Then Insert_String = Space(10): Exit Function
        s1 = 0
        On Error Resume Next
Cjl:
        s1 = InStr(s1 + 1, SourceSrtTitle, SrtEffectCode)
        If SrtEffectCode = "/b" And s1 > 0 Then
           If Mid$(SourceSrtTitle, s1, 3) = "/be" Then GoTo Cjl
        End If
        s3 = Len(SrtEffectCode)
        If s1 > 0 Then
           s2 = InStr(s1 + 1, SourceSrtTitle, "/")
           If s2 > 0 Then
              St = Mid$(SourceSrtTitle, s1 + s3, s2 - s1 - s3)
              If InStr(1, St, "{") > 0 Then St = Left$(St, InStr(1, St, "{") - 1) & Right$(St, Len(St) - InStr(1, St, "{"))
              If InStr(1, St, "}") > 0 Then St = Left$(St, InStr(1, St, "}") - 1) & Right$(St, Len(St) - InStr(1, St, "}"))
           Else
              s2 = InStr(s1 + 1, SourceSrtTitle, "}")
              If s2 > 0 Then
                 St = Mid$(SourceSrtTitle, s1 + s3, s2 - s1 - s3)
              End If
           End If
           s2 = InStr(1, SourceSrtTitle, SrtCode)
           If InStr(1, SrtEffectCode, "/fn") > 0 Or InStr(1, SrtEffectCode, "/fs") > 0 Then
              Insert_String = Left$(SourceSrtTitle, s2 + s3) & St & Right$(SourceSrtTitle, Len(SourceSrtTitle) - s2 - LEffect)
           Else
              Insert_String = Left$(SourceSrtTitle, s2 + s3 + 1) & St & Right$(SourceSrtTitle, Len(SourceSrtTitle) - s2 - LEffect)
           End If
        Else
           Insert_String = SourceSrtTitle
        End If
End Function

 

'取得srt字幕中类似XML代码的值
Private Function GetXMLCodeValue(ByVal SourceSrtTitle As String, ByVal SrtXMLcode As String) As String
        Dim LineCount As Long, sSrt() As String
        Dim P1 As Long, P2 As Long, P3 As Long, CO As String, St As String
        Dim Pc1 As Long, Pc2 As Long
        Dim i As Long, j As Long, K As Long
       
        On Error Resume Next
       
        If InStr(1, SourceSrtTitle, "-CRLF-") > 0 Then
           If InStr(1, SourceSrtTitle, SrtXMLcode) > 0 Then
              sSrt = Split(SourceSrtTitle, "-CRLF-")
              'ReDim sSrt_1(UBound(sSrt))
              For LineCount = 0 To UBound(sSrt)
                  P1 = InStr(1, sSrt(LineCount), SrtXMLcode)                 
                  St = Mid$(LCase(SrtXMLcode), 2, 1): P2 = InStr(1, sSrt(LineCount), "")
                  If LCase(SrtXMLcode) = "color=" Then
                     P2 = InStr(1, sSrt(LineCount), "")
                     If InStr(1, sSrt(LineCount), "color=") > 0 Then
                        Pc1 = InStr(1, sSrt(LineCount), "color="): Pc2 = InStr(Pc1 + 6, sSrt(LineCount), ">")
                        CO = Mid$(sSrt(LineCount), Pc1 + 6, Pc2 - Pc1 - 6)
                        If InStr(1, CO, Chr$(34)) > 0 Then CO = "&H" & Mid$(CO, 2, Len(CO) - 2)
                        If InStr(1, CO, "#") > 0 Then CO = "&H" & Right$(CO, Len(CO) - InStr(1, CO, "#"))
                     End If
                  End If
                  K = LineCount
                  If P1 > 0 Then                    
                     St = Mid$(LCase(SrtXMLcode), 2, 1): P3 = InStr(1, sSrt(K), "f" & St & "="): Mid$(sSrt(K), P3 + 3, 1) = "1"
                     If LCase(SrtXMLcode) = "color=" Then
                        Pc1 = InStr(1, sSrt(K), "fc="): Pc2 = InStr(1, sSrt(K), "][fp")
                        sSrt(K) = Left$(sSrt(K), Pc1 + 2) & CO & Right$(sSrt(K), Len(sSrt(K)) - Pc2 + 1)
                     End If
                     If P2 > 0 Then
                        GoTo Cjl
                     Else
                        For i = LineCount + 1 To UBound(sSrt)                           
                            St = Mid$(LCase(SrtXMLcode), 2, 1): j = InStr(1, sSrt(i), "")
                            If LCase(SrtXMLcode) = "color=" Then j = InStr(1, sSrt(i), "")
                            K = i                           
                            St = Mid$(LCase(SrtXMLcode), 2, 1): P3 = InStr(1, sSrt(K), "f" & St & "="): Mid$(sSrt(K), P3 + 3, 1) = "1"
                            If LCase(SrtXMLcode) = "color=" Then
                               Pc1 = InStr(1, sSrt(K), "fc="): Pc2 = InStr(1, sSrt(K), "][fp")
                               sSrt(K) = Left$(sSrt(K), Pc1 + 2) & CO & Right$(sSrt(K), Len(sSrt(K)) - Pc2 + 1)
                            End If
                            If j > 0 Then Exit For
                        Next
                     End If
                  End If
Cjl:          '执行下一次循环
              Next
             
              St = sSrt(0)
              For i = 1 To UBound(sSrt) '合并字幕
                  St = St & (" -CRLF- " & sSrt(i))
              Next
              Erase sSrt
              GetXMLCodeValue = St
           Else
              GetXMLCodeValue = SourceSrtTitle
           End If

        Else
           If InStr(1, SourceSrtTitle, SrtXMLcode) > 0 Then             
              St = Mid$(LCase(SrtXMLcode), 2, 1): P1 = InStr(1, SourceSrtTitle, "f" & St & "="): Mid$(SourceSrtTitle, P1 + 3, 1) = "1"
              If LCase(SrtXMLcode) = "color=" Then
                 P1 = InStr(1, SourceSrtTitle, "color="): P2 = InStr(P1 + 6, SourceSrtTitle, ">")
                 CO = Mid$(SourceSrtTitle, P1 + 6, P2 - P1 - 6)
                 If InStr(1, CO, Chr$(34)) > 0 Then CO = "&H" & Mid$(CO, 2, Len(CO) - 2)
                 If InStr(1, CO, "#") > 0 Then CO = "&H" & Right$(CO, Len(CO) - InStr(1, CO, "#"))
                 P1 = InStr(1, SourceSrtTitle, "fc="): P2 = InStr(1, SourceSrtTitle, "][fp")
                 SourceSrtTitle = Left$(SourceSrtTitle, P1 + 2) & CO & Right$(SourceSrtTitle, Len(SourceSrtTitle) - P2 + 1)
              End If
              GetXMLCodeValue = SourceSrtTitle
           Else
              GetXMLCodeValue = SourceSrtTitle
           End If
        End If
       
End Function

 

'去掉所有特效代码
Private Function DeleEffectCode(ByVal SourceSrtTitle As String, ByVal StartCh As String, ByVal EndCh As String) As String
       
        Dim P1 As Long, P2 As Long, PS As Long
       
        On Error Resume Next
       
        PS = 1
        Do While InStr(PS, SourceSrtTitle, StartCh) > 0
           P1 = InStr(PS, SourceSrtTitle, StartCh)
           P2 = InStr(P1 + 1, SourceSrtTitle, EndCh)
           Mid$(SourceSrtTitle, P1, P2 - P1 + 1) = Space(P2 - P1 + 1)
           PS = P2 + 1
           DoEvents
        Loop
        DeleEffectCode = Trim(SourceSrtTitle)
      
End Function

你可能感兴趣的:(vb,string,insert,function,fp,input)