/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.container.riff; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import com.ttProject.container.riff.type.Avih; import com.ttProject.container.riff.type.Data; import com.ttProject.container.riff.type.Db; import com.ttProject.container.riff.type.Dc; import com.ttProject.container.riff.type.Fact; import com.ttProject.container.riff.type.Fmt; import com.ttProject.container.riff.type.Hdrl; import com.ttProject.container.riff.type.Idx1; import com.ttProject.container.riff.type.Info; import com.ttProject.container.riff.type.Isft; import com.ttProject.container.riff.type.Junk; import com.ttProject.container.riff.type.List; import com.ttProject.container.riff.type.Movi; import com.ttProject.container.riff.type.Pc; import com.ttProject.container.riff.type.Riff; import com.ttProject.container.riff.type.Strf; import com.ttProject.container.riff.type.Strh; import com.ttProject.container.riff.type.Strl; import com.ttProject.container.riff.type.Vprp; import com.ttProject.container.riff.type.Wb; import com.ttProject.nio.channels.ByteReadChannel; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.unit.ISelector; import com.ttProject.unit.IUnit; import com.ttProject.util.BufferUtil; /** * riff unit selector * NOTE there are multi riff avi.... should I support? * @author taktod */ public class RiffUnitSelector implements ISelector { /** format information */ private java.util.List<RiffFormatUnit> formatUnitList = new ArrayList<RiffFormatUnit>(); private Strh prevStrhUnit = null; // remain data size in the data tag. private long dataRemainLength = -1; /** * {@inheritDoc} */ @Override public IUnit select(IReadChannel channel) throws Exception { if(channel.position() == channel.size()) { return null; } RiffUnit unit = null; if(dataRemainLength > 0) { int blockSize = 0; RiffFormatUnit formatUnit = formatUnitList.get(0); switch (formatUnit.getCodecType()) { case PCM_ALAW: case PCM_MULAW: blockSize = 0x0100; if(dataRemainLength < 0x0100) { blockSize = (int)dataRemainLength; } break; default: blockSize = formatUnit.getBlockSize(); break; } ByteBuffer buffer = ByteBuffer.allocate(8); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.put((byte)0x30); buffer.put((byte)0x30); buffer.put((byte)((Type.wb.intValue() &0x0000FF00) >> 8)); buffer.put((byte)(Type.wb.intValue() &0x000000FF)); buffer.putInt(blockSize); buffer.flip(); buffer.order(ByteOrder.BIG_ENDIAN); channel = new ByteReadChannel(BufferUtil.connect(buffer, BufferUtil.safeRead(channel, blockSize))); } // check first 4byte // sometimes old ffmpeg has a bug to put the extra 0x00 on the data. // we need to ignore this. int firstByte = -1; while((firstByte = BufferUtil.safeRead(channel, 1).get()) == 0) { // skip 0x00 } ByteBuffer buffer = BufferUtil.safeRead(channel, 3); int typeValue = (firstByte & 0xFF) << 24; typeValue |= (buffer.get() & 0xFF) << 16; typeValue |= (buffer.get() & 0xFF) << 8; typeValue |= (buffer.get() & 0xFF); Type type = Type.getType(typeValue); switch(type) { case RIFF: // header if(channel.position() != 4) { throw new Exception("position of header is invalid."); } unit = new Riff(); break; case FMT: // format information(must) { RiffFormatUnit formatUnit = new Fmt(); formatUnitList.add(formatUnit); if(prevStrhUnit != null) { // maybe no way to come here.. formatUnit.setupStrhInfo(prevStrhUnit); } unit = formatUnit; } break; case FACT: // sampleNum and so on... unit = new Fact(); break; case DATA: // data body.(must) unit = new Data(); break; case LIST: // ? unit = new List(); break; case hdrl: unit = new Hdrl(); break; case avih: unit = new Avih(); break; case strl: unit = new Strl(); break; case strh: unit = new Strh(); prevStrhUnit = (Strh)unit; break; case strf: { // check strh, if RiffFormatUnit formatUnit = null; switch(prevStrhUnit.getFccType()) { case auds: // use fmt formatUnit = new Fmt(); break; case mids: case tets: throw new Exception("unknown for mids or tets."); case vids: formatUnit = new Strf(prevStrhUnit.getRiffCodecType()); break; } formatUnitList.add(formatUnit); if(prevStrhUnit != null) { formatUnit.setupStrhInfo(prevStrhUnit); } unit = formatUnit; } break; case INFO: unit = new Info(); break; case ISFT: unit = new Isft(); break; case movi: unit = new Movi(); break; case vprp: unit = new Vprp(); break; case idx1: unit = new Idx1(); break; case db: unit = new Db(typeValue); break; case dc: unit = new Dc(typeValue); break; case pc: unit = new Pc(typeValue); break; case wb: unit = new Wb(typeValue); break; case JUNK: unit = new Junk(); break; default: throw new RuntimeException("unexpected frame type.:" + type); } if(unit == null) { throw new Exception("unit is undefined.maybe non-support type.:" + type); } if(unit instanceof RiffFrameUnit) { RiffFrameUnit frameUnit = (RiffFrameUnit)unit; frameUnit.setFormatUnit(formatUnitList.get(frameUnit.getTrackId())); } unit.minimumLoad(channel); if(unit instanceof Data) { dataRemainLength = unit.getSize() - 8; } return unit; } }