PctGL SERIES  
http://pctgl.cnblogs.com

 

这是核心解析代码的 1.0.0.0.0.0.0 版本,实现了数据提取,并没有任何代码优化和错误处理,属于逻辑验证过程

代码中可能含有未使用的参数,未使用的变量, 作为保留占位用

代码符合规范要求,对于标准格式的 m3u8 文件可以正确解析,提取,提取出的数据没有做保存

完整的代码待全部完善后再发,这是m3u8下载程序的一部分

 

 

m3u8_stream_load(UTF8数据流字节数组)

参数示例:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-KEY:METHOD=AES-128,URI="https://cdn.jkuyggfgb.cn/enc.key",IV=0x07f64b3f577ab4b1a7a832aabe9d9e77
#EXTINF:5.000000,
https://xxxt.com/short/8uyRPN36ykc%3D/001.ts
#EXTINF:5.000000,
https://xxxt.com/short/8uyRPN36ykc%3D/002.ts
#EXT-X-ENDLIST

 

m3u8_stream_load_node  (以#开头到下一个#之前的UTF8数据流字节数组,  节点输出缓存)

参数示例:#EXT-X-KEY:METHOD=AES-128,URI="https://cdn.jkuyggfgb.cn/enc.key",IV=0x07f64b3f577ab4b1a7a832aabe9d9e77

 

m3u8_stream_load_node_value (被提取出来的值的内容-UTF8数据流字节数组,  节点输出缓存)

参数示例:METHOD=AES-128,URI="https://cdn.jkuyggfgb.cn/enc.key",IV=0x07f64b3f577ab4b1a7a832aabe9d9e77

 

m3u8_stream_load_node_attribs (被提取出来的属性内容-UTF8数据流字节数组,  节点输出缓存)

参数示例:METHOD=AES-128

 

m3u8_stream_cutpart_clearnullchar    参数略。。。 

把输入的字节数组按照参数要求截取其中的一部分,在截取时一并把其中的的NullChar清除,返回一个新的字节数组

 

代码流程:

 

m3u8_stream_load  

         >    m3u8_stream_cutpart_clearnullchar   

                        >     m3u8_stream_load_node  

                                        >    m3u8_stream_load_node_value 

                                                       >    m3u8_stream_load_node_attribs 

 

 

M3U8 的文件格式说明:

'// HTTP Live Streaming
'// RFC8216
'// https://datatracker.ietf.org/doc/html/rfc8216

 

  1 Private Type m3u8_node_attribs
  2     Name                            As String               '// 属性名
  3     Value                           As String               '// 属性值
  4 End Type
  5 
  6 Private Type m3u8_node
  7     Title                           As String               '// 标签条目 名
  8     Value                           As String               '// 标签条目 值
  9     Attribs()                       As m3u8_node_attribs   '// 属性
 10     AttribCount                     As Long
 11 End Type
 12 
 13 
 14 Private Function m3u8_stream_cutpart_clearnullchar(mulBits() As Byte, ByVal cutFrom As Long, ByVal cutSize As Long, ByVal chrFlag As Byte) As Byte() '  String
 15     
 16     '// 从 mulBits 数组中,复制指定长度的字节数据,并同时把要复制数据的 NullChar 清除,组合成新的数组后返回
 17     
 18 '    Select Case chrFlag
 19 '           Case 10, 13, &HFF
 20 '           '//  所有非法及无意义字符将被转换为 0,标志设置为 &hFF, 除11/13外
 21 '                For i = 0 To cutSize - 1
 22 '                Next
 23 '    End Select
 24     
 25     ' 定义一个字节数组,用于存储处理后的字节数据
 26     Dim putBytes() As Byte
 27     
 28     ' 定义一个长整型变量,用于循环计数
 29     Dim i As Long
 30     
 31     If cutSize = 0 Then MsgBox "复制长度为 0 的错误,无法识别为 0 时应该返回什么内容" & vbCrLf & StrConv(mulBits, vbUnicode): Stop
 32     
 33     ' 重新调整 putBytes 数组的大小为 cutSize
 34     ReDim putBytes(cutSize - 1)
 35     
 36     ' 定义一些变量用于记录读写位置、复制的起始位置和复制的大小
 37     Dim rwPoint As Long
 38     Dim copyfrom As Long
 39     Dim copySize As Long
 40     Dim copyto   As Long
 41     
 42     copyto = cutFrom + cutSize - 1
 43     
 44     ' 将 copyfrom 初始化为 cutFrom
 45     copyfrom = cutFrom
 46     
 47     ' 循环遍历从 cutFrom 到 cutTo 的范围
 48     For i = cutFrom To cutFrom + cutSize - 1
 49         
 50 '        Debug.Print Chr(mulBits(i));
 51         
 52         ' 如果当前字节为 0
 53         If mulBits(i) = 0 Then
 54             
 55 '            flag_null_char = True
 56             
 57             ' 计算从 copyfrom 到当前位置的长度
 58             copySize = i - copyfrom
 59             
 60             ' 如果复制长度大于 0
 61             If copySize Then
 62             
 63                 ' 使用 CopyMemory 函数将 mulBits 数组中从 copyfrom 开始的 copySize 个字节复制到 putBytes 数组中从 rwPoint 位置开始的地方
 64                 CopyMemory putBytes(rwPoint), mulBits(copyfrom), copySize
 65                 
 66             End If
 67             
 68             ' 更新读写位置
 69             rwPoint = rwPoint + copySize
 70             
 71             ' 更新复制的起始位置为当前位置的下一个位置
 72             copyfrom = i + 1
 73             
 74         End If
 75     Next
 76     
 77     ' 计算最后一段的复制长度
 78     copySize = copyto - copyfrom + 1 '    cutFrom + cutSize - 1 - copyfrom
 79     
 80     ' 如果最后一段长度大于 0,将最后一段数据复制到 putBytes 数组中
 81     If copySize > 0 Then CopyMemory putBytes(rwPoint), mulBits(copyfrom), copySize
 82     
 83     ' 重新调整 putBytes 数组大小以保留实际使用的部分, 此时 rwpoint 读写点指向下一个可读写位置所以要 -1
 84     ReDim Preserve putBytes(rwPoint + copySize - 1)
 85     
 86     ' 使用 StrConv 函数将 putBytes 数组转换为 Unicode 字符串并返回
 87 '    m3u8_stream_cutpart_clearnullchar = StrConv(putBytes, vbUnicode)
 88     m3u8_stream_cutpart_clearnullchar = putBytes
 89     
 90 End Function
 91 
 92 
 93 
 94 Private Sub m3u8_stream_load_node_attribs(mulBits() As Byte, iopNode As m3u8_node)
 95     '// 识别是否具有属性
 96     '//     分析的数据示例:
 97     '//                         #EXT-X-KEY:METHOD=AES-128,URI=https://askbfcdn.com/20241025/maEwILYb/2000kb/hls/key.key,IV=0x00000000000000000000000000000000
 98     '//                                    ~~~~~~~~~~~~~~
 99     
100     Dim BoundOfArray            As Long
101     Dim iCount                  As Long
102     
103     Dim byteOfTitle()           As Byte
104     Dim strTitle                As String
105 
106     Dim byteOfValue()           As Byte
107     Dim strValue                As String
108     
109     BoundOfArray = UBound(mulBits)
110     
111     '// 如果属性内容的 首字符或末位字符为 = ,则此属性无效,直接忽略
112     If mulBits(0) = 61 Then Exit Sub
113     If mulBits(BoundOfArray) = 61 Then Exit Sub
114     
115     For iCount = 0 To BoundOfArray
116         
117         '// 查找 =
118         If mulBits(iCount) = 61 Then  '   "="
119                         
120             '// = 的两边分别为 属性名
121             ReDim byteOfTitle(iCount - 1)
122             CopyMemory byteOfTitle(0), mulBits(0), iCount
123             strTitle = StrConv(byteOfTitle, vbUnicode)
124             
125             '// 属性值
126             ReDim byteOfValue(BoundOfArray - iCount - 1)
127             CopyMemory byteOfValue(0), mulBits(iCount + 1), BoundOfArray - iCount
128             strValue = StrConv(byteOfValue, vbUnicode)
129             
130             '// 只查找内容的第一个 = ,再有 = 也只视为值的一部分
131             Exit For
132 
133         End If
134         
135     Next
136     
137     '// 此条件为真时表示没有找到 = ,则本次内容为无值属性
138     If iCount > BoundOfArray Then strTitle = StrConv(mulBits, vbUnicode)
139     
140     With iopNode
141     
142         ReDim Preserve .Attribs(.AttribCount)
143         
144         .Attribs(.AttribCount).Name = strTitle
145         .Attribs(.AttribCount).Value = strValue
146         .AttribCount = .AttribCount + 1
147                     
148     End With
149     
150     
151 End Sub
152 
153 Private Function m3u8_stream_load_node_value(mulBits() As Byte, iopNode As m3u8_node) As Long
154     
155     '// 识别是否具有属性
156     '//     分析的数据示例:
157     '//                         #EXT-X-KEY:METHOD=AES-128,URI="https://askbfcdn.com/20241025/maEwILYb/2000kb/hls/key.key",IV=0x00000000000000000000000000000000
158     '//                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159     
160     Dim BoundOfArray            As Long
161     Dim iCount                  As Long
162     
163     Dim flag_has_attribs        As Boolean
164 
165     Dim bytesAttr()             As Byte
166     
167     Dim char_last_pss           As Long         '// 上次 # 位置 + 1
168 '    Dim char_flag_pss           As Byte         '// 标识符
169     Dim char_now_pss            As Long         '// 本次 # 位置 - 1
170     Dim char_size_pss           As Long         '// 内容长度
171     
172     BoundOfArray = UBound(mulBits)
173     
174     For iCount = 0 To BoundOfArray
175         
176         Select Case mulBits(iCount)
177                Case 44  '   ","
178                     
179                     flag_has_attribs = True
180                     
181 '                    char_now_pss = iCount - 1
182                     
183                     bytesAttr = m3u8_stream_cutpart_clearnullchar(mulBits, char_last_pss, iCount - char_last_pss, 0)
184                     
185                     m3u8_stream_load_node_attribs bytesAttr, iopNode
186                     
187                     char_last_pss = iCount + 1
188                     
189                     
190                Case 34: mulBits(iCount) = 0 ' 双引号 "
191                     
192                Case 61: flag_has_attribs = True   ' "="         '//  ????????????  暂未启用的条件
193                     
194         End Select
195         
196     Next
197     
198     '// 如果值,没有属性时如何识别,如果值只有一个属性时,怎样区分: #EXT-X-KEY:METHOD=AES-128, #EXT-X-KEY:NONE 的区别
199     
200     If flag_has_attribs Then
201     
202         bytesAttr = m3u8_stream_cutpart_clearnullchar(mulBits, char_last_pss, BoundOfArray - char_last_pss + 1, 0)
203         
204         m3u8_stream_load_node_attribs bytesAttr, iopNode
205 
206     Else
207         iopNode.Value = StrConv(mulBits, vbUnicode)
208     
209     End If
210     
211 End Function
212 
213 Private Function m3u8_stream_load_node(mulBits() As Byte, iopNode As m3u8_node) As Long
214     
215     '//     分析的数据示例:    #EXT-X-MEDIA-SEQUENCE:0
216     '//                         #EXTINF:2.2,https://askbfcdn.com/20240926/FniAhBPw/1000kb/hls/6mscWE8g.ts
217     '//                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
218     
219     Debug.Print StrConv(mulBits, vbUnicode)
220     
221     Dim BoundOfArray            As Long
222     Dim iCount                  As Long
223     
224     Dim byteOfTitle()           As Byte
225     Dim strTitle                As String
226     
227     Dim byteOfValue()           As Byte
228     Dim strValue                As String
229     
230     BoundOfArray = UBound(mulBits)
231     
232     If mulBits(0) = 58 Then Exit Function               '// #: 形式的视为无效条目, 冒号位置有问题
233     If mulBits(BoundOfArray) = 58 Then Exit Function    '// #xxxx: 形式的视为无效条目,冒号位置有问题,有标签没有值为错误语法
234     
235     For iCount = 0 To BoundOfArray
236         
237         Select Case mulBits(iCount)
238                Case 58  '   ":"
239                     
240                     
241                     If iCount < 4 Then MsgBox "不是节点标签"        '// 小于 #EXT 的 4 字节长度, 视为注释
242                     
243                     
244                     '// 检测冒号(:) 获取条目名
245                     ReDim byteOfTitle(iCount - 1)
246                     CopyMemory byteOfTitle(0), mulBits(0), iCount
247                     
248 '                    Dim testFlags       As Long
249 '                    CopyMemory testFlags, byteOfTitle(0), 4
250 '                    If testFlags <> &H54584523 Then MsgBox "属于注释行"       '// 名字不符合规范, 不是 #EXT 开头的, 视为注释
251                     
252                     '// 此时应检查 byteoftitle 前 4 个字节是否为 #EXT, &H54584523
253                     
254                     
255                     '// 存储条目名
256                     strTitle = StrConv(byteOfTitle, vbUnicode)
257                     
258                     
259                     '// 提取节点的值的部分
260                     ReDim byteOfValue(BoundOfArray - iCount - 1)
261                     CopyMemory byteOfValue(0), mulBits(iCount + 1), BoundOfArray - iCount
262                     
263                     strValue = StrConv(byteOfValue, vbUnicode)
264                     
265                     
266                     
267                     '// 分析节点值的部分,获取到的结果放到 NewNode
268                     Call m3u8_stream_load_node_value(byteOfValue, iopNode)
269                     
270                     
271                     '//
272                     m3u8_stream_load_node = 1
273                     
274                     '// 第 1 个冒号后面的数据,全当做值处理,不再继续分析,后续数据已经过 属性分析
275                     Exit For
276                
277         End Select
278         
279     Next
280     
281     '// 存在没有值的独立节点条目
282     If iCount > BoundOfArray Then strTitle = StrConv(mulBits, vbUnicode): m3u8_stream_load_node = 1
283         
284     iopNode.Title = strTitle
285     iopNode.Value = strValue
286     
287 '''''''''    '// 对新的 node 条目节点, 数据归集
288 '''''''''    Select Case strTitle
289 '''''''''           Case m3u8_node_EXTM3U: PG.m3u8_format.SymbolBegin = strTitle
290 ''''''''''           Case m3u8_node_EXT_X_ENDLIST
291 ''''''''''           Case m3u8_node_EXT_X_KEY
292 '''''''''           Case m3u8_node_EXTINF: PG.m3u8_format.SymbolEnd = strTitle
293 '''''''''           Case Else
294 '''''''''                Me.Add strTitle, NewNode.Value
295 '''''''''
296 '''''''''
297 '''''''''    End Select
298     
299     
300 End Function
301 
302 Private Function m3u8_stream_load(mulBits() As Byte) As Long
303     
304     '// 解析 m3u8 文件数据, mulUnicodeBits = 字节数组, 该数组必须是 utf-8 类型, 函数会对数据核对,不符合标准要求的视为错误
305     '// 错误 = m3u8_stream_load ( utf8字节数组 )
306      
307     Dim NewNode                 As m3u8_node
308      
309     Dim SizeOfArray             As Long
310     Dim iCount                  As Long
311     
312     Dim arrayNode()             As Byte
313     
314     Dim char_last_pss           As Long         '// 上次 # 位置 + 1
315     Dim char_flag_pss           As Byte         '// 标识符
316 '    Dim char_now_pss            As Long         '// 本次 # 位置 - 1
317     Dim char_size_pss           As Long         '// 内容长度
318     Dim char_null_pss           As Long         '// 空字符
319     
320     '// 双引号三态标志, 用于在检测 # 号时, 如果存在被双引号包括的 #,应该被识别为一个文本
321 '    Dim flags_doubleQuotation   As flags_three_states
322     Dim flags_doubleQuotation   As Boolean
323     
324     SizeOfArray = UBound(mulBits)
325     
326     
327     '// 数据整理, 将 CR、LF、0 to &H1F、&H7F To &H9F ,全部删除
328     '// 整理后的数据没有分行,数据都位于 # 开头的节点之内
329     '// 然后进行 行数据 分析
330     
331     For iCount = 0 To SizeOfArray
332         
333         Select Case mulBits(iCount)
334                Case 35  '   "#"
335                     
336                     '// 检测双引号状态,当处于不配对双引号状态时, 说明此时 # 处于文本描述过程中
337                     If (flags_doubleQuotation = False) Then
338                     
339                         '// 设置字节标志
340                         char_flag_pss = 35
341                         
342                         '// 如果未设置过复制起始位置
343                         If char_last_pss = 0 Then
344                             
345 '                            // 设置起始点
346                             char_last_pss = iCount + 1
347                             
348                         Else
349                         
350 LastLoop:
351                             arrayNode = m3u8_stream_cutpart_clearnullchar(mulBits, char_last_pss, iCount - char_last_pss, char_flag_pss)
352                             
353                             '// 将获取的有效数据文本 提交处理
354                             
355                             '//     此时取得的结果为:  #EXT-X-MEDIA-SEQUENCE:0
356                             '//                         #EXTINF:2.2,https://askbfcdn.com/20240926/FniAhBPw/1000kb/hls/6mscWE8g.ts
357                             
358                             
359                             If m3u8_stream_load_node(arrayNode, NewNode) Then
360                                 '//
361                                 
362                                 Dim a As Long
363                                 Dim n As String
364                                 If NewNode.AttribCount Then
365                                     For a = 0 To NewNode.AttribCount - 1
366                                         n = n & "{" & a & "} [" & NewNode.Attribs(a).Name & "]" & "  <>  " & "[" & NewNode.Attribs(a).Value & "]" & vbCrLf
367                                     Next
368                                 End If
369                                 
370                                 SendMessage Form1.Text2.hWnd, EM_SETSEL, ByVal Len(Form1.Text2), ByVal Len(Form1.Text2)
371                                 
372                                 
373 '                                Form1.Text2.SelStart = Len(Form1.Text2)
374 '                                Form1.Text2.SelLength = 1
375                                 Form1.Text2.SelText = "<" & NewNode.Title & ">" & _
376                                                       "<" & NewNode.Value & ">" & _
377                                                       "   arrribs=" & NewNode.AttribCount & vbCrLf & _
378                                                           n & vbCrLf
379                                 
380                                 Erase NewNode.Attribs
381                                 NewNode.AttribCount = 0
382                                 n = ""
383 '                                NewNode.Title
384                                 
385                                 
386                             Else
387                                 '// raise error
388                                 
389                                 
390                                 
391                             End If
392                             
393                             '// 以上分析完成后, 从新设置新的复制点
394                             char_last_pss = iCount + 1
395                             char_flag_pss = 35
396                             
397                         End If
398                     
399                     End If
400                     
401                Case 34: flags_doubleQuotation = Not flags_doubleQuotation   '// 双引号配对状态检测, =true不配对     'flags_three_states_Transformation flags_doubleQuotation          '// 三态变换
402                
403                
404 '               Case 58  '   ":" 冒号
405 '
406 '               Case 59  '   ";" 分号
407                
408                Case 10, 13: mulBits(iCount) = 0                             '// 把 cr/lf 字符转换为空字符,在提取数据前将空字符排除
409                     
410                Case 1 To &H1F, &H7F To &H9F: mulBits(iCount) = 0            '// 在 m3u8 定义中,这些都属于不应该出现的字符
411                     '// 禁止出现的字符
412                
413                Case Else: If iCount = SizeOfArray Then GoTo LastLoop   '// 最后一段字符时
414                     
415         End Select
416         
417         
418         
419     Next
420     
421     
422     
423     
424     
425     
426 End Function

 

posted on 2024-11-02 13:26  PctGL  阅读(15)  评论(0编辑  收藏  举报