対策としては、ANDROID_LOOP=trueのoggファイルを再生するときには、getDuration()で得た再生時間(ms)経過後にMediaPlayerの再生を停止するしかないようです。
問題はどうやってANDROID_LOOP=trueのoggファイルか判定するかです。
まずANDROID_LOOP=trueメタデータ付きのoggファイルをバイナリエディタで開いてみました。すると文字列で"ANDROID_LOOP=true"を確認することができます。
ちょっと強引な方法ですが、oggファイルを読み込んでその中に文字列"ANDROID_LOOP=true"が含まれているか確認すれば判定できそうです。
いくつかのoggファイルを確認したところ、"ANDROID_LOOP=true"が存在する場所はファイルによって異なっていたため、読み込むバイト数はあらかじめきめられません。ただしいずれのoggファイルも先頭付近にありましたので、とりあえず256バイト読み込んで判定してみます。
static public boolean isAndroidLoop(InputStream stream) { try { byte[] data = new byte[256]; stream.read(data); String text = new String(data); return text.contains("ANDROID_LOOP=true"); } catch (IOException e) { e.printStackTrace(); return false; } }
手持ちのoggファイルをいくつか試してみましたが、これで一応、ANDROID_LOOP=trueかどうか判定することができました。でも本当にこれでいいんでしょうか。
- ANDROID_LOOP=trueが256バイト以降にあったら正しく判定できない
- oggファイルのサイズが256バイト以下の小さいファイルであったら正しく判定できない
- そもそもこのやり方って正しいの?
- oggファイルは音声専用のファイルフォーマットではなく、movやaviのように動画や音声など複数のデータをまとめて格納するためのコンテナファイル
- oggファイルはページという単位でデータを格納する
本家のogg仕様
Mitsugu Oyama のソフトウェア倉庫
まず1ページ分のbyte配列を読み込む関数を作成します。
public static byte[] readPageBytes(InputStream stream) { try { DataInputStream dis = new DataInputStream(stream); // 読み込めなかったらデータの終わりと判断する byte[] capture_pattern = new byte[4]; if (dis.read(capture_pattern) == -1) return new byte[0]; if (capture_pattern[0]!=0x4f || capture_pattern[1]!=0x67 || capture_pattern[2]!=0x67 || capture_pattern[3]!=0x53) return null; byte[] header = new byte[22]; dis.read(header); byte page_segments = dis.readByte(); int segment_data_bytes = 0; byte[] segment_table = new byte[page_segments]; int segment_table_int[] = new int[page_segments]; for(int i=0; i<page_segments; i++) { segment_table[i] = dis.readByte(); segment_table_int[i] = segment_table[i] & 0xFF; //JavaではByteは符号付き segment_data_bytes += segment_table_int[i]; } byte[][] segment_data = new byte[page_segments][]; for(int i=0; i<page_segments; i++) { segment_data[i] = new byte[segment_table_int[i]]; dis.read(segment_data[i]); } int result_index = 0; int result_bytes = 4 + 22 + 1 + page_segments + segment_data_bytes; byte[] result = new byte[result_bytes]; System.arraycopy(capture_pattern, 0, result, result_index, 4); result_index += 4; System.arraycopy(header, 0, result, result_index, 22); result_index += 22; result[result_index] = page_segments; result_index += 1; System.arraycopy(segment_table, 0, result, result_index, page_segments); result_index += page_segments; for(int i = 0; i<page_segments; i++) { System.arraycopy(segment_data[i], 0, result, result_index, segment_table_int[i]); result_index += segment_table_int[i]; } return result; } catch (IOException e) { e.printStackTrace(); return null; } }
この関数を以下のように 呼び出します。
static public boolean isAndroidLoop(InputStream stream) { while(true) { byte[] raw_page = readPageBytes(stream); if (raw_page == null) break; if (raw_page.length == 0) break; String text = new String(raw_page); if (text.contains("ANDROID_LOOP=true")) { return true; } } return false; }
0 件のコメント:
コメントを投稿