Python实现FLV视频拼接功能

一颗松果
一颗松果
发布时间:2026-10-13 | 4人浏览过
原创

本文介绍如何使用Python拼接FLV视频。首先解析FLV文件头与Tag结构,包括Header的9字节定义及Script、Audio、Video Tag的组成。接着通过Python代码实现读取、修改时间戳并写入新文件,最终完成多个FLV文件的合并,并测试了拼接性能与潜在问题。

文章摘要

本文简要解析FLV文件格式,并基于此使用Python代码实现FLV视频的拼接操作。

FLV文件格式解析

一、FLV文件格式

网络上关于FLV格式解析的文章众多,此处仅介绍关键部分,帮助读者理解后续代码逻辑。

FLV文件由文件头(Header)和文件体(Body)顺序拼接而成。解析时需以二进制模式读取文件内容。

Header部分标识文件封装格式为FLV,并声明包含音频、视频或两者。以下为FLV文件Header结构,共占用9个字节:

b'FLV\x01\x05\x00\x00\x00\t' 前3字节(FLV)标识文件类型。 第4字节(\x01)为版本号,固定为1。 第5字节(\x05)表示存储对象类型,转换为二进制为00000101,其中左侧的1表示包含音频,右侧的1表示包含视频。 后4字节(\x00\x00\x00\t)表示文件头长度,固定值为9。

Body部分由多个Tag组成。除第一个Tag外,每个Tag包含头部(11字节)、主体(长度不定)和尾部(4字节)。第一个Tag仅包含尾部。

Tag分为脚本(scripts)、音频(audio)和视频(video)三类。通常第二个Tag为脚本类型,且仅有一个,后续Tag均为音视频类型。

以下以脚本Tag为例进行介绍:

头部示例:b'\x12\x00\tb\x00\x00\x00\x00\x00\x00\x00' 第1字节(\x12)表示Tag类型,脚本对应值18,音频为8,视频为9。 第2-4字节(\x00\tb)表示Tag主体长度,此处为2402。 第5-7字节(\x00\x00\x00)为时间戳,脚本类型通常固定为0。 第8字节(\x00)为时间戳扩展,当3字节不足时使用此字节作为大端序处理。 后3字节(\x00\x00\x00)为Stream ID,固定为0。

主体部分包含FLV视频的基本信息,如时长、大小、分辨率等,结构较为复杂,此处不作深入介绍。

尾部示例:b'\x00\x00\tm'

尾部固定为4字节,表示Tag头部与主体的总长度,即11 + 2402 = 2413。

FLV视频拼接实现

二、FLV视频拼接

将多个FLV视频合并为一个可正常播放的视频,通常可满足大部分需求。因此,拼接过程中无需对FLV进行精细调整,达到基本播放要求即可。

设置阅读器

阅读器类用于便捷地读取文件二进制内容。


class Reader():
  def __init__(self, content): # content (bytes):FLV文件的二进制内容
    self.content = content
    self.start = 0
    self.eof = False # 判断是否已读完全部内容
    self.length = len(self.content)
    
  def read(self, n=1):
    # 设置 if 语句防止过度读取内容
    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

向新建FLV文件写入Header和Tag

假设待拼接的视频基本信息相似,即均包含音视频,且分辨率、码率等参数相同或相近。

生成可正常播放的FLV视频,Header和Tag必不可少。策略是选取第一个FLV的文件头写入新文件,随后依次将修改时间戳后的Tag写入其中,从而实现拼接。


def add_flv(flv, target, videoTimeStamp, audioTimeStamp): # 修改并添加 Tag 的函数
  with open(flv, 'rb') as f:
    content = f.read()
  reader = Reader(content)
  header = reader.read(13)
  with open(target, 'ab') as f:
    while not reader.eof: # 一直读取直到读完,此时 reader.eof = True
      dataType = reader.read(1)
      dataSize = reader.read(3)
      timeStamp = int.from_bytes(reader.read(3), 'big') # 将 3 字节转换成整数
      headerRemained = reader.read(4)
      if dataType == b'\t': # 视频
        timeStamp += videoTimeStamp
        videoTS = timeStamp
      if dataType == b'\x08': # 音频
        timeStamp += audioTimeStamp
        audioTS = timeStamp
      timeStamp = timeStamp.to_bytes(3, 'big') # 将整数转换成 3 字节
      tagHeader = dataType + dataSize + timeStamp + headerRemained
      tagData_andSize = reader.read(int.from_bytes(dataSize, 'big') + 4)
      f.write(tagHeader)
      f.write(tagData_andSize)
  return videoTS, audioTS
def merge_flv(flvs, target): # 主函数
  videoTS = 0
  audioTS = 0
  for i, flv in enumerate(flvs):
    with open(flv, 'rb') as f:
      content = f.read()
    reader = Reader(content)
    
    header = reader.read(13) # flvHeader + tagSize0
    if i == 0: # 写入第 1 个FLV视频的文件头
      with open(target, 'wb') as f:
        f.write(header)
        
    videoTS, audioTS = add_flv(flv, target, videoTS, audioTS)

拼接实现


import time
since = time.time()
flvs = ['m1.flv', 'm2.flv', 'm3.flv', 'm4.flv'] # 视频大小:45MB,20MB,59MB,54MB
target = 't.flv'
merge_flv(flvs, target)
end = time.time()
print('Merging flvs takes {:.2f} s'.format(end - since))
# Merging flvs takes 0.88 s

测试显示,拼接4个总大小为178MB的视频耗时0.88秒。

总结

FLV文件格式相对简明,对数据容错性较高。即使未调整Scripts中的参数,拼接后的视频仍能正常播放。

然而,拼接视频存在潜在问题,例如视频末尾可能出现约0.5秒的音画不同步现象,且难以方便地分离出完整的视频和音频流。

以上为Python实现FLV视频拼接功能的介绍,希望对大家有所帮助。如有疑问请留言,小编会及时回复。感谢大家对本站的支持! 若本文对您有帮助,欢迎转载,请注明出处,谢谢!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表