OGG文件的数据结构以及读取其注释信息的代码

2024-03-19 23:58

本文主要是介绍OGG文件的数据结构以及读取其注释信息的代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

OGG文件的数据结构以及读取其注释信息的代码

 

  笔者的手机原配的铃声都是ogg文件,所以笔者研究了一下这种文件的数据结构。
  Vorbis是一种有损音频压缩格式,通常以Ogg作为容器格式,所以常合称为Ogg Vorbis,所形成的文件后缀是Ogg。

 

一、OGG 文件的组织形式
  ogg文件解码后,按应用要求的时序关系合成若干物理流,一个物理流由若干逻辑流组成,一个逻辑流由若干包(Packet)组成。
  但ogg文件本身是由页(page)组成的,这样,在形成ogg文件的时候,就要将逻辑流的各个包分割为若干区段(segment)后再进行页封装,每页都加上页头。一个区段的长度最多为255字节,一页最多封装255个区段。如果几个包的总长度≤255个区段,那么这几个包的区段可以封装在一页;如果一个包长度>255个区段,那么就会被被封装成两页或多页,下一个包必须用新的页开始封装。
  OGG 文件的基本组织形式见表1:

 

表1 OGG 文件流的基本组织形式
------------------------------------------------------
A*  B*  C*  …  A#  B#  C#  D*   D#
------------------------------------------------------
bos  bos  bos    eos  eos  eos  bos  eos
------------------------------------------------------
说明:bos为开始流,eos为结束流。

 

  可以看出,文件链接了两个物理流,A、B和C三个逻辑流组成一个物理流,逻辑流D单独是一个物理流。一个物理流中的所有逻辑流的bos页都必须在物理位置上相邻,如表1所示*A*、*B*、*C*三个bos页的位置。  
  逻辑流包括有语音流、文本流、图片流、音频流、视频流等。


 

二、OGG 页结构
  每页之间相互独立,都包含了各自应有的信息,页的大小是可变的,通常为4-8KB,最大值不超过65307字节(27+255+255×255=65307)。页由页头部(pageheader)和页数据(pagedata)组成,页头部格式见表2。注:ogg文件中有关长度和大小的计算均使用小端字节序列格式。

 

表2 OGG 页结构
--------------------------------------------------------------------------------
域名称      占用字节  描述
--------------------------------------------------------------------------------
capture_pattern    4  页标识,"OggS"的ASCII字符 4F 67 67 53
structure_version   1  版本ID,当前版本默认=0
Header_type_flag   1  页头部类型
Granule_position   8  区段位置
Serial_number     4  逻辑流的序列号
Page_seguence_number 4  本页在逻辑流的序号,OGG解码器据此识别有无页丢失。
CRC_cbecksum     4  循环冗余校验码校验和
Number_page_segments 1  本页的区段数量,指明区段表中有多少个区段长度,≤255
Segment_table    ≤255 区段长度表,每个字节表示一个区段的长度
--------------------------------------------------------------------------------
  说明:
①页标识:标识着一个页的开始。其作用是分离Ogg封装格式还原媒体编码时识别新页。
②头部类型1字节8位值前3位的意义:
 第1位=1:本页的媒体编码数据与前一页属于同一个逻辑流的同一个包,=0:表示本页是新包。
 第2位=1:本页为逻辑流的第一页bos;=0:不是第一页。
 第3位=1:本页为逻辑流的最后一页eos;=0:不是最后一页。
③区段位置不是指区段在文件中的位置,而是指区段在逻辑流中的位置。它存储了媒体编码相关的参数信息,对于音频流来说,它存储着到本页为止逻辑流在PCM输出中采样码的数目,可以由它来算得时间戳。对于视频流来说,它存储着到本页为止视频帧编码的数目。若此值=0,表示截止到本页,逻辑流的包未结束。
④流序列号,即本页所在的流ID,它是区分本页所属逻辑流与其他逻辑流的序号,可以通过这个值来划分流。
⑤循环冗余校验码校验和包含页的32位CRC校验(头部零CRC校验、页数据校验)的产生多项式为:0x04c11db7。
⑥区段长度表记录着逻辑流中的每个包中每个区段的长度值,取值范围是0-255。包中的最后一个区段长度<255,其它区段长度都=255。这些值以区段出现的先后顺序排列。此域的字节数是区段数量域所表示的数字,即在0-255字节之间。从区段长度表中可以计算出每个包的长度,例如:区段表中的值为 4D FF 45 FF FF FF 40 FF FF 66,那么:
  第一包有1个区段,总长度=4D
  第二包有2个区段,总长度=FF+45
  第三包有4个区段,总长度=FF+FF+FF+40
  第四包有3个区段,总长度=FF+FF+66
