/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.container.ogg.type; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.apache.log4j.Logger; import com.ttProject.container.ogg.Crc32; import com.ttProject.container.ogg.OggPage; import com.ttProject.frame.IAnalyzer; import com.ttProject.frame.IFrame; import com.ttProject.frame.opus.OpusFrameAnalyzer; import com.ttProject.frame.speex.SpeexFrameAnalyzer; import com.ttProject.frame.theora.TheoraFrameAnalyzer; import com.ttProject.frame.vorbis.VorbisFrameAnalyzer; import com.ttProject.nio.channels.ByteReadChannel; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.unit.extra.bit.Bit1; import com.ttProject.unit.extra.bit.Bit5; import com.ttProject.unit.extra.bit.Bit8; import com.ttProject.util.BufferUtil; /** * startPage(will have header data.) * @author taktod * * TODO Out of Memoryが発生する可能性があるので、frameListをpageごとに保持するように変更したほうがよい。 * crc32の計算が微妙・・・どうすりゃいいんだ。 */ public class StartPage extends OggPage { /** logger */ private Logger logger = Logger.getLogger(StartPage.class); /** frame Analyzer */ private IAnalyzer analyzer = null; /** passedTic(for audio only.) */ private long passedTic = 0; /** * constructor * @param version * @param zeroFill * @param logicEndFlag * @param logicStartFlag * @param packetContinurousFlag */ public StartPage(Bit8 version, Bit1 packetContinurousFlag, Bit1 logicStartFlag, Bit1 logicEndFlag, Bit5 zeroFill) { super(version, packetContinurousFlag, logicStartFlag, logicEndFlag, zeroFill); } /** * {@inheritDoc} */ @Override public void minimumLoad(IReadChannel channel) throws Exception { super.minimumLoad(channel); super.update(); } /** * {@inheritDoc} */ @Override public void load(IReadChannel channel) throws Exception { boolean isFirstData = true; channel.position(getPosition() + 27 + getSegmentSizeList().size()); int targetSize = 0; for(Bit8 size : getSegmentSizeList()) { if(size.get() == 0xFF) { targetSize += 0xFF; } targetSize += size.get(); ByteBuffer buffer = BufferUtil.safeRead(channel, targetSize); if(isFirstData) { // firstData will have codecInformation on header. switch(buffer.get()) { case 0x01: logger.info("vorbis?"); analyzer = new VorbisFrameAnalyzer(); break; case 'S': logger.info("speex?"); analyzer = new SpeexFrameAnalyzer(); break; case (byte)0x80: logger.info("theora?"); analyzer = new TheoraFrameAnalyzer(); break; case 'O': logger.info("opus?"); analyzer = new OpusFrameAnalyzer(); break; default: throw new Exception("unknown codec is found."); } buffer.position(0); } isFirstData = false; IReadChannel bufferChannel = new ByteReadChannel(buffer); getFrameList().add((IFrame)analyzer.analyze(bufferChannel)); // need to read all of bufferChannel } channel.position(getPosition() + getSize()); super.update(); } /** * {@inheritDoc} */ @Override protected void requestUpdate() throws Exception { // here is the position to make buffer. // write header buffer. ByteBuffer headerBuffer = getHeaderBuffer(); ByteBuffer buffer = ByteBuffer.allocate(getSize()); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.put(headerBuffer); // write frame for(IFrame frame : getFrameList()) { buffer.put(frame.getData()); } ByteBuffer tmpBuffer = buffer.duplicate(); tmpBuffer.flip(); // make crc32 Crc32 crc32 = new Crc32(); while(tmpBuffer.remaining() > 0) { crc32.update(tmpBuffer.get()); } // write crc32 buffer.position(22); buffer.putInt((int)crc32.getValue()); buffer.position(tmpBuffer.position()); buffer.flip(); setData(buffer); } /** * ref the frame analyzer * @return */ public IAnalyzer getAnalyzer() { return analyzer; } /** * set the passedTic(sampleNum) * @param passedTic */ public void setPassedTic(long passedTic) { this.passedTic = passedTic; } /** * ref the passedTic(sampleNum) * @return */ public long getPassedTic() { return passedTic; } }