oggのページヘッダを表示してみる

某ゲームのBGMを切り出すついでに、oggフォーマットについて調べてみたのでほんの少しだけメモ。

  • OggフォーマットはRFC3533に記述されてる
  • ページと呼ばれる単位に分かれている
  • ページが連なってひとつのストリームになっている
  • 開始ページ(bos = begining of stream)で始まって、終了ページ(eos = end of stream)で終わる
  • header_typeはbitで管理されている
  • 開始ページは0x02の位置のbitがsetされているページ
  • 終了ページは0x04の位置のbitがsetされているページ
  • bitstream_serial_numberはストリームに固有の番号が割り振られている
  • page_sequence_numberはページの欠損・順序の識別のためのシーケンス番号


以下、oggページヘッダを表示するスクリプト

#!/usr/bin/perl
# print_ogg_header.pl, v 0.1, hiyokomanjiu, 2010/7/19
use strict;
use warnings;

my $filename = $ARGV[0] if $#ARGV >= 0;
open( my $oggfile, $filename ) or die "Can't open file " . $ARGV[0];
binmode $oggfile;

my $buf;
while( read( $oggfile, $buf, 4 ) ) {
    if( $buf =~ /OggS/ ) {
        my $capture_pattern = $buf;
        my ($version, $header_type, $granule_position,
                $bitstream_serial_number, $page_sequence_number, $crc_checksum,
                $number_page_segments, $segment_table, $stream_data);
        read( $oggfile, $version, 1 );
        read( $oggfile, $header_type, 1 );
        read( $oggfile, $granule_position, 8 );
        read( $oggfile, $bitstream_serial_number, 4 );
        read( $oggfile, $page_sequence_number, 4 );
        read( $oggfile, $crc_checksum, 4 );
        read( $oggfile, $number_page_segments, 1 );
        read( $oggfile, $segment_table, unpack("C", $number_page_segments) );
        my $data_size = 0;
        for( my $i = 0; $i < unpack("C", $number_page_segments); $i++ ) {
            $data_size += unpack("C", substr($segment_table, $i));
        }
        read( $oggfile, $stream_data, $data_size );

        printf( "version: %d\n", unpack("C", $version) );
        printf( "header type: %02X\n", unpack("C", $header_type) );
        printf( "granule position: %d\n", unpack("i", $granule_position) );
        printf( "bitstream serial number: %d\n", unpack("i", $bitstream_serial_number) );
        printf( "page sequence number: %d\n", unpack("i", $page_sequence_number) );
        printf( "number page segments: %d\n", unpack("C", $number_page_segments) );
        printf( "data_size: %d\n\n", $data_size );
    }else{
        die "This file isn't ogg format\n";
    }
}
close $oggfile;