⑦页头部的长度和整个页的长度计算:
 页头部长度=27+区段数量
 页长度=页头长度+区段长度表中每个区段的大小=页头部长度+所有区段长度之和。
⑧页头部后面紧接着页数据,页数据包括本页所有的区段数据。


 

三、OGG Vorbis 比特流的结构
  Ogg文件解码后形成比特流,比特流最前面是三个包头,按照在文件中的顺序依次是:标识头(identification header)、注释头(comment header)和装备头(Setup Header)。标识头设置了版本和流的简单音频特性(如采样率和声道数目等),注释头包括用户文本注释和供应商以及封装软件产生的字符串,装备头包括所需的解码器装备信息,以及完整的VQ和译码本。通常情况下,标识头分割在ogg文件第1页,注释头和装备头分割在ogg文件第2页,这些包头数据也就是所在页的页数据。从第3页开始的页数据才是真正的媒体流的压缩数据。三个包头的结构分别见表3、表4、表6。

 

表3 标识头结构
---------------------------------------------------------------------
域名称    占用字节  描述
---------------------------------------------------------------------  
header_type_flag  1 =1:包头类型为标识包
packet_pattern   6 =76 6F 72 62 69 73,包头标识,vorbis的Ascii码
vorbis_version   4 版本
audio_channels   1 声道数目,必须>0
audio_sample_rate 4 音频采样率,必须>0
bitrate_maximum  4 最大比特率
bitrate_nominal  4 标称比特率
bitrate_minimum  4 最小比特率
blocksize_0      块大小0: 占用4位,与blocksize_1共占用1字节
blocksize_1      块大小1: 占用4位,必须≥blocksize_0
framing_flag    1 =1,边界标志,表示标识头结束
---------------------------------------------------------------------
说明:
  ①比特率域仅作为提示。尤其是标称比特率,是纯粹VBR流,只有>0,该域才是有意义的。如果三个比特率域设置为相同的值,意味着固定速率比特流,或者有严格边界但接近固定速率的比特流。仅设置标称比特率意味着只有一个 VBR(可变位速率) 或 ABR(平均位速率) 流。设置最大或最小比特率意味着一个 VBR 比特流,遵守比特率限制。没有设置表明由编码器自行处理。
  ②块大小域不知为何意。


表4 注释头的结构
----------------------------------------------------------------------
域名称    占用字节  描述
----------------------------------------------------------------------
header_type_flag 1  =3:包头类型为注释包
packet_pattern  6  =76 6F 72 62 69 73,包头标识,vorbis 的Ascii码
companyinfolength 4  制作软件信息所占用的字节数
companyinfo      制作软件信息
retention_byte  4  保留字节
comment[1]_length 4  注释[1]字符串所占用的字节数
comment[1]       注释[1]内容
 ……
comment[N]长度  4  注释[N]字符串所占用的字节数
comment[N]       注释[N]内容
framing_flag   1  =1,边界标志,表示注释头结束
----------------------------------------------------------------------
说明:
  ①注释名称后面用等号连接注释内容。
  ②常用的注释名称见表5。
  ③注释名称是可以重复的。例如:如果一个曲目由三个艺术家共同演唱,那么以下情况是允许的:
ARTIST=张三
ARTIST=李四
ARTIST=王五


表5 常用注释名称
---------------------------------
注释名称    中译义
---------------------------------
ALBUM      专辑
ARTIST     艺术家
COPYRIGHT    版权
DATE      日期
DESCRIPTION   描述
GENRE      风格
CONTACT     联系人
ISRC      国际标准记录代码
LICENSE     许可证
LOCATION    声道位置
ORGANIZATION  公司名
PERFORMER    表演者
TITLE      标题
TRACKNUMBER   曲目号
TYER      年代
VERSION     版本
---------------------------------
说明:
  用户也可以自己创新注释名称。


