/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.frame.speex.type;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.apache.log4j.Logger;
import com.ttProject.frame.speex.SpeexFrame;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.util.BufferUtil;
/**
* speex header frame.
*
* speexでは、headerの部分が欠如しているらしい。(ffmpegの出力より)
* これは推測ですが、どうやらspeexのoggファイル化したときにでる、header部分が固定化されているために、削除状態になっている感じ。
* aacのdeviceSpecificInfoが1つで固定なので、省略されている感じ。
* よってframe数はaudioTagごとに固定されているみたいです。
* その確認として、2つのaudioTagが合体しているaudioTagをつくって、再生したところ、はじめの音がこわれました。
* 正解な気がします。
*
* 以上とりあえず推測
* speexは1つのframeあたり320samplesで動作している模様です。
*
* flvでは存在しないが、speexのheaderがきちんとある。
* oggにするとheader + meta + 実体みたいな感じになるみたいです。
* @see http://www.speex.org/docs/manual/speex-manual/node8.html
* headerの情報は
* 8byte: speexString Speex
* 20byte: speexVersion(超過分は0x00で埋め) 1.2rc1
* 4byte: speexVersionId 01 00 00 00
* 4byte: headerSize 50 00 00 00 ←これが0x50を示しているみたい
* 4byte: rate 00 7D 00 00 ←0x7d00 32000
* 4byte: mode 02 00 00 00
* 4byte: modeBitstreamVersion 04 00 00 00
* 4byte: nbChannels 02 00 00 00
* 4byte: bitrate A0 73 00 00 ←29600(31kbpsっぽいけど・・・あわないなんだろう)
* 4byte: frameSize 80 02 00 00 ←0x280 640
* 4byte: vbr 00 00 00 00
* 4byte: framesPerPacket 01 00 00 00
* 4byte: extraHeaders 00 00 00 00
* 4byte: reserved1 00 00 00 00
* 4byte: reserved2 00 00 00 00
*
* @author taktod
*/
public class HeaderFrame extends SpeexFrame {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(HeaderFrame.class);
// TODO should I use Bit object, not string.
private String speexString;
private String speexVersion;
private int speexVersionId;
private int headerSize;
private int rate; // samplingRate
private int mode;
private int modeBitstreamVersion;
private int nbChannels;
private int bitRate;
private int frameSize;
private int vbr;
private int framesPerPacket;
private int extraHeaders;
private int reserved1;
private int reserved2;
/**
* initialize with flv default value.
*/
public void fillWithFlvDefault() throws Exception {
speexString = "Speex ";
speexVersion = "1.2rc1";
speexVersionId = 1;
headerSize = 0x50;
rate = 0x3E80;
mode = 1;
modeBitstreamVersion = 4;
nbChannels = 1;
bitRate = 0xA4D8; // flvにあわせて変更すべき?(42200bpsにしておく。固定か?、vlcではこの値をみて、データの長さを計算しているみたいです。)
frameSize = 0x140; // seems to be the fix value.
vbr = 0;
framesPerPacket = 1;
extraHeaders = 0;
reserved1 = 0;
reserved2 = 0;
// super.setReadPosition(channel.position());
super.setSize(headerSize);
super.setSampleNum(frameSize * framesPerPacket);
super.setSampleRate(rate);
super.setChannel(nbChannels);
super.update();
}
/**
* {@inheritDoc}
*/
@Override
public void minimumLoad(IReadChannel channel) throws Exception {
speexString = new String(BufferUtil.safeRead(channel, 8).array());
speexVersion = new String(BufferUtil.safeRead(channel, 20).array());
ByteBuffer buffer = BufferUtil.safeRead(channel, 52);
buffer.order(ByteOrder.LITTLE_ENDIAN);
speexVersionId = buffer.getInt();
headerSize = buffer.getInt();
rate = buffer.getInt();
mode = buffer.getInt();
modeBitstreamVersion = buffer.getInt();
nbChannels = buffer.getInt();
bitRate = buffer.getInt();
frameSize = buffer.getInt(); // sampleNumのことっぽい 320固定だとおもってたけど・・・
vbr = buffer.getInt();
framesPerPacket = buffer.getInt(); // I haven't seen other than 1. fixed value?
extraHeaders = buffer.getInt();
reserved1 = buffer.getInt();
reserved2 = buffer.getInt();
// logger.info(toString());
super.setReadPosition(channel.position());
super.setSize(channel.size());
super.setSampleNum(frameSize * framesPerPacket); // fix with framesPerPacket.
super.setSampleRate(rate);
super.setChannel(nbChannels);
super.update();
}
/**
* {@inheritDoc}
*/
@Override
public void load(IReadChannel channel) throws Exception {
super.update();
}
/**
* {@inheritDoc}
*/
@Override
protected void requestUpdate() throws Exception {
ByteBuffer buffer = ByteBuffer.allocate(80);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(speexString.getBytes());
while(buffer.position() < 8) {
buffer.put((byte)0x00);
}
buffer.put(speexVersion.getBytes());
while(buffer.position() < 28) {
buffer.put((byte)0x00);
}
buffer.putInt(speexVersionId);
buffer.putInt(headerSize);
buffer.putInt(rate);
buffer.putInt(mode);
buffer.putInt(modeBitstreamVersion);
buffer.putInt(nbChannels);
buffer.putInt(bitRate);
buffer.putInt(frameSize);
buffer.putInt(vbr);
buffer.putInt(framesPerPacket);
buffer.putInt(extraHeaders);
buffer.putInt(reserved1);
buffer.putInt(reserved2);
buffer.flip();
super.setData(buffer);
}
/**
* {@inheritDoc}
*/
@Override
public ByteBuffer getPackBuffer() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder data = new StringBuilder("speexHeaderFrame:\n");
data.append(" string:").append(speexString).append("\n");
data.append(" version:").append(speexVersion).append("\n");
data.append(" versionId:").append(speexVersionId).append("\n");
data.append(" headerSize:").append(headerSize).append("\n");
data.append(" rate:").append(rate).append("\n");
data.append(" mode:").append(mode).append("\n");
data.append(" modeBitstreamVersion:").append(modeBitstreamVersion).append("\n");
data.append(" nbChannels:").append(nbChannels).append("\n");
data.append(" bitRate:").append(bitRate).append("\n");
data.append(" frameSize:").append(frameSize).append("\n");
data.append(" vbr:").append(vbr).append("\n");
data.append(" framesPerPacket:").append(framesPerPacket).append("\n");
data.append(" extraHeaders:").append(extraHeaders).append("\n");
data.append(" reserved1:").append(reserved1).append("\n");
data.append(" reserved2:").append(reserved2).append("\n");
return data.toString();
}
}