/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.container.mpegts.field; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import com.ttProject.nio.channels.ByteReadChannel; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.unit.extra.Bit; import com.ttProject.unit.extra.BitConnector; import com.ttProject.unit.extra.BitLoader; import com.ttProject.unit.extra.bit.Bit1; import com.ttProject.unit.extra.bit.Bit31; import com.ttProject.unit.extra.bit.Bit33; import com.ttProject.unit.extra.bit.Bit6; import com.ttProject.unit.extra.bit.Bit8; import com.ttProject.unit.extra.bit.Bit9; /** * adaptationField * @author taktod */ public class AdaptationField { /** logger */ @SuppressWarnings("unused") private Logger logger = Logger.getLogger(AdaptationField.class); private Bit8 adaptationFieldLength = new Bit8(); // it is possible to have only length. private Bit1 discontinuityIndicator = null; // 0 private Bit1 randomAccessIndicator = null; // keyFrame flag? aac mp3 pes and keyFrame of h264 will be 1 private Bit1 elementaryStreamPriorityIndicator = null; // 0 private Bit1 pcrFlag = null; private Bit1 opcrFlag = null; // originalPcr(in the case of copy use??) // 0 private Bit1 splicingPointFlag = null; // 0 private Bit1 transportPrivateDataFlag = null; // 0 private Bit1 adaptationFieldExtensionFlag = null; // 0 // pcr private Bit33 pcrBase = null; private Bit6 pcrPadding = null; private Bit9 pcrExtension = null; // opcr private Bit33 opcrBase = null; private Bit6 opcrPadding = null; private Bit9 opcrExtension = null; /** * prepare headerFlag for byte data. */ private void initElement() { if(discontinuityIndicator == null) { discontinuityIndicator = new Bit1(); } if(randomAccessIndicator == null) { randomAccessIndicator = new Bit1(); } if(elementaryStreamPriorityIndicator == null) { elementaryStreamPriorityIndicator = new Bit1(); } if(pcrFlag == null) { pcrFlag = new Bit1(); } if(opcrFlag == null) { opcrFlag = new Bit1(); } if(splicingPointFlag == null) { splicingPointFlag = new Bit1(); } if(transportPrivateDataFlag == null) { transportPrivateDataFlag = new Bit1(); } if(adaptationFieldExtensionFlag == null) { adaptationFieldExtensionFlag = new Bit1(); } } /** * update length */ private void checkLength() { int length = 0; if(discontinuityIndicator != null) { length += 1; } if(pcrFlag != null && pcrFlag.get() == 1) { length += 6; } if(opcrFlag != null && opcrFlag.get() == 1) { length += 6; } adaptationFieldLength.set(length); } public boolean hasPcr() { initElement(); checkLength(); return pcrFlag.get() == 1; } public long getPcrBase() { BitConnector connector = new BitConnector(); return connector.connect(new Bit31(), pcrBase).getLong(); } public long getOpcrBase() { BitConnector connector = new BitConnector(); return connector.connect(new Bit31(), opcrBase).getLong(); } public void setPcrFlag(int flag) { initElement(); pcrFlag.set(flag); checkLength(); } public void setPcrBase(long base) throws Exception { initElement(); pcrBase = new Bit33(); pcrPadding = new Bit6(0x3F); pcrExtension = new Bit9(); ByteBuffer buffer = ByteBuffer.allocate(8); buffer.putLong(base); buffer.flip(); BitLoader loader = new BitLoader(new ByteReadChannel(buffer)); loader.load(new Bit31(), pcrBase); checkLength(); } public void setRandomAccessIndicator(int flg) { // adaptationFieldの長さが存在しない場合は1に変更する必要あり。 initElement(); randomAccessIndicator = new Bit1(flg); checkLength(); } // pcr opcr spliceCountdown stuffingBytes等々・・・ public void load(IReadChannel channel) throws Exception { BitLoader bitLoader = new BitLoader(channel); bitLoader.load(adaptationFieldLength); if(adaptationFieldLength.get() == 0x00) { return; } int size = adaptationFieldLength.get(); initElement(); bitLoader = new BitLoader(channel); bitLoader.load(discontinuityIndicator, randomAccessIndicator, elementaryStreamPriorityIndicator, pcrFlag, opcrFlag, splicingPointFlag, transportPrivateDataFlag, adaptationFieldExtensionFlag); size --; // load extra data. if(pcrFlag.get() != 0x00) { // pcr // next 33bit 6bit 9bit is the data. // 33bit will be duration(timebase = 90000) // 6bit is padding bit filled with zero. // 9bit is duration(timebase 27M) pcrBase = new Bit33(); pcrPadding = new Bit6(); pcrExtension = new Bit9(); bitLoader = new BitLoader(channel); bitLoader.load(pcrBase, pcrPadding, pcrExtension); size -= 6; } if(opcrFlag.get() != 0x00) { // looks like pcr. just do the same. opcrBase = new Bit33(); opcrPadding = new Bit6(); opcrExtension = new Bit9(); bitLoader = new BitLoader(channel); bitLoader.load(opcrBase, opcrPadding, opcrExtension); size -= 6; } if(splicingPointFlag.get() != 0x00) { throw new Exception("splicingPoint analyzation is not supported yet."); } if(transportPrivateDataFlag.get() != 0x00) { throw new Exception("transportPrivateData analyzation is not supported yet."); } if(adaptationFieldExtensionFlag.get() != 0x00) { throw new Exception("adaptationFieldExtension analyzation is not supported yet."); } if(size != 0) { // fill with empty data(0xff) channel.position(channel.position() + size); // skip the data. } } /** * change length * @param length */ public void setLength(int length) { adaptationFieldLength = new Bit8(length); initElement(); } /** * ref length * @return */ public int getLength() { return adaptationFieldLength.get(); } public List<Bit> getBits() { List<Bit> list = new ArrayList<Bit>(); int length = adaptationFieldLength.get(); list.add(adaptationFieldLength); if(length == 0) { return list; } list.add(discontinuityIndicator); list.add(randomAccessIndicator); list.add(elementaryStreamPriorityIndicator); list.add(pcrFlag); list.add(opcrFlag); list.add(splicingPointFlag); list.add(transportPrivateDataFlag); list.add(adaptationFieldExtensionFlag); length --; if(pcrFlag.get() != 0x00) { list.add(pcrBase); list.add(pcrPadding); list.add(pcrExtension); length -= 6; } if(opcrFlag.get() != 0x00) { list.add(opcrBase); list.add(opcrPadding); list.add(opcrExtension); length -= 6; } for(int i = 0;i < length;i ++) { list.add(new Bit8((byte)0xFF)); } return list; } public int getRandomAccessIndicator() { initElement(); checkLength(); return randomAccessIndicator.get(); } @Override public String toString() { StringBuilder data = new StringBuilder(); data.append(" "); data.append("adaptationField:"); data.append(" afl:").append(Integer.toHexString(adaptationFieldLength.get())); if(adaptationFieldLength.get() != 0) { data.append(" di:").append(discontinuityIndicator); data.append(" rai:").append(randomAccessIndicator); data.append(" espi:").append(elementaryStreamPriorityIndicator); data.append(" pf:").append(pcrFlag); data.append(" of:").append(opcrFlag); data.append(" spf:").append(splicingPointFlag); data.append(" tpdf:").append(transportPrivateDataFlag); data.append(" afef:").append(adaptationFieldExtensionFlag); if(pcrFlag.get() != 0x00) { data.append("[pcrBase:").append(Long.toHexString(getPcrBase())) .append("(").append(getPcrBase() / 90000f).append("sec)"); data.append(" pcrPadding:").append(pcrPadding); data.append(" pcrExtension:").append(pcrExtension); data.append("]"); } if(opcrFlag.get() != 0x00) { data.append("[opcrBase:").append(Long.toHexString(getOpcrBase())) .append("(").append(getOpcrBase() / 90000f).append("sec)"); data.append(" opcrPadding:").append(opcrPadding); data.append(" opcrExtension:").append(opcrExtension); data.append("]"); } } return data.toString(); } }