package org.jcodec.codecs.h264.decode;
import static org.jcodec.codecs.h264.H264Utils.unescapeNAL;
import static org.jcodec.codecs.h264.io.model.NALUnitType.IDR_SLICE;
import static org.jcodec.codecs.h264.io.model.NALUnitType.NON_IDR_SLICE;
import static org.jcodec.codecs.h264.io.model.NALUnitType.PPS;
import static org.jcodec.codecs.h264.io.model.NALUnitType.SPS;
import org.jcodec.codecs.common.biari.MDecoder;
import org.jcodec.codecs.h264.decode.aso.MapManager;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.CABAC;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* MPEG 4 AVC ( H.264 ) Frame reader
*
* Conforms to H.264 ( ISO/IEC 14496-10 ) specifications
*
* @author The JCodec project
*
*/
public class FrameReader {
private IntObjectMap<SeqParameterSet> sps;
private IntObjectMap<PictureParameterSet> pps;
public FrameReader() {
this.sps = new IntObjectMap<SeqParameterSet>();
this.pps = new IntObjectMap<PictureParameterSet>();
}
public List<SliceReader> readFrame(List<ByteBuffer> nalUnits) {
List<SliceReader> result = new ArrayList<SliceReader>();
for (ByteBuffer nalData : nalUnits) {
NALUnit nalUnit = NALUnit.read(nalData);
unescapeNAL(nalData);
if (SPS == nalUnit.type) {
SeqParameterSet _sps = SeqParameterSet.read(nalData);
sps.put(_sps.seq_parameter_set_id, _sps);
} else if (PPS == nalUnit.type) {
PictureParameterSet _pps = PictureParameterSet.read(nalData);
pps.put(_pps.pic_parameter_set_id, _pps);
} else if (IDR_SLICE == nalUnit.type || NON_IDR_SLICE == nalUnit.type) {
if (sps.size() == 0 || pps.size() == 0) {
Logger.warn("Skipping frame as no SPS/PPS have been seen so far...");
return null;
}
result.add(createSliceReader(nalData, nalUnit));
}
}
return result;
}
private SliceReader createSliceReader(ByteBuffer segment, NALUnit nalUnit) {
BitReader _in = BitReader.createBitReader(segment);
SliceHeaderReader shr = new SliceHeaderReader();
SliceHeader sh = shr.readPart1(_in);
sh.pps = pps.get(sh.pic_parameter_set_id);
sh.sps = sps.get(sh.pps.seq_parameter_set_id);
shr.readPart2(sh, nalUnit, sh.sps, sh.pps, _in);
Mapper mapper = new MapManager(sh.sps, sh.pps).getMapper(sh);
CAVLC[] cavlc = new CAVLC[] { new CAVLC(sh.sps, sh.pps, 2, 2), new CAVLC(sh.sps, sh.pps, 1, 1),
new CAVLC(sh.sps, sh.pps, 1, 1) };
int mbWidth = sh.sps.pic_width_in_mbs_minus1 + 1;
CABAC cabac = new CABAC(mbWidth);
MDecoder mDecoder = null;
if (sh.pps.entropy_coding_mode_flag) {
_in.terminate();
int[][] cm = new int[2][1024];
int qp = sh.pps.pic_init_qp_minus26 + 26 + sh.slice_qp_delta;
cabac.initModels(cm, sh.slice_type, sh.cabac_init_idc, qp);
mDecoder = new MDecoder(segment, cm);
}
return new SliceReader(sh.pps, cabac, cavlc, mDecoder, _in, mapper, sh, nalUnit);
}
public void addSpsList(List<ByteBuffer> spsList) {
for (ByteBuffer byteBuffer : spsList) {
addSps(byteBuffer);
}
}
public void addSps(ByteBuffer byteBuffer) {
ByteBuffer clone = NIOUtils.clone(byteBuffer);
unescapeNAL(clone);
SeqParameterSet s = SeqParameterSet.read(clone);
sps.put(s.seq_parameter_set_id, s);
}
public void addPpsList(List<ByteBuffer> ppsList) {
for (ByteBuffer byteBuffer : ppsList) {
addPps(byteBuffer);
}
}
public void addPps(ByteBuffer byteBuffer) {
ByteBuffer clone = NIOUtils.clone(byteBuffer);
unescapeNAL(clone);
PictureParameterSet p = PictureParameterSet.read(clone);
pps.put(p.pic_parameter_set_id, p);
}
}