表6 装备头的结构
----------------------------------------------------------------------------------------
域名称             占用字节 描述
----------------------------------------------------------------------------------------
header_type_flag           1  =5:包头类型为装备包
packet_pattern            6  =76 6F 72 62 69 73,包头标识,vorbis的Ascii码
lists of codebook configurations     码本结构列表
time-domain transform configurations   时间戳转换配置
floor configurations           底层配置
residue configurations          剩余配置
channel mapping configurations      信道映射的配置
mode configurations           模式配置
----------------------------------------------------------------------------------------
说明:
  装备头后面紧接着的就是真正的媒体压缩数据流了。


 

四、实例解析
  下面是我手机里的 Lock.ogg 的部分数据:
-------------------------------------------------------------------------
0000: 4F 67 67 53 00 02 00 00 00 00 00 00 00 00 82 78   OggS..........倄
0010: 00 00 00 00 00 00 12 85 4E 81 01 1E 01 76 6F 72   .......匩....vor
0020: 62 69 73 00 00 00 00 01 44 AC 00 00 FF FF FF FF   bis.....D.......
0030: 00 F4 01 00 FF FF FF FF B8 01 4F 67 67 53 00 00   ..........OggS..
0040: 00 00 00 00 00 00 00 00 82 78 00 00 01 00 00 00   ........倄......
0050: CC 63 C9 DB 0F 4D FF FF FF FF FF FF FF FF FF FF   蘡邵.M..........
0060: FF FF FF E8 03 76 6F 72 62 69 73 1D 00 00 00 58   .....vorbis....X
0070: 69 70 68 2E 4F 72 67 20 6C 69 62 56 6F 72 62 69   iph.Org libVorbi
0080: 73 20 49 20 32 30 30 34 30 36 32 39 01 00 00 00   s I 20040629....
0090: 1C 00 00 00 45 4E 43 4F 44 45 52 3D 41 64 6F 62   ....ENCODER=Adob
00A0: 65 28 52 29 20 41 75 64 69 74 69 6F 6E 28 52 29   e(R) Audition(R)
00B0: 01 05 76 6F 72 62 69 73 29 42 43 56 01 00 08 00   ..vorbis)BCV....
……
-------------------------------------------------------------------------
解析:
0000-0039:第一页
 0000-001B:页头部
  0000-0003=4F 67 67 53:页标识,OggS的Ascii字符
  0004=00:版本号为0
  0005=02:页头部类型:本页为逻辑流的第一页bos
  0006-000D=0:区段位置为0
  000E-0011=82 78 00 00:逻辑流ID
  0012-0015=0:本页在逻辑流中的序号为0
  0016-0019=12 85 4E 81:循环冗余校验码校验和
  001A=01:区段表中有1个区段
  001B=1E:区段表中的区段长度为 1E
 001C-0039:页数据(0039=1C+1E-1)
  001C=01:包头类型为标识包,包长度为1E(001C-0039),是区段表中区段的长度
  001D-0022=76 6F 72 62 69 73:包头标识,vorbis的Ascii码
  0023-0026=0:版本号为0
  0027=01:单声道
  0028-002B=44 AC 00 00:音频采样率为44.1KHZ(&HAC44=44100)
  002C-002F=FF FF FF FF:最大比特率未设置
  0030-0033=00 F4 01 00:标称比特率
  0034-0037=FF FF FF FF:最小比特率未设置
  0038=B8:块大小0为二进制的1011,块大小1为二进制的1000
  0039=01:标识包结束
003A-0E8B:第二页
 003A-0063:页头部
  003A-003D=4F 67 67 53:页标识,OggS的Ascii字符
  003E=00:版本号为0
  003F=00:页头部类型:本页为新包,不是逻辑流的第一页,也不是最后一页
  0040-0047=0:区段位置为0
  0048-004B=82 78 00 00:逻辑流ID
  004C-004F=01 00 00 00:本页在逻辑流中的序号为1
  0050-0053=CC 63 C9 DB:循环冗余校验码校验和
  0054=0F:区段表中有15个区段
  0055-0063=4D FF FF FF FF FF FF FF FF FF FF FF FF FF E8:区段表中15个区段的长度
 0064-0E8B:页数据(0E8B=64+4D+FF*D+E8-1)
  0064=03:包头类型为注释包,包长度为4D(0064-00B0),是区段表中第1个区段的长度
  0065-006A=76 6F 72 62 69 73:包头标识,vorbis的Ascii码
  006B-006E=1D 00 00 00:制作软件信息]的长度为29字节
  006F-008B=制作软件信息字符串:Xiph.Org libVorbis I 20040629
  008C-008F=01 00 00 00:保留字节
  0094-00AF=注释[1]字符串:ENCODER=Adobe(R) Audition(R)
  00B0=01:注释包结束
  00B1=05:包头类型为装备包
  00B2-00B7=76 6F 72 62 69 73:包头标识,vorbis的Ascii码
  ……


