怎么看python的scripts_使用Python解析FLV文件的Scripts Tag

本文介绍了如何使用Python解析FLV文件中的Scripts Tag,详细解释了Scripts Tag的结构,并提供了解析Scripts Tag的代码示例,帮助理解每个值的含义。
摘要由CSDN通过智能技术生成

运行环境:Windows 10,Python 3.7.3

发布日期:2020 年 1 月 24 日

前言

为了拼接出一个没有瑕疵的 FLV 视频,仅修改音视频数据的时间戳还是不够的。为此,我们需要先对 FLV 视频的关键 Tag ——Scripts Tag 有一个较深的认识,然后便可以合理地修改 Scripts Tag。

解析FLV文件的Scripts Tag

首先,Scripts Tag 是由头部,主体和尾部组成,头部和尾部的结构十分简单,重点部分是主体。

正如 FLV 文件的数据是分块的,Scripts Tag 主体里的数据也是分块的。数据块有大有小,层层嵌套,无论怎么样变化,数据块的结构都可以表示为 A + (B) + C:

A:块的类型,1 字节,常见值有 0 (数字),1 (布尔值),2 (字符串),3 (对象),8 (字典),10 (数组);

B:字符串的长度( 2 字节)或数组的元素个数( 4 字节),不一定有,取决于块的类型;

C:块的数据。

Scripts Tag 主体通常只有 2 个数据块,主要信息都包括在第 2 个数据块中。

第 1 个数据块如下:

b'\x02\x00\nonMetaData'

A = \x02 = 2,这是个字符串数据块,B 存在,且 B 为 2 个字节

B = \x00\n = 10,表示字符串的长度为 10 字节

C = onMetaData

第 2 个数据块( C 很长,暂不列出):

b'\x08\x00\x00\x00\x1a' + C

A = \x08 = 8,这是个字典数据块,B 存在,且 B 为 4 个字节

B = \x00\x00\x00\x1a = 26,该值表示 C 含有 26 对键值

C:接下来解析

以 C 中含有的 2 个键值对作为解析示例:

b'\x00\x0bdescription\x02\x00\x12This is an example'

字典数据块中,因为每个键都是字符串,故其没有添加类型,前 2 个字节就直接表示字符串的长度,

\x00\x0b = 11,键 = description ,

接下来的字节则是键所对应的值,这是一个标准的数据块,

显而易见,A = \x02,B = \x00\x12,C = This is an example 。

--------------------------------------------------------------------------------------------

b'\x00\x08duration\x00@kd\xc4\x9b\xa5\xe3T'

同理,\x00\x08 = 8,键 = duration ,该键对应的值,是一个数字数据块,

A = \x00,B 不存在,C 为 8 字节 Double 数,C = @kd\xc4\x9b\xa5\xe3T = 219.149 。

在了解上述内容后,基本上就可以解析出 Scripts Tag 里的信息了。

完整代码

# author: Okery

# date: 2020.01.24

import struct

class Reader(): # 阅读器,可以使我们方便地读取数据

def __init__(self, content):

self.content = content

self.start = 0

self.eof = False

self.length = len(self.content)

def read(self, n=1):

if self.length > (self.start + n):

out = self.content[self.start:self.start + n]

self.start += n

else:

out = self.content[self.start:]

self.eof = True

return out

class ScriptsTag(Reader): # 解析 Scripts Tag 的类

def begin(self):

while not self.eof:

type_ = ord(self.read(1))

print(f' ', end='')

self.parse(type_)

def parse(self, type_, end='\n'): # print 函数里的空格只是为了规范化输出,不必深究

print(f' ({type_}) ', end='')

if type_ == 0: # number

value = struct.unpack('>d', self.read(8))[0] # 将 8 字节 Double 数转化成十进制数

print(value, end=end)

elif type_ == 1: # boolean 布尔数据块的结构为: A + C ( 1 字节)

value = ord(self.read(1))

print(value, end=end)

elif type_ == 2: # string

