??博客主頁??:??https://blog.csdn.net/wkd_007??
??博客內(nèi)容??:??嵌入式開發(fā)、Linux、C語言、C++、數(shù)據(jù)結(jié)構(gòu)、音視頻??
??本文內(nèi)容??:??介紹libogg庫,并給出ligogg庫使用的例子??
??金句分享??:??子曰:不患人之不己知,患不知人也?!墩撜Z·學而篇》。意思是,不要擔心別人不了解自己,只需要擔心自己不了解別人。??
本文未經(jīng)允許,不得轉(zhuǎn)發(fā)?。?!
相關(guān)文章:
1、RFC3533 :Ogg封裝格式版本 0(The Ogg Encapsulation Format Version 0)
2、Ogg封裝格式詳解——包含Ogg封裝過程、數(shù)據(jù)包(packet)、頁(page)、段(segment)等
3、libogg庫詳解介紹以及使用——附帶libogg庫解析.opus文件的C源碼
4、RFC7845:Opus音頻編解碼器的Ogg封裝(Ogg Encapsulation for the Opus Audio Codec)
5、opus編解碼庫(opus-1.4)詳細介紹以及使用——附帶解碼示例代碼
6、opus編碼的Ogg封裝文件詳解
??一、libogg庫概述
Ogg是一種多媒體容器格式,是Xiph.org多媒體編解碼器的原生文件和流格式。與所有Xiph.org技術(shù)一樣,它是一種開放的格式,任何人都可以免費使用。
libogg庫包含創(chuàng)建、解碼和處理ogg比特流的必要功能。最新版本穩(wěn)定版本更新到1.3.4
,開發(fā)版本更新到1.3.5
,libogg庫的下載鏈接:https://xiph.org/downloads/
??二、libogg庫編譯
本文下載的是libogg-1.3.5.tar.gz
。
?2.1 編譯環(huán)境如下:
$ 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)
?2.2 libogg編譯
tar zxvf libogg-1.3.5.tar.gz
cd libogg-1.3.5/
./configure --prefix=`pwd`/result_gcc
make && make install
??三、libogg庫簡單介紹
?3.1 ogg.h 頭文件
libogg庫的所有結(jié)構(gòu)體和函數(shù)都定義在 ogg.h 頭文件中,并且大概分為5個部分:
- 1、結(jié)構(gòu)體;
- 2、比特流打包函數(shù);
- 3、Ogg編碼相關(guān)函數(shù);
- 4、Ogg解碼相關(guān)函數(shù);
- 5、通用函數(shù)。
頭文件內(nèi)容如下:
/********************************************************************
* *
* 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 <stddef.h>
#include <ogg/os_types.h>
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 */
?3.2 libogg 庫函數(shù)解析
下面簡單介紹在Ogg封裝格式解碼過程中需要用到的庫函數(shù),需要了解更多庫函數(shù)解釋的可以參考:https://xiph.org/ogg/doc/libogg/reference.html
int ogg_sync_init(ogg_sync_state *oy);
功能:將 ogg_sync_state *結(jié)構(gòu)體 初始化為已知狀態(tài)
參數(shù):ogg_sync_state *
long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
功能: 將同步到比特流中的下一頁,并返回關(guān)于我們前進或跳過了多少字節(jié)的信息。
返回值:
-n:跳過的字節(jié);
0:頁沒準備好,需要更多的字節(jié),且沒跳過字節(jié);
n:頁的總字節(jié)數(shù)n
char *ogg_sync_buffer(ogg_sync_state *oy, long size);
功能:分配數(shù)據(jù)緩沖區(qū)
int ogg_sync_wrote(ogg_sync_state *oy, long bytes);
功能:告知添加了多少字節(jié)
int ogg_page_bos(const ogg_page *og);
功能:起始頁返回1
//--------------------------------------
int ogg_page_eos(const ogg_page *og);
功能:結(jié)束頁返回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)個數(shù)
int ogg_stream_init(ogg_stream_state *os,int serialno);
功能:初始化ogg_stream_state結(jié)構(gòu),并分配適當?shù)膬?nèi)存以準備編碼或解碼
//--------------------------------------
int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
功能:將一個完整的頁面添加到比特流中。
//--------------------------------------
int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
功能:數(shù)據(jù)已經(jīng)被提交到ogg_stream_state之后。每次連續(xù)調(diào)用該函數(shù)都會返回從這些數(shù)據(jù)段構(gòu)建的下一個完整數(shù)據(jù)包。
//--------------------------------------
int ogg_stream_clear(ogg_stream_state *os);
功能:這個函數(shù)清除并釋放ogg_stream_state結(jié)構(gòu)使用的內(nèi)部內(nèi)存,但不釋放結(jié)構(gòu)本身。
在同一個結(jié)構(gòu)上多次調(diào)用ogg_stream_clear是安全的。
??四、Ogg封裝格式使用 libogg 庫解碼——C語言代碼
在 上篇文章 第五小節(jié) ,筆者用C語言寫了一個讀取Ogg文件的測試程序,現(xiàn)在,這里使用ligogg
庫再實現(xiàn)一個Ogg封裝格式文件讀取的程序。
解碼程序步驟:
- 1、打開文件;
- 2、調(diào)用
ogg_sync_init
初始化一個ogg_sync_state
結(jié)構(gòu)體; - 3、獲取一頁數(shù)據(jù),并處理;
- 3.1、根據(jù)序列號初始化一個比特流
ogg_stream_init
; - 3.2、把頁數(shù)據(jù)給到比特流
ogg_stream_pagein
; - 3.3、獲取數(shù)據(jù)包
ogg_stream_packetout
; - 3.4、釋放比特流;
- 3.1、根據(jù)序列號初始化一個比特流
- 4、關(guān)閉文件
程序使用的 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:將結(jié)構(gòu)體初始化為已知狀態(tài)
* ogg_sync_pageseek: 將同步到比特流中的下一頁,并返回關(guān)于我們前進或跳過了多少字節(jié)的信息。
返回值:
-n:跳過的字節(jié);
0:頁沒準備好,需要更多的字節(jié),且沒跳過字節(jié);
n:頁的總字節(jié)數(shù)n
* ogg_sync_buffer:分配數(shù)據(jù)緩沖區(qū)
* ogg_sync_wrote:告知添加了多少字節(jié)
* ogg_page_serialno:獲取序列號
* ogg_page_bos:起始頁
* ogg_page_eos:結(jié)束頁
* ogg_page_pageno:獲取頁號
* ogg_page_granulepos:獲取頁 granule pos
* ogg_page_packets:獲取該頁 段 (segment)個數(shù)
* ogg_stream_init:初始化ogg_stream_state結(jié)構(gòu),并分配適當?shù)膬?nèi)存以準備編碼或解碼
* ogg_stream_pagein:將一個完整的頁面添加到比特流中。
* ogg_stream_packetout:數(shù)據(jù)已經(jīng)被提交到ogg_stream_state之后。每次連續(xù)調(diào)用該函數(shù)都會返回從這些數(shù)據(jù)段構(gòu)建的下一個完整數(shù)據(jù)包。
* ogg_stream_clear:這個函數(shù)清除并釋放ogg_stream_state結(jié)構(gòu)使用的內(nèi)部內(nèi)存,但不釋放結(jié)構(gòu)本身。在同一個結(jié)構(gòu)上多次調(diào)用ogg_stream_clear是安全的。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ogg/ogg.h"
#define CHUNK 4096
ogg_sync_state g_ogsync;
ogg_page g_page;
// 獲取下一頁數(shù)據(jù) 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); // 將頁數(shù)據(jù)給到 比特流
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工具的源碼。
??五、總結(jié)
本文介紹了ligogg-1.3.5庫的下載、編譯,以及個別庫函數(shù)的用法,最后給出一個Ogg封裝格式的解碼程序代碼。文章來源:http://www.zghlxwxcb.cn/news/detail-744627.html
如果文章有幫助的話,點贊??、收藏?,支持一波,謝謝 ??????文章來源地址http://www.zghlxwxcb.cn/news/detail-744627.html
到了這里,關(guān)于【音視頻 | Ogg】libogg庫詳細介紹以及使用——附帶libogg庫解析.opus文件的C源碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!