五、提取注释信息的代码

Private Sub Command1_Click()
On Error GoTo 100
Dim oggData() As Byte, teme() As Byte, i As Integer, k As Integer, tLen As Integer
Dim OpenName As String, z As String, st As String

OpenName="(全路径ogg文件名)"

ReDim oggData(FileLen(OpenName) - 1)
Open OpenName For Binary As #1
Get #1, , oggData

teme = StrConv("vorbis", vbFromUnicode)
k = InStrB(InStrB(oggData, teme) + 6, oggData, teme) - 1 '查找第2个vorbis位置
tLen = oggData(k + 6) - 1 '获取制作软件信息长度
k = k + 10
GoSub 200
st = "SOFTWAREinfo=" & z
k = k + 4

Do
  tLen = oggData(k) - 1    '获取注释长度
  If tLen = 0 Then Exit Do '如果是边界标志,退出
  k = k + 4
  GoSub 200
  st = st & vbCrLf & z
Loop Until k > 255

Text2 = st
100
Close
Exit Sub

200
ReDim teme(tLen)
For i = 0 To tLen: teme(i) = oggData(k): k = k + 1: Next
z = IIf(IsTextUTF8(teme), UTF_8ToTxt(teme), StrConv(teme, vbUnicode))
Return
End Sub

Private Function IsTextUTF8(bytSrc() As Byte) As Boolean '有的ogg文件的注释信息是UTF-8编码,必须加以判断
Dim i As Integer, AscN As Integer, n As Integer
n = UBound(bytSrc)
Do While i <= n
  If bytSrc(i) < 128 Then 'ascii字符
    i = i + 1: AscN = AscN + 1
  ElseIf (bytSrc(i) And &HF0) = &HE0 Then '3个字节的UTF-8
    If (bytSrc(i + 1) And &HC0) = &H80 Then
      If (bytSrc(i + 2) And &HC0) = &H80 Or (bytSrc(i + 2) And &HC0) = 0 Then i = i + 3 Else Exit Function
    Else
      Exit Function
    End If
  Else
    Exit Function
  End If
Loop
IsTextUTF8 = (AscN <> n + 1)
End Function

Private Function UTF_8ToTxt(bytSrc() As Byte) As String 'UTF_8编码转换为普通文本
On Error GoTo 100
Dim tem() As Byte, L As Integer, k As Integer, i As Integer
k = UBound(bytSrc)
ReDim tem(k * 2) As Byte
For i = 0 To k
  If bytSrc(i) < 128 Then
    tem(L) = bytSrc(i)
  Else
    tem(L + 1) = ((bytSrc(i) And 15) * 16 + (bytSrc(i + 1) And 60) / 4)
    tem(L) = (bytSrc(i + 1) And 3) * 64 + (bytSrc(i + 2) And 63)
    i = i + 2
  End If
  L = L + 2
Next
ReDim Preserve tem(L - 1) As Byte
UTF_8ToTxt = tem
100
End Function

这篇关于OGG文件的数据结构以及读取其注释信息的代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/827633

相关文章

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

python获取cmd环境变量值的实现代码

《python获取cmd环境变量值的实现代码》:本文主要介绍在Python中获取命令行(cmd)环境变量的值,可以使用标准库中的os模块,需要的朋友可以参考下... 前言全局说明在执行py过程中,总要使用到系统环境变量一、说明1.1 环境:Windows 11 家庭版 24H2 26100.4061

Python中文件读取操作漏洞深度解析与防护指南

《Python中文件读取操作漏洞深度解析与防护指南》在Web应用开发中,文件操作是最基础也最危险的功能之一,这篇文章将全面剖析Python环境中常见的文件读取漏洞类型,成因及防护方案,感兴趣的小伙伴可... 目录引言一、静态资源处理中的路径穿越漏洞1.1 典型漏洞场景1.2 os.path.join()的陷

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代