/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.media.extra.mp4; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import com.ttProject.media.flv.CodecType; import com.ttProject.media.flv.tag.AudioTag; import com.ttProject.media.mp4.Atom; import com.ttProject.media.mp4.IAtomAnalyzer; import com.ttProject.media.mp4.atom.Stco; import com.ttProject.media.mp4.atom.Stsc; import com.ttProject.media.mp4.atom.Stsz; import com.ttProject.media.mp4.atom.Stts; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.util.BufferUtil; /** * 音声データ用のatom * @author taktod * */ public class Sond extends Atom implements IIndexAtom { /** データサイズ */ private int size; /** sample数 */ private int totalSampleCount; /** データサイズ */ private int totalSize; /** timescale値(1秒あたり何ticあるか) */ private int timescale; /** サンプルレート */ private int sampleRate; /** チャンネルカウント 1:モノラル 2:ステレオ */ private byte channelCount; /** mediaSequenceHeader */ private Msh msh; /** stco */ private Stco stco; /** stsc */ private Stsc stsc; /** stsz */ private Stsz stsz; /** stts */ private Stts stts; /** * コンストラクタ * @param size * @param position */ public Sond(int position, int size) { super(Sond.class.getSimpleName().toLowerCase(), position, size); this.size = size; } public void setSize(int size) { this.size = size; } public int getSize() { return size; } public int getTotalSize() { return totalSize; } public int getTotalSampleCount() { return totalSampleCount; } public int getTotalFlvSize() { if(msh == null) { return totalSampleCount * (11 + 4 + 1) + totalSize; } else { return totalSampleCount * (11 + 4 + 2) + totalSize + msh.getSize() - 8 + 11 + 4 + 2; } } public void setTimescale(int timescale) { this.timescale = timescale; } public int getTimescale() { return timescale; } public int getSampleRate() { return sampleRate; } public byte getChannelCount() { return channelCount; } public Msh getMsh() { return msh; } public Stco getStco() { return stco; } public Stsc getStsc() { return stsc; } public Stsz getStsz() { return stsz; } public Stts getStts() { return stts; } @Override public void analyze(IReadChannel ch, IAtomAnalyzer analyzer) throws Exception { ch.position(getPosition() + 8); ByteBuffer buffer = BufferUtil.safeRead(ch, 21); buffer.position(4); totalSampleCount = buffer.getInt(); totalSize = buffer.getInt(); timescale = buffer.getInt(); sampleRate = buffer.getInt(); channelCount = buffer.get(); // ここから先がタグデータ while(ch.position() < getPosition() + getSize()) { int position = ch.position(); buffer = BufferUtil.safeRead(ch, 8); int size = buffer.getInt(); String tag = BufferUtil.getDwordText(buffer); if("msh ".equals(tag)) { msh = new Msh(position, size); } else if("stco".equals(tag)) { stco = new Stco(position, size); } else if("stsc".equals(tag)) { stsc = new Stsc(position, size); } else if("stsz".equals(tag)) { stsz = new Stsz(position, size); } else if("stts".equals(tag)) { stts = new Stts(position, size); } else { throw new Exception("解析不能なタグを発見:" + tag); } ch.position(position + size); } } @Override public void writeIndex(WritableByteChannel idx) throws Exception { ByteBuffer buffer = ByteBuffer.allocate(29); buffer.putInt(size); // サイズ buffer.put("sond".getBytes()); // タグ buffer.putInt(0); // version + flags buffer.putInt(0); // totalSampleCount buffer.putInt(0); // totalSize buffer.putInt(timescale); // timescale buffer.putInt(sampleRate); // sampleRate buffer.put(channelCount); buffer.flip(); idx.write(buffer); } /** * flv用のmediaSequenceHeaderを作成します。 * @param tmp * @return * @throws Exception */ public AudioTag createFlvMshTag(IReadChannel tmp) throws Exception { if(msh == null) { // mediaSequenceHeaderが解析されていない場合は応答しない。 return null; } AudioTag mshTag = new AudioTag(); // とりあえずmshがある場合はaacなので、そのようにしておく mshTag.setCodec(CodecType.AAC); mshTag.setChannels(channelCount); mshTag.setSampleRate(sampleRate); mshTag.setMSHFlg(true); tmp.position(msh.getPosition() + 8); mshTag.setData(tmp, msh.getSize() - 8); return mshTag; } }