package com.googlecode.mp4parser.h264;
import com.coremedia.iso.IsoBufferWrapper;
import com.googlecode.mp4parser.h264.model.NALUnit;
import com.googlecode.mp4parser.h264.model.NALUnitType;
import com.googlecode.mp4parser.h264.model.PictureParameterSet;
import com.googlecode.mp4parser.h264.model.SeqParameterSet;
import com.googlecode.mp4parser.h264.model.SliceHeader;
import com.googlecode.mp4parser.h264.read.CAVLCReader;
import com.googlecode.mp4parser.h264.read.SliceHeaderReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
*
*/
public class AccessUnitSourceImpl implements AccessUnitSource, StreamParams {
private Map<Integer, SeqParameterSet> seqParameterSetMap = new HashMap<Integer, SeqParameterSet>();
private Map<Integer, PictureParameterSet> pictureParameterSetMap = new HashMap<Integer, PictureParameterSet>();
NALUnitReader nalUnitReader;
IsoBufferWrapper currentNal;
SliceHeaderReader sliceHeaderReader = new SliceHeaderReader(this);
public AccessUnitSourceImpl(NALUnitReader nalUnitReader) throws IOException {
this.nalUnitReader = nalUnitReader;
currentNal = nalUnitReader.nextNALUnit();
}
public AccessUnit nextAccessUnit() throws IOException {
if (currentNal == null) {
return null;
}
List<IsoBufferWrapper> au = new LinkedList<IsoBufferWrapper>();
NALUnit oldNu = null;
SliceHeader oldSh = null;
boolean nalEnd = false;
do {
currentNal.position(0);
NALUnit nu = NALUnit.read(currentNal);
if (nu.type == NALUnitType.SPS) {
SeqParameterSet sps = SeqParameterSet.read(currentNal);
seqParameterSetMap.put(sps.seq_parameter_set_id, sps);
} else if (nu.type == NALUnitType.PPS) {
PictureParameterSet pps = PictureParameterSet.read(currentNal);
pictureParameterSetMap.put(pps.pic_parameter_set_id, pps);
} else if (nal_type_ok_for_sample.contains(nu.type)) {
if (nu.type == NALUnitType.IDR_SLICE
|| nu.type == NALUnitType.NON_IDR_SLICE) {
SliceHeader sh = sliceHeaderReader.read(nu, new CAVLCReader(currentNal));
if (oldNu != null && oldSh != null
&& !sameAccessUnit(oldNu, nu, oldSh, sh)) {
nalEnd = true;
} else {
oldNu = nu;
oldSh = sh;
currentNal.position(0);
au.add(currentNal);
}
} else if (oldNu != null && nu.type == NALUnitType.SEI) {
nalEnd = true;
} else {
currentNal.position(0);
au.add(currentNal);
}
}
}
while (!nalEnd && (currentNal = nalUnitReader.nextNALUnit()) != null);
return new AccessUnitImpl(au);
}
public SeqParameterSet getSPS(int id) {
return seqParameterSetMap.get(id);
}
public PictureParameterSet getPPS(int id) {
return pictureParameterSetMap.get(id);
}
private class AccessUnitImpl implements AccessUnit {
LinkedList<IsoBufferWrapper> nals;
private AccessUnitImpl(List<IsoBufferWrapper> nals) {
this.nals = new LinkedList<IsoBufferWrapper>(nals);
}
public IsoBufferWrapper nextNALUnit() throws IOException {
try {
IsoBufferWrapper ret = nals.peek();
nals.remove();
return ret;
} catch (NoSuchElementException e) {
return null;
}
}
}
static Set<NALUnitType> nal_type_ok_for_sample = new HashSet<NALUnitType>() {
{
this.add(NALUnitType.NON_IDR_SLICE);
this.add(NALUnitType.SLICE_PART_A);
this.add(NALUnitType.SLICE_PART_B);
this.add(NALUnitType.SLICE_PART_C);
this.add(NALUnitType.IDR_SLICE);
this.add(NALUnitType.SEI);
this.add(NALUnitType.AUX_SLICE);
}
};
private boolean sameAccessUnit(NALUnit nu1, NALUnit nu2, SliceHeader sh1,
SliceHeader sh2) {
if (sh1.pic_parameter_set_id != sh2.pic_parameter_set_id)
return false;
if (sh1.frame_num != sh2.frame_num)
return false;
PictureParameterSet pps = pictureParameterSetMap.get(sh1.pic_parameter_set_id);
SeqParameterSet sps = seqParameterSetMap.get(pps.seq_parameter_set_id);
if ((sps.pic_order_cnt_type == 0 && sh1.pic_order_cnt_lsb != sh2.pic_order_cnt_lsb))
return false;
if ((sps.pic_order_cnt_type == 1 && (sh1.delta_pic_order_cnt[0] != sh2.delta_pic_order_cnt[0] || sh1.delta_pic_order_cnt[1] != sh2.delta_pic_order_cnt[1])))
return false;
if (((nu1.nal_ref_idc == 0 || nu2.nal_ref_idc == 0) && nu1.nal_ref_idc != nu2.nal_ref_idc))
return false;
if (((nu1.type == NALUnitType.IDR_SLICE) != (nu2.type == NALUnitType.IDR_SLICE)))
return false;
if (sh1.idr_pic_id != sh2.idr_pic_id)
return false;
return true;
}
}