/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.media.mp3;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.ttProject.media.Manager;
import com.ttProject.media.extra.Bit1;
import com.ttProject.media.extra.Bit2;
import com.ttProject.media.extra.Bit3;
import com.ttProject.media.extra.Bit4;
import com.ttProject.media.extra.Bit8;
import com.ttProject.media.extra.BitLoader;
import com.ttProject.media.mp3.frame.ID3;
import com.ttProject.media.mp3.frame.Mp3;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.util.BufferUtil;
/**
* @see http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
* @author taktod
*/
public class Mp3Manager extends Manager<Frame> {
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(Mp3Manager.class);
/**
* {@inheritDoc}
*/
@Override
public List<Frame> getUnits(ByteBuffer data) throws Exception {
ByteBuffer buffer = appendBuffer(data);
if(buffer == null) {
return null;
}
IReadChannel bufferChannel = new ByteReadChannel(buffer);
List<Frame> result = new ArrayList<Frame>();
while(true) {
int position = bufferChannel.position();
Frame frame = getUnit(bufferChannel);
if(frame == null) {
// positionを戻します。
buffer.position(position);
break;
}
frame.analyze(bufferChannel);
// TODO analyze動作の中身をつくっておきたいところ。
// logger.info(position);
// logger.info(frame.getSize());
bufferChannel.position(position + frame.getSize());
result.add(frame);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public Frame getUnit(IReadChannel source) throws Exception {
if(source.size() - source.position() < 3) {
// 少なくとも3バイトは必要
return null;
}
int position = source.position();
ByteBuffer buffer = BufferUtil.safeRead(source, 3);
byte[] data = new byte[3];
buffer.get(data);
// ID3V2の場合
if(data[0] == 'I'
&& data[1] == 'D'
&& data[2] == '3') {
if(source.size() - position < 10) {
// 少なくとも10(始めから数えて必要)バイト必要
return null;
}
buffer = BufferUtil.safeRead(source, 7);
short version = buffer.getShort();
data = new byte[5];
buffer.get(data);
ID3 id3 = new ID3(position, ((data[1] & 0x7F) << 21) + ((data[2] & 0x7F) << 14) + ((data[3] & 0x7F) << 7) + (data[4] & 0x7F) + 10, version, data[0]);
if(id3.getPosition() + id3.getSize() > source.size()) {
// 中身を充填するのに必要なサイズがない場合はnullを応答
return null;
}
return id3;
}
// ID3V1の場合(終端にくるデータなので、とりあえず無視しておく)
else if(data[0] == 'T'
&& data[1] == 'A'
&& data[2] == 'G') {
throw new RuntimeException("ID3v1はサポートしていないです。");
}
else if(data[0] == (byte)0xFF && (data[1] & 0xE0) == 0xE0) {
// データが4バイト読み込めない場合は動作不能
if(source.size() - position < 4) {
return null;
}
byte data3 = BufferUtil.safeRead(source, 1).get();
IReadChannel byteChannel = new ByteReadChannel(new byte[]{
data[0], data[1], data[2], data3
});
Bit3 syncBit_1 = new Bit3();
Bit8 syncBit_2 = new Bit8();
Bit2 mpegVersion = new Bit2();
Bit2 layer = new Bit2();
Bit1 protectionBit = new Bit1();
Bit4 bitrateIndex = new Bit4();
Bit2 samplingRateIndex = new Bit2();
Bit1 paddingBit = new Bit1();
Bit1 privateBit = new Bit1();
Bit2 channelMode = new Bit2();
Bit2 modeExtension = new Bit2();
Bit1 copyRight = new Bit1();
Bit1 originalFlg = new Bit1();
Bit2 emphasis = new Bit2();
BitLoader bitLoader = new BitLoader(byteChannel);
bitLoader.load(syncBit_1, syncBit_2,
mpegVersion, layer, protectionBit, bitrateIndex, samplingRateIndex,
paddingBit, privateBit, channelMode, modeExtension, copyRight,
originalFlg, emphasis);
byteChannel.close();
Mp3 mp3 = new Mp3(position, mpegVersion, layer, protectionBit, bitrateIndex, samplingRateIndex, paddingBit, privateBit, channelMode, modeExtension, copyRight, originalFlg, emphasis);
if(mp3.getPosition() + mp3.getSize() > source.size()) {
return null;
}
return mp3;
}
return null;
}
}