/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.unit.extra;
import org.apache.log4j.Logger;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.unit.extra.bit.Bit1;
import com.ttProject.unit.extra.bit.Bit2;
import com.ttProject.unit.extra.bit.Bit3;
import com.ttProject.unit.extra.bit.Bit4;
import com.ttProject.unit.extra.bit.Bit5;
import com.ttProject.unit.extra.bit.Bit6;
import com.ttProject.unit.extra.bit.Bit64;
import com.ttProject.unit.extra.bit.Bit7;
import com.ttProject.util.BufferUtil;
/**
* load bit data from ReadChannel
* @author taktod
*/
public class BitLoader {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(BitLoader.class);
/** targetChannel */
private final IReadChannel channel;
/** tmpData */
private long tmpData = 0;
/** left bit count */
private int left = 0;
/** little endian flag */
private boolean littleEndianFlg = false;
/** for h264 or h265 flag */
private boolean emulationPreventionFlg = false;
/** for h264 or h265 byte data for 0x00 */
private byte firstByte = -1;
private byte secondByte = -1;
/**
* set little endian flag
* @param flg
*/
public void setLittleEndianFlg(boolean flg) {
littleEndianFlg = flg;
}
/**
* check mode is little endian.
* @return
*/
public boolean isLittleEndian() {
return littleEndianFlg;
}
/**
* for h264 or h265 emulation prevention flg
* @param flg
*/
public void setEmulationPreventionFlg(boolean flg) {
emulationPreventionFlg = flg;
}
/**
* check mode is emulation prevention.
* @return
*/
public boolean isEmulationPrevention() {
return emulationPreventionFlg;
}
/**
* constructor
* @param channel
*/
public BitLoader(IReadChannel channel) throws Exception {
this.channel = channel;
}
/**
* load the bit.
* @param bit
*/
public void load(Bit bit) throws Exception {
if(bit instanceof EbmlValue) {
EbmlValue ebml = (EbmlValue) bit;
Bit1 bit1 = null;
do {
bit1 = new Bit1();
load(bit1);
} while(ebml.addBit1(bit1)); // check the data size, by reading one by one.
load(ebml.getDataBit()); // load the left data.
}
else if(bit instanceof ExpGolomb) {
ExpGolomb golomb = (ExpGolomb) bit;
Bit1 bit1 = null;
do {
bit1 = new Bit1();
load(bit1);
} while(golomb.addBit1(bit1));
}
else {
if(littleEndianFlg) {
while(left < bit.bitCount) {
byte currentByte = BufferUtil.safeRead(channel, 1).get();
if(emulationPreventionFlg) {
if(firstByte == 0 && secondByte == 0 && currentByte == 3) {
firstByte = -1;
secondByte = -1;
continue;
}
firstByte = secondByte;
secondByte = currentByte;
}
tmpData = (tmpData | (currentByte & 0xFFL) << left);
left += 8;
}
int bitCount = bit.bitCount;
if(bit instanceof Bit64) {
((Bit64)bit).setLong(tmpData);
}
else if(bit instanceof BitN) {
((BitN) bit).setLong(tmpData & ((1L << bitCount) - 1));
}
else {
bit.set((int)(tmpData & ((1L << bitCount) - 1)));
}
if(bitCount == 64) {
// shift task for 64 bit is nothing. therefore, do 32bit shift twice.
tmpData >>>= 32;
tmpData >>>= 32;
}
else {
tmpData >>>= bitCount;
}
left -= bitCount;
}
else {
// TODO BitNを分割して読み込む動作がなくなったことで64bit以上のBitNデータが読み込み不能になっている。(nellymoserの読み込みでこまるはず)
// support only big endian now.
if(bit instanceof BitN && bit.bitCount > 64) {
for(Bit b : ((BitN)bit).bits) {
load(b);
}
}
else {
while(left < bit.bitCount) {
byte currentByte = BufferUtil.safeRead(channel, 1).get();
if(emulationPreventionFlg) {
if(firstByte == 0 && secondByte == 0 && currentByte == 3) {
firstByte = -1;
secondByte = -1;
continue;
}
firstByte = secondByte;
secondByte = currentByte;
}
tmpData = (tmpData << 8 | (currentByte & 0xFFL));
left += 8;
}
int bitCount = bit.bitCount;
if(bit instanceof BitN) {
((BitN) bit).setLong(tmpData >>> (left - bitCount));
}
else {
bit.set((int)(tmpData >>> (left - bitCount)));
}
left -= bitCount;
}
}
}
}
/**
* load multiple bits.
* @param bits
* @throws Exception
*/
public void load(Bit... bits) throws Exception {
for(Bit bit : bits) {
if(bit != null) {
load(bit);
}
}
}
/**
* get the extra data(less than 1byte).
* @return
*/
public Bit getExtraBit() throws Exception {
Bit bit = null;
switch(left) {
case 1:
bit = new Bit1();
break;
case 2:
bit = new Bit2();
break;
case 3:
bit = new Bit3();
break;
case 4:
bit = new Bit4();
break;
case 5:
bit = new Bit5();
break;
case 6:
bit = new Bit6();
break;
case 7:
bit = new Bit7();
break;
default:
return null;
}
load(bit);
return bit;
}
}