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

相关文章

C#读取本地网络配置信息全攻略分享

《C#读取本地网络配置信息全攻略分享》在当今数字化时代,网络已深度融入我们生活与工作的方方面面,对于软件开发而言,掌握本地计算机的网络配置信息显得尤为关键,而在C#编程的世界里,我们又该如何巧妙地读取... 目录一、引言二、C# 读取本地网络配置信息的基础准备2.1 引入关键命名空间2.2 理解核心类与方法

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

SpringCloud集成AlloyDB的示例代码

《SpringCloud集成AlloyDB的示例代码》AlloyDB是GoogleCloud提供的一种高度可扩展、强性能的关系型数据库服务,它兼容PostgreSQL,并提供了更快的查询性能... 目录1.AlloyDBjavascript是什么?AlloyDB 的工作原理2.搭建测试环境3.代码工程1.

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

Java中ArrayList的8种浅拷贝方式示例代码

《Java中ArrayList的8种浅拷贝方式示例代码》:本文主要介绍Java中ArrayList的8种浅拷贝方式的相关资料,讲解了Java中ArrayList的浅拷贝概念,并详细分享了八种实现浅... 目录引言什么是浅拷贝?ArrayList 浅拷贝的重要性方法一:使用构造函数方法二:使用 addAll(

JAVA利用顺序表实现“杨辉三角”的思路及代码示例

《JAVA利用顺序表实现“杨辉三角”的思路及代码示例》杨辉三角形是中国古代数学的杰出研究成果之一,是我国北宋数学家贾宪于1050年首先发现并使用的,:本文主要介绍JAVA利用顺序表实现杨辉三角的思... 目录一:“杨辉三角”题目链接二:题解代码:三:题解思路:总结一:“杨辉三角”题目链接题目链接:点击这里

SpringBoot使用注解集成Redis缓存的示例代码

《SpringBoot使用注解集成Redis缓存的示例代码》:本文主要介绍在SpringBoot中使用注解集成Redis缓存的步骤,包括添加依赖、创建相关配置类、需要缓存数据的类(Tes... 目录一、创建 Caching 配置类二、创建需要缓存数据的类三、测试方法Spring Boot 熟悉后,集成一个外

轻松掌握python的dataclass让你的代码更简洁优雅

《轻松掌握python的dataclass让你的代码更简洁优雅》本文总结了几个我在使用Python的dataclass时常用的技巧,dataclass装饰器可以帮助我们简化数据类的定义过程,包括设置默... 目录1. 传统的类定义方式2. dataclass装饰器定义类2.1. 默认值2.2. 隐藏敏感信息

opencv实现像素统计的示例代码

《opencv实现像素统计的示例代码》本文介绍了OpenCV中统计图像像素信息的常用方法和函数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 统计像素值的基本信息2. 统计像素值的直方图3. 统计像素值的总和4. 统计非零像素的数量

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输