length = int.from_bytes(self.read(2), 'big')

value = self.read(length).decode()

print(value, end=end)

elif type_ == 3: # object 对象数据块的结构和字典数据块的相似

while not self.eof:

print('\n\t ', end='')

self.parse(2, '')

valueType = ord(self.read(1))

self.parse(valueType)

elif type_ == 9: # object end marker

value = self.read(0).decode()

print(value, end=end)

elif type_ == 8: # ecma array

numElements = int.from_bytes(self.read(4), 'big')

print(f'{numElements}')

for i in range(numElements):

keyLength = int.from_bytes(self.read(2), 'big')

key = self.read(keyLength).decode()

valueType = ord(self.read(1))

print(f' {key}:', end='')

self.parse(valueType)

elif type_ == 10: # strict array 数组数据块,其结构和字典数据块相似,只有值没有键

numElements = int.from_bytes(self.read(4), 'big')

print(f'{numElements}')

for i in range(numElements):

print(f'\t ', end='')

valueType = ord(self.read(1))

self.parse(valueType)

else:

print(f'Unresloved type {type_}')

def parse_flv(flv): # 主函数

with open(flv, 'rb') as f:

content = f.read()

reader = Reader(content)

header = reader.read(13)

while not reader.eof:

tagHeader = reader.read(11)

tagData = reader.read(int.from_bytes(tagHeader[1:4], 'big'))

tagSize = reader.read(4)

dataSize = int.from_bytes(tagHeader[1:4], 'big')

timeStamp = int.from_bytes(tagHeader[4:7], 'big')

# 判断 Tag 类型。注意,从字节串中截取的单个值为整数,故 tagHeader[0] 为 18,而不是 b'\x12'

if tagHeader[0] == 18:

tagType = 'scripts'

print(tagType, dataSize, timeStamp)

ScriptsTag(tagData).begin()

elif tagHeader[0] == 9:

tagType = 'video'

# print(tagType, dataSize, timeStamp)

elif tagHeader[0] == 8:

tagType = 'audio'

# print(tagType, dataSize, timeStamp)

else:

print('This may NOT be a .flv file!')

return

输出样例

样例很长,有大幅度删减。

scripts 2402 0

(2) onMetaData

(8) 26

hasVideo: (1) 1

duration: (0) 382.4

datasize: (0) 45991684.0

videosize: (0) 41086520.0

framerate: (0) 23.981254347746496

videodatarate: (0) 837.3441177432009

width: (0) 1280.0

height: (0) 720.0

......

keyframes: (3)

(2) filepositions (10) 99

(0) 2430.0

(0) 2510.0

......

(0) 30690020.0

(0) 30828476.0

Scripts Tag 的数据解析后还是比较容易看出每个值的含义。在这里说一下, keyframes 表示的是关键帧,即我们观看视频快进时的每个时间节点。

如果我的文章对您有帮助,还请您点个赞,谢谢!

Python scripts是指用Python编写的源代码文件,其中包含了一系列Python语句和命令。这些脚本文件可以在Python解释器中执行,以实现特定的功能或完成特定的任务。在Windows系统上,可以使用Python解释器的可执行文件来运行Python脚本。Python解释器的可执行文件通常位于C:\PythonXX\python.exe,其中XX是Python的版本号,例如Python 3.9的解释器可执行文件位于C:\Python39\python.exe。要运行一个Python脚本,可以在命令行中输入python脚本的文件路径,然后按下回车键即可执行该脚本。另外,要使用一些特定的Python库或模块,可能需要在系统中安装相应的依赖库,例如python39.dll文件用于Python 3.9的解释器。如果你要运行一个Python脚本,首先需要安装Python解释器,然后可以直接运行或调用相应的脚本文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Python解释器的各个文件夹的含义](https://blog.csdn.net/m0_63668906/article/details/129737458)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Python安装与开发环境搭建(Python扩展库)](https://blog.csdn.net/hyh17808770899/article/details/105796618)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值