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 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)

《使用Python从PPT文档中提取图片和图片信息(如坐标、宽度和高度等)》PPT是一种高效的信息展示工具,广泛应用于教育、商务和设计等多个领域,PPT文档中常常包含丰富的图片内容,这些图片不仅提升了... 目录一、引言二、环境与工具三、python 提取PPT背景图片3.1 提取幻灯片背景图片3.2 提取

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

基于Python实现读取嵌套压缩包下文件的方法

《基于Python实现读取嵌套压缩包下文件的方法》工作中遇到的问题,需要用Python实现嵌套压缩包下文件读取,本文给大家介绍了详细的解决方法,并有相关的代码示例供大家参考,需要的朋友可以参考下... 目录思路完整代码代码优化思路打开外层zip压缩包并遍历文件:使用with zipfile.ZipFil

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注