/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.container.mkv.type; import java.nio.ByteBuffer; import org.apache.log4j.Logger; import com.ttProject.container.mkv.Lacing; import com.ttProject.container.mkv.MkvBlockTag; import com.ttProject.container.mkv.Type; import com.ttProject.frame.IAudioFrame; import com.ttProject.frame.IFrame; import com.ttProject.frame.IVideoFrame; import com.ttProject.frame.aac.AacFrame; import com.ttProject.frame.h264.SliceFrame; import com.ttProject.frame.vp8.Vp8Frame; import com.ttProject.frame.vp9.Vp9Frame; import com.ttProject.nio.channels.IReadChannel; import com.ttProject.unit.extra.BitConnector; import com.ttProject.unit.extra.BitLoader; import com.ttProject.unit.extra.EbmlValue; import com.ttProject.unit.extra.bit.Bit1; import com.ttProject.unit.extra.bit.Bit2; import com.ttProject.unit.extra.bit.Bit3; import com.ttProject.util.BufferUtil; /** * SimpleBlock * data sample. * A3 44 B4 81 00 00 80 00 00 02 6C ... * A3[SimpleBlock] * 44 B4[tag size(ebml)] * -- already programmed in mkvTag. * 81[trackId(ebml)] * 00 00[timestamp diff(16bit)] * 1000 0000 * . keyFrame flag * ... reserved0 * . is invisible frame? 1:invisible * .. lacing(for frame dividing.*1) * . discardable:what? * *1:for h264, detailed data is separated by h264 nal(sizeNal) * @see http://matroska.org/technical/specs/index.html#simpleblock_structure * @author taktod */ public class SimpleBlock extends MkvBlockTag { /** logger */ @SuppressWarnings("unused") private Logger logger = Logger.getLogger(SimpleBlock.class); private Bit1 keyFrameFlag = new Bit1(); private Bit3 reserved = new Bit3(); private Bit1 invisibleFrameFlag = new Bit1(); private Bit2 lacing = new Bit2(); private Bit1 discardableFlag = new Bit1(); /** * constructor * @param size */ public SimpleBlock(EbmlValue size) { super(Type.SimpleBlock, size); } /** * constructor */ public SimpleBlock() { this(new EbmlValue()); } /** * {@inheritDoc} */ @Override public void minimumLoad(IReadChannel channel) throws Exception { super.minimumLoad(channel); BitLoader loader = new BitLoader(channel); loader.load(keyFrameFlag, reserved, invisibleFrameFlag, lacing, discardableFlag); } /** * {@inheritDoc} */ @Override protected Lacing getLacingType() throws Exception { return Lacing.getType(lacing.get()); } public boolean isKeyFrame() { return keyFrameFlag.get() == 1; } public boolean isInvisibleFrame() { return invisibleFrameFlag.get() == 1; } /** * {@inheritDoc} */ @Override protected int getRemainedSize() { return getMkvSize() - (getTrackId().getBitCount() + 24) / 8; } /** * {@inheritDoc} */ @Override protected void requestUpdate() throws Exception { // here we need to make up the file. BitConnector connector = new BitConnector(); ByteBuffer buffer = connector.connect(getTrackId(), getTimestampDiff(), keyFrameFlag, reserved, invisibleFrameFlag, lacing, discardableFlag); // from here, frame body. IFrame frame = getFrame(); switch(frame.getCodecType()) { case AAC: AacFrame aacFrame = (AacFrame)frame; buffer = BufferUtil.connect(buffer, aacFrame.getBuffer()); break; case H264: // use data nal. if(frame instanceof SliceFrame) { SliceFrame sliceFrame = (SliceFrame) frame; buffer = BufferUtil.connect(buffer, sliceFrame.getDataPackBuffer()); } else { throw new Exception("only sliceFrame is supported for h264 data."); } break; // case H265: default: buffer = BufferUtil.connect(buffer, frame.getData()); break; } getTagSize().set(buffer.remaining()); buffer = BufferUtil.connect(connector.connect(getTagId(), getTagSize()), buffer); setSize(buffer.remaining()); setData(buffer); } /** * add frame * @param trackId * @param frame */ public void addFrame(int trackId, IFrame frame, int timestampDiff) throws Exception { super.setPts(timestampDiff); super.setTimebase(1000); // TODO this timebase is depend on the trackEntry setting. super.getTrackId().set(trackId); super.addFrame(frame); getTimestampDiff().set(timestampDiff); // audio is treated as KeyFrame. if(frame instanceof IAudioFrame) { keyFrameFlag.set(1); } else if(frame instanceof IVideoFrame) { IVideoFrame vFrame = (IVideoFrame)frame; if(vFrame.isKeyFrame()) { keyFrameFlag.set(1); } else { keyFrameFlag.set(0); } // TODO vp8 and vp9 need to put invisible for invisible frames. // block tag do have invisible setting. switch(frame.getCodecType()) { case VP8: @SuppressWarnings("unused") Vp8Frame vp8Frame = (Vp8Frame)frame; break; case VP9: @SuppressWarnings("unused") Vp9Frame vp9Frame = (Vp9Frame)frame; break; default: break; } } // for the lacing, I got a sample with laced mp3, however, nolaced mp3 also works. so later fix to use lacing if necessary. super.update(); } }