学习python也有1周了,因为之前学习了golang,所以觉得python和golang有很多类似的地方,比如切片和语法,python的确是一门很好的语言,至少你了解了之后才会发现其实它很强大,编写效率很高,至少目前为止我觉得运行效率也很高,之前没学过python2,所以跳过直接python3,既然是学习总得练练手吧,因为前段时间打算做一个抓直播地址的工具,截取网卡数据包的时候,接触过rtmp协议和flv协议,所以打算做一个flv文件解析库,其实也谈不上库,也就是对python如何解析flv协议做一个封装而已,因为这个类Flv在AMF数据的处理上也只处理了AMF0的常用数据,amf元数据在rtmp直播中也常用到,其他的元数据,没有demo我也没法测试
flv协议相信做过直播的童鞋都很清楚,本文也就大概的提一下,因为网上介绍的文章很多,就不详细的介绍了
FLV(Flash Video)是现在非常流行的流媒体格式,由于其视频文件体积轻巧、封装播放简单等特点,使其很适合在网络上进行应用,目前主流的视频网站无一例外地使用了FLV格式。另外由于当前浏览器与Flash Player紧密的结合,使得网页播放FLV视频轻而易举,也是FLV流行的原因之一。
FLV是流媒体封装格式,我们可以将其数据看为二进制字节流。总体上看,FLV包括文件头(File Header)和文件体(File Body)两部分,其中文件体由一系列的Tag及Tag Size对组成。
FLV格式解析
header
头部分由一下几部分组成
Signature(3 Byte)+Version(1 Byte)+Flags(1 Bypte)+DataOffset(4 Byte)
- signature 占3个字节
固定FLV三个字符作为标示。一般发现前三个字符为FLV时就认为他是flv文件。 - Version 占1个字节
标示FLV的版本号。这里我们看到是1 - Flags 占1个字节
内容标示。第0位和第2位,分别表示 video 与 audio 存在的情况.(1表示存在,0表示不存在)。截图看到是0x05
,也就是00000101
,代表既有视频,也有音频。 - DataOffset 4个字节
表示FLV的header长度。这里可以看到固定是9
body
FLV的body部分是由一系列的back-pointers + tag构成
- back-pointers 固定4个字节,表示前一个tag的size。
- tag 分三种类型,video、audio、scripts。
tag组成
tag type
+tag data size
+Timestamp
+TimestampExtended
+stream id
+ tag data
- type 1个字节。8为Audio,9为Video,18为scripts
- tag data size 3个字节。表示tag data的长度。从streamd id 后算起。
- Timestreamp 3个字节。时间戳
- TimestampExtended 1个字节。时间戳扩展字段
- stream id 3个字节。总是0
- tag data 数据部分
其实重点还是是数据部分,flv文件其实是有很多tag组合,常见的tag包含音频,视频,和脚本(元数据),脚本tag就是描述视频或音频的信息的数据,如宽度、高度、时间等等,一个文件中通常只有一个元数据, 音频tag和视频tag就是音视频信息了,采样、声道、频率,编码等信息,这都是基础的知识,关于协议网上很多介绍,下面直接上干货,之后打算学习下python通过ctypes封装调用c方面的知识,谁不想代码跑得快点了
flv.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = "mengdj@outlook.com"
import struct
import traceback
from enum import Enum, unique
"""flv文件解析,元数据解析采用amf0格式"""
@unique
class TagType(Enum):
"""标签类型"""
FLV_TAG_AUDIO = 0x08
FLV_TAG_VIDEO = 0x09
FLV_TAG_SCRIPT = 0x12
@unique
class Amf0DataType(Enum):
"""脚本中的变量类型(本程序支持的元数据类型)"""
FLV_AMF0_NUMBER = 0x00
FLV_AMF0_BOOLEAN = 0x01
FLV_AMF0_STRING = 0x02
FLV_AMF0_OBJECT = 0X03
FLV_AMF0_NULL = 0x05
FLV_AMF0_ARRAY = 0x08
FLV_AMF0_END_OF_OBJECT = 0x09
FLV_AMF0_STRICT_ARRAY = 0X0a
FLV_AMF0_DATE = 0X0b
FLV_AMF0_LONG_STRING = 0X0c
class UnSupportFileFormat(Exception):
pass
class UnSupportAmfValFormat(Exception):
pass
class Tag(object):
"""flv文件头"""
previousTagsSize = 0
type = 0
length = 0
timestamp = 0
exTimestamp = 0
streamsId = 0
# 原始数据
data = []
def parse(self):
"""请子类实现此方法来解析原始数据"""
pass
def __str__(self):
"""like tostring"""
return "%s previousTagsSize:%d type:%d length:%d timestamp:%d exTimestamp:%d streamsId:%d" % (
self.__class__, self.previousTagsSize, self.type, self.length, self.timestamp, self.exTimestamp,
self.streamsId)
def getBytes(self):
"""获取原始字节数据"""
return self.data
# end of class Tag
class AudioTag(Tag):