言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

Ogg

概要

Ogg と libogg について書く。Ogg フォーマットについては http://en.wikipedia.org/wiki/Ogg に本家ドキュメントよりもわかりやすい説明が図入りで書いてあるので、そちら参照。Ogg Vorbis と言えば mp3 に変わる音楽フォーマットとして要注目なんだけど、Ogg それ自体はバイナリの情報を細切れにして保存するだけの物。次のような専門用語がある。

  • 物理ストリーム (physical bitstream)
    • ひとつの ogg ファイル(またはバイト列)の事
  • 論理ストリーム (logical bitstream)
    • ひとつの物理ストリームには複数の論理ストリームが入ってる場合がある。
    • ひとつの論理ストリームにひとつの音声や映像が入ってる。
    • 音声と映像をまとめたファイルを作成出来る。
  • ページ (page)
    • 物理ストリームは複数のページで出来ている。
    • ひとつのページはどれかの論理ストリームに属している。
  • パケット (packet)
    • ひとつのページには複数のパケットが入っている。
    • パケットは、ページをまたぐ事もある。
    • アプリケーションは、パケットごとにエンコード/デコードする。

libogg

Ogg の参照実装として、libogg が公開されている。libogg で使うおもな構造は以下の通り。

  • ogg_page
    • ひとつのページを表す。アクセス用関数はこんな感じ。
    • ogg_page_serialno(ogg_page* og) どの論理ストリームに属すかを決めるシリアル番 号。
    • ogg_page_pageno(ogg_page* og) ページの番号。
    • ogg_page_packets(ogg_page* og) このページにいくつパケットが入っているか。
  • ogg_packet
    • ひとつのパケット。メンバはこんな感じ。
    • packet データへのポインタ。
    • bytes データの大きさ。
    • granulepos コーデックによって使われるパケットの細かい位置
    • packetno パケットの番号。
  • ogg_sync_state
    • 物理ストリームからどこまでページを読み込んだか。
  • ogg_stream_state
    • 論理ストリームからどこまでパケットを読んだか。

サンプル

サンプルとして、ogg をダンプするプログラムを作ってみる。

/*
 * Ogg file dump program.
 * $ cc -Wall -g -o oggdump -logg oggdump.c
 */

#include <stdio.h>
#include <ogg/ogg.h>

#define BUFSIZE 4096

int main(){
  ogg_sync_state   sync_state; /* ページの同期状態 */
  ogg_stream_state stream_state; /* 論理ストリームの状態 */
  ogg_page         page; /* ページ */
  ogg_packet       packet; /* 生パケット */
  int eos= 0;
  int initialized = 0;

  ogg_sync_init(&sync_state); /* 同期状態の初期化 */

  while(!eos) {

    char * buffer= ogg_sync_buffer(&sync_state,BUFSIZE); /* 作業用バッファを取得する */
    int bytes= fread(buffer, 1, BUFSIZE, stdin); /* 標準入力から作業用バッファに書き込み */
    ogg_sync_wrote(&sync_state,bytes); /* ogg に渡したサイズを伝える */

    if (bytes == 0) eos= 1;

    int result= ogg_sync_pageout(&sync_state,&page); /* ページ取り出し */
    if (result == 0) continue; /* データ不足。さらに読み込み */
    printf("serialno = %i, pageno = %li, packets = %i\n",
           ogg_page_serialno(&page), ogg_page_pageno(&page), ogg_page_packets(&page));

    if (!initialized) {
      ogg_stream_init(&stream_state,ogg_page_serialno(&page)); /* 論理ストリームを初期化 */
      initialized = 1;
    }
    /* ページからパケットを取り出す */
    ogg_stream_pagein(&stream_state, &page); /* ページをパケットに分けておく */
    while(1) {
      int result= ogg_stream_packetout(&stream_state,&packet); /* パケットを取り出す */
      if (result == 0) break; /* もっとデータを読む */
      if (result < 0) continue; /* データ不足。さらに読み込み */
      printf("\tgranulepos = %lli, packetno = %lli, size = %li\n",
             packet.granulepos, packet.packetno, packet.bytes);
    }

    if(ogg_page_eos(&page)) eos= 1;
  }

  ogg_stream_clear(&stream_state);
  ogg_sync_clear(&sync_state);
  return 0;
}