/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.packet.mp3; import java.nio.ByteBuffer; import org.apache.log4j.Logger; import com.ttProject.packet.MediaPacket; /** * mp3の実体処理 * @author taktod */ public abstract class Mp3Packet extends MediaPacket { @SuppressWarnings("unused") private Logger logger = Logger.getLogger(Mp3Packet.class); /** 書き込みする状態かどうかフラグ(id3v2のタグは、スキップするので、そのときにこのフラグを確認します。) */ private boolean writeMode = false; /** 処理データ量 */ private int targetLength = 0; /** mp3の設定データ */ private int mpegVersion; private int layer; private int bitrate; private float samplingRate; private int paddingBit; @SuppressWarnings("unused") private int channelMode; // これだけ特に参照していない。 /** 開始位置 */ private int startPos = -1; /** 参照用のマネージャーオブジェクト */ private final Mp3PacketManager manager; /** * コンストラクタ * @param manager */ public Mp3Packet(Mp3PacketManager manager) { this.manager = manager; } /** * 解析を実施する。 */ @Override public boolean analize(ByteBuffer buffer) { while(buffer.remaining() >= 4) { int position = buffer.position(); if(targetLength != 0) { // 処理途上な場合の処理 // 指定された量のデータがない場合は、かけるだけ書いて、応答する。 if(writeMode) { // 書き込み要求 int length = targetLength > buffer.remaining() ? buffer.remaining() : targetLength; byte[] data = new byte[length]; buffer.get(data); getBuffer(length).put(data); targetLength -= length; } else { // スキップ要求 if(targetLength > buffer.remaining()) { targetLength -= buffer.remaining(); // targetLengthの大きくて今回の処理でおわらない場合 buffer.position(position + buffer.remaining()); } else { // targetLengthの方が小さい場合単にスキップすればよい。 buffer.position(position + targetLength); targetLength = 0; } } } else { // 4バイト以上あることを保証してあります。 byte[] readData = new byte[4]; buffer.get(readData); // この4バイトの中身がヘッダーであることを期待します。 if(readData[0] == 'I' && readData[1] == 'D' && readData[2] == '3') { buffer.position(position); // id3v2のタグであることが期待されます。 // このタイミングで、10バイト読み込めることを期待します。 if(buffer.remaining() < 10) { return false; } readData = new byte[10]; buffer.get(readData); int id3v2Length = ((readData[6] & 0x7F) << 21) + ((readData[7] & 0x7F) << 14) + ((readData[8] & 0x7F) << 7) + (readData[9] & 0x7F); // 書き込みをせずに、対象量を読み飛ばします。 targetLength = id3v2Length; writeMode = false; } else if(readData[0] == 'T' && readData[1] == 'A' && readData[2] == 'G') { // id3v1のタグの場合はファイルの終端である可能性が高いので、処理を終わらせます。 // logger.info("id3v1タグ"); // manager側にデータをおわった旨の登録が必要だと思われます。 // ここは例外ではなく、おわったという処理にすべきかも throw new RuntimeException("終端が見えたので処理を中止します。"); } else { // 通常のタグ if((readData[0] & 0xFF) != 0xFF || (readData[1] & 0xE0) != 0xE0) { // headerの確認bitがおかしいです。 throw new RuntimeException("headerのsyncビットがおかしいです。"); } // mpegAudio versionID switch((readData[1] & 0x18) >>> 3) { default: // 解釈できない。 throw new RuntimeException("解釈できないデータがversionIdにきました。"); case 0: // mpeg2.5 mpegVersion = 2; break; case 1: // reserved throw new RuntimeException("予約されているversionIdです。処理不能"); case 2: // mpeg2 mpegVersion = 1; break; case 3: // mpeg1 mpegVersion = 0; break; } // layer switch((readData[1] & 0x06) >> 1) { default: throw new RuntimeException("解釈できないデータがlayerにきました。"); case 0: throw new RuntimeException("予約されているlayerです。処理不能"); case 1: layer = 2; break; case 2: layer = 1; break; case 3: layer = 0; break; } // protectionbit(興味なし) // bitrateIndex bitrate = getBitrate((readData[2] & 0xF0) >>> 4); // samplingRate samplingRate = sampleRateTable[mpegVersion][(readData[2] & 0x0C) >>> 2]; // paddingBit paddingBit = (readData[2] & 0x02) >>> 1; // privateBit skip // channelMode channelMode = (readData[3] & 0xC0) >>> 6; // durationを更新 setDuration(getTime() / 1000 - manager.getPassedTime()); int passedTime = getPassedTime() / 1000; // この方法だと、分割につかう秒数がだめな感じになってしまう。(端数がちょっとずつずれていく。) // TODO 分割に利用するdurationのデータ取得だけ、なんとかしておかないとだめ。 if(passedTime >= manager.getDuration()) { // 経過時間が5秒すぎている場合は、次のパケットにすすむ(分割を実行する。) // 現在時刻から、manager上の経過時刻をひいてdurationを求める manager.addPassedTime(getDuration()); buffer.position(position); return true; } // 書き込みを実行するサイズを指定しておく。 targetLength = getSize() - 4; // frameCountをインクリメントしておく。 // 現在の経過時間を計算しておく。 manager.addFrameCount(); getBuffer(4).put(readData); writeMode = true; } } } return false; } /** * 経過時刻を取得する。 * @return */ private int getPassedTime() { if(startPos == -1) { startPos = getTime(); } return getTime() - startPos; } /** * パケットから現在時刻を取得する。 * @return */ private int getTime() { if(layer == 0) { // layer1 return (int)Math.floor(manager.getFrameCount() * 384 / samplingRate); } else if(layer == 1) { // layer2 return (int)Math.floor(manager.getFrameCount() * 1152 / samplingRate); } else if(layer == 2) { // layer3 if(mpegVersion == 0) { return (int)Math.floor(manager.getFrameCount() * 1152 / samplingRate); } else { return (int)Math.floor(manager.getFrameCount() * 576 / samplingRate); } } return -1; } /** * パケットのサイズを計算する。 * TODO layer1の動作は自信なし * @return */ private int getSize() { if(layer == 0) { // layer1 return (int)Math.floor((12 * bitrate / samplingRate + paddingBit) * 4); } else if(layer == 1) { // layer2 return (int)Math.floor(144 * bitrate / samplingRate + paddingBit); } else if(layer == 2) { // layer3 if(mpegVersion == 0) { return (int)Math.floor(144 * bitrate / samplingRate + paddingBit); } else { return (int)Math.floor(72 * bitrate / samplingRate + paddingBit); } } return -1; } /** * 変換テーブル */ public static final float sampleRateTable[][] = { {44.1f, 48.0f, 32.0f}, // mpeg 1 {22.05f, 24.0f, 16.0f}, // mpeg 2 {11.025f, 12.0f, 8.0f} // mpeg 2.5 }; /** * ビットレートを取得する * @param index * @return */ public int getBitrate(int index) { if(mpegVersion == 2 || mpegVersion == 1) { // 2.5と2の場合 if(layer == 0) { // layer1 return bitrateIndexV2L1[index]; } else if(layer == 1 || layer == 2) { // layer2,3 return bitrateIndexV2L23[index]; } } if(mpegVersion == 0) { // 1の場合 if(layer == 2) { // layer3 return bitrateIndexV1L3[index]; } else if(layer == 1) { // layer2 return bitrateIndexV1L2[index]; } else if(layer == 0) { // layer1 return bitrateIndexV1L1[index]; } } return -1; } public static final int bitrateIndexV1L1[] = { -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }; public static final int bitrateIndexV1L2[] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }; public static final int bitrateIndexV1L3[] = { -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }; public static final int bitrateIndexV2L1[] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }; public static final int bitrateIndexV2L23[] = { -1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }; }