博客主页:https://blog.csdn.net/wkd_007
博客内容:嵌入式开发、Linux、C语言、C++、数据结构、音视频
本文内容:介绍libogg库,并给出ligogg库使用的例子
金句分享:子曰:不患人之不己知,患不知人也。——《论语·学而篇》。意思是,不要担心别人不了解自己,只需要担心自己不了解别人。
本文未经允许,不得转发!!!
相关文章:
1、RFC3533 :Ogg封装格式版本 0(The Ogg Encapsulation Format Version 0)
2、Ogg封装格式详解——包含Ogg封装过程、数据包(packet)、页(page)、段(segment)等
3、libogg库详解介绍以及使用——附带libogg库解析.opus文件的C源码
Ogg是一种多媒体容器格式,是Xiph.org多媒体编解码器的原生文件和流格式。与所有Xiph.org技术一样,它是一种开放的格式,任何人都可以免费使用。
libogg库包含创建、解码和处理ogg比特流的必要功能。最新版本稳定版本更新到1.3.4
,开发版本更新到1.3.5
,libogg库的下载链接:https://xiph.org/downloads/
本文下载的是libogg-1.3.5.tar.gz
。
$ uname -a
Linux ubuntu 4.4.0-128-generic #154~14.04.1-Ubuntu SMP Fri May 25 14:58:51 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.5 LTS
Release: 14.04
Codename: trusty
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.4' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4)
tar zxvf libogg-1.3.5.tar.gz
cd libogg-1.3.5/
./configure --prefix=`pwd`/result_gcc
make && make install
libogg库的所有结构体和函数都定义在 ogg.h 头文件中,并且大概分为5个部分:
头文件内容如下:
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: toplevel libogg include
********************************************************************/
#ifndef _OGG_H
#define _OGG_H
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
typedef struct {
void *iov_base;
size_t iov_len;
} ogg_iovec_t;
typedef struct {
long endbyte;
int endbit;
unsigned char *buffer;
unsigned char *ptr;
long storage;
} oggpack_buffer;
/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
typedef struct {
unsigned char *header;
long header_len;
unsigned char *body;
long body_len;
} ogg_page;
/* ogg_stream_state contains the current encode/decode state of a logical
Ogg bitstream **********************************************************/
typedef struct {
unsigned char *body_data; /* bytes from packet bodies */
long body_storage; /* storage elements allocated */
long body_fill; /* elements stored; fill mark */
long body_returned; /* elements of fill returned */
int *lacing_vals; /* The values that will go to the segment table */
ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
this way, but it is simple coupled to the
lacing fifo */
long lacing_storage;
long lacing_fill;
long lacing_packet;
long lacing_returned;
unsigned char header[282]; /* working space for header encode */
int header_fill;
int e_o_s; /* set when we have buffered the last packet in the
logical bitstream */
int b_o_s; /* set after we've written the initial page
of a logical bitstream */
long serialno;
long pageno;
ogg_int64_t packetno; /* sequence number for decode; the framing
knows where there's a hole in the data,
but we need coupling so that the codec
(which is in a separate abstraction
layer) also knows about the gap */
ogg_int64_t granulepos;
} ogg_stream_state;
/* ogg_packet is used to encapsulate the data and metadata belonging
to a single raw Ogg/Vorbis packet *************************************/
typedef struct {
unsigned char *packet;
long bytes;
long b_o_s;
long e_o_s;
ogg_int64_t granulepos;
ogg_int64_t packetno; /* sequence number for decode; the framing
knows where there's a hole in the data,
but we need coupling so that the codec
(which is in a separate abstraction
layer) also knows about the gap */
} ogg_packet;
typedef struct {
unsigned char *data;
int storage;
int fill;
int returned;
int unsynced;
int headerbytes;
int bodybytes;
} ogg_sync_state;
/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/
extern void oggpack_writeinit(oggpack_buffer *b);
extern int oggpack_writecheck(oggpack_buffer *b);
extern void oggpack_writetrunc(oggpack_buffer *b,long bits);
extern void oggpack_writealign(oggpack_buffer *b);
extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits);
extern void oggpack_reset(oggpack_buffer *b);
extern void oggpack_writeclear(oggpack_buffer *b);
extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits);
extern long oggpack_look(oggpack_buffer *b,int bits);
extern long oggpack_look1(oggpack_buffer *b);
extern void oggpack_adv(oggpack_buffer *b,int bits);
extern void oggpack_adv1(oggpack_buffer *b);
extern long oggpack_read(oggpack_buffer *b,int bits);
extern long oggpack_read1(oggpack_buffer *b);
extern long oggpack_bytes(oggpack_buffer *b);
extern long oggpack_bits(oggpack_buffer *b);
extern unsigned char *oggpack_get_buffer(oggpack_buffer *b);
extern void oggpackB_writeinit(oggpack_buffer *b);
extern int oggpackB_writecheck(oggpack_buffer *b);
extern void oggpackB_writetrunc(oggpack_buffer *b,long bits);
extern void oggpackB_writealign(oggpack_buffer *b);
extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits);
extern void oggpackB_reset(oggpack_buffer *b);
extern void oggpackB_writeclear(oggpack_buffer *b);
extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits);
extern long oggpackB_look(oggpack_buffer *b,int bits);
extern long oggpackB_look1(oggpack_buffer *b);
extern void oggpackB_adv(oggpack_buffer *b,int bits);
extern void oggpackB_adv1(oggpack_buffer *b);
extern long oggpackB_read(oggpack_buffer *b,int bits);
extern long oggpackB_read1(oggpack_buffer *b);
extern long oggpackB_bytes(oggpack_buffer *b);
extern long oggpackB_bits(oggpack_buffer *b);
extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b);
/* Ogg BITSTREAM PRIMITIVES: encoding **************************/
extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
extern int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov,
int count, long e_o_s, ogg_int64_t granulepos);
extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill);
extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_flush_fill(ogg_stream_state *os, ogg_page *og, int nfill);
/* Ogg BITSTREAM PRIMITIVES: decoding **************************/
extern int ogg_sync_init(ogg_sync_state *oy);
extern int ogg_sync_clear(ogg_sync_state *oy);
extern int ogg_sync_reset(ogg_sync_state *oy);
extern int ogg_sync_destroy(ogg_sync_state *oy);
extern int ogg_sync_check(ogg_sync_state *oy);
extern char *ogg_sync_buffer(ogg_sync_state *oy, long size);
extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes);
extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og);
extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op);
/* Ogg BITSTREAM PRIMITIVES: general ***************************/
extern int ogg_stream_init(ogg_stream_state *os,int serialno);
extern int ogg_stream_clear(ogg_stream_state *os);
extern int ogg_stream_reset(ogg_stream_state *os);
extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno);
extern int ogg_stream_destroy(ogg_stream_state *os);
extern int ogg_stream_check(ogg_stream_state *os);
extern int ogg_stream_eos(ogg_stream_state *os);
extern void ogg_page_checksum_set(ogg_page *og);
extern int ogg_page_version(const ogg_page *og);
extern int ogg_page_continued(const ogg_page *og);
extern int ogg_page_bos(const ogg_page *og);
extern int ogg_page_eos(const ogg_page *og);
extern ogg_int64_t ogg_page_granulepos(const ogg_page *og);
extern int ogg_page_serialno(const ogg_page *og);
extern long ogg_page_pageno(const ogg_page *og);
extern int ogg_page_packets(const ogg_page *og);
extern void ogg_packet_clear(ogg_packet *op);
#ifdef __cplusplus
}
#endif
#endif /* _OGG_H */
下面简单介绍在Ogg封装格式解码过程中需要用到的库函数,需要了解更多库函数解释的可以参考:https://xiph.org/ogg/doc/libogg/reference.html
int ogg_sync_init(ogg_sync_state *oy);
功能:将 ogg_sync_state *结构体 初始化为已知状态
参数:ogg_sync_state *
long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
功能: 将同步到比特流中的下一页,并返回关于我们前进或跳过了多少字节的信息。
返回值:
-n:跳过的字节;
0:页没准备好,需要更多的字节,且没跳过字节;
n:页的总字节数n
char *ogg_sync_buffer(ogg_sync_state *oy, long size);
功能:分配数据缓冲区
int ogg_sync_wrote(ogg_sync_state *oy, long bytes);
功能:告知添加了多少字节
int ogg_page_bos(const ogg_page *og);
功能:起始页返回1
//--------------------------------------
int ogg_page_eos(const ogg_page *og);
功能:结束页返回1
//--------------------------------------
ogg_int64_t ogg_page_granulepos(const ogg_page *og);
功能:获取页的 granule position
//--------------------------------------
int ogg_page_serialno(const ogg_page *og);
功能:获取流的序列号
//--------------------------------------
long ogg_page_pageno(const ogg_page *og);
功能:获取页的序号
//--------------------------------------
int ogg_page_packets(const ogg_page *og);
功能:获取页的的包(段segment)个数
int ogg_stream_init(ogg_stream_state *os,int serialno);
功能:初始化ogg_stream_state结构,并分配适当的内存以准备编码或解码
//--------------------------------------
int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
功能:将一个完整的页面添加到比特流中。
//--------------------------------------
int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
功能:数据已经被提交到ogg_stream_state之后。每次连续调用该函数都会返回从这些数据段构建的下一个完整数据包。
//--------------------------------------
int ogg_stream_clear(ogg_stream_state *os);
功能:这个函数清除并释放ogg_stream_state结构使用的内部内存,但不释放结构本身。
在同一个结构上多次调用ogg_stream_clear是安全的。
在 上篇文章 第五小节 ,笔者用C语言写了一个读取Ogg文件的测试程序,现在,这里使用ligogg
库再实现一个Ogg封装格式文件读取的程序。
解码程序步骤:
ogg_sync_init
初始化一个ogg_sync_state
结构体;ogg_stream_init
;ogg_stream_pagein
;ogg_stream_packetout
;程序使用的 48000Hz-s16le-1ch-ChengDu.opus ,是opus编码的Ogg封装文件。
程序编译需要了解前面编译好的ligogg库和头文件,参考:
gcc liboggDec.c ../../libogg-1.3.5/result_gcc/lib/libogg.a -I ../../libogg-1.3.5/result_gcc/include/
Ogg文件解码程序如下,虽然并没有完整解析出音频,但可以加深对libogg库的理解:
// ligoggDec.c
// gcc liboggDec.c ../../libogg-1.3.5/result_gcc/lib/libogg.a -I ../../libogg-1.3.5/result_gcc/include/
/*
* ogg_sync_init:将结构体初始化为已知状态
* ogg_sync_pageseek: 将同步到比特流中的下一页,并返回关于我们前进或跳过了多少字节的信息。
返回值:
-n:跳过的字节;
0:页没准备好,需要更多的字节,且没跳过字节;
n:页的总字节数n
* ogg_sync_buffer:分配数据缓冲区
* ogg_sync_wrote:告知添加了多少字节
* ogg_page_serialno:获取序列号
* ogg_page_bos:起始页
* ogg_page_eos:结束页
* ogg_page_pageno:获取页号
* ogg_page_granulepos:获取页 granule pos
* ogg_page_packets:获取该页 段 (segment)个数
* ogg_stream_init:初始化ogg_stream_state结构,并分配适当的内存以准备编码或解码
* ogg_stream_pagein:将一个完整的页面添加到比特流中。
* ogg_stream_packetout:数据已经被提交到ogg_stream_state之后。每次连续调用该函数都会返回从这些数据段构建的下一个完整数据包。
* ogg_stream_clear:这个函数清除并释放ogg_stream_state结构使用的内部内存,但不释放结构本身。在同一个结构上多次调用ogg_stream_clear是安全的。
*/
#include
#include
#include
#include "ogg/ogg.h"
#define CHUNK 4096
ogg_sync_state g_ogsync;
ogg_page g_page;
// 获取下一页数据 2023-11-03 10:31:28
static int get_next_page(FILE *f, ogg_sync_state *ogsync, ogg_page *page,
ogg_int64_t *written)
{
int ret;
char *buffer;
size_t bytes;
while((ret = ogg_sync_pageseek(ogsync, page)) <= 0) {
if(ret < 0) {
/* unsynced, we jump over bytes to a possible capture - we don't need to read more just yet */
printf("WARNING: Hole in data (%d bytes) found at approximate offset %" PRId64 " bytes. Corrupted Ogg.\n", -ret, *written);
continue;
}
/* zero return, we didn't have enough data to find a whole page, read */
buffer = ogg_sync_buffer(ogsync, CHUNK);
bytes = fread(buffer, 1, CHUNK, f);
if(bytes == 0) {
ogg_sync_wrote(ogsync, 0);
return 0;
}
ogg_sync_wrote(ogsync, (long)bytes);
*written += bytes;
}
return 1;
}
int main()
{
FILE *file = fopen("48000Hz-s16le-1ch-ChengDu.opus", "rb");
if(!file) {
printf("fopen error\n");
return -1;
}
ogg_sync_init(&g_ogsync);
ogg_int64_t written;
while (get_next_page(file, &g_ogsync, &g_page, &written))
{
int packets = ogg_page_packets(&g_page);
printf("page_num:%03lu; ",ogg_page_pageno(&g_page));
printf("Oggs:%c %c %c %c; ",g_page.header[0],g_page.header[1],g_page.header[2],g_page.header[3]);
printf("type=%d, granule_position:%08lld, seg_num=%d; ", g_page.header[5],(long long)ogg_page_granulepos(&g_page), packets);
// 准备bit流
ogg_stream_state streamState;
ogg_packet packet;
ogg_stream_init(&streamState, ogg_page_serialno(&g_page)); // 给定一个流序列号,初始化 streamState
ogg_stream_pagein(&streamState, &g_page); // 将页数据给到 比特流
int i = 0;
for(i=0; i<packets; i++)
{
ogg_stream_packetout(&streamState, &packet);
if(packet.bytes >= 19 && memcmp(packet.packet, "OpusHead", 8)==0)
{
printf("OpusHead; ");
}
}
printf("\n");
ogg_stream_clear(&streamState);
}
return 0;
}
Ogg编码程序,这里就不给了,需要的话,可以去看看opus-tool工具的源码。
本文介绍了ligogg-1.3.5库的下载、编译,以及个别库函数的用法,最后给出一个Ogg封装格式的解码程序代码。
如果文章有帮助的话,点赞、收藏⭐,支持一波,谢谢