package org.jcodec.codecs.h264.decode; import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.debugPrint; import static org.jcodec.common.tools.MathUtil.wrap; import org.jcodec.codecs.h264.H264Const; import org.jcodec.codecs.h264.io.model.Frame; import org.jcodec.codecs.h264.io.model.SliceHeader; import org.jcodec.codecs.h264.io.model.SliceType; import org.jcodec.common.IntObjectMap; import org.jcodec.common.model.Picture8Bit; import org.jcodec.platform.Platform; import java.util.Arrays; import java.util.Comparator; /** * Contains reference picture list management logic * * @author The JCodec Project */ public class RefListManager { private SliceHeader sh; private int[] numRef; private Frame[] sRefs; private IntObjectMap<Frame> lRefs; private Frame frameOut; public RefListManager(SliceHeader sh, Frame[] sRefs, IntObjectMap<Frame> lRefs, Frame frameOut) { this.sh = sh; this.sRefs = sRefs; this.lRefs = lRefs; if (sh.num_ref_idx_active_override_flag) numRef = new int[] { sh.num_ref_idx_active_minus1[0] + 1, sh.num_ref_idx_active_minus1[1] + 1 }; else numRef = new int[] { sh.pps.num_ref_idx_active_minus1[0] + 1, sh.pps.num_ref_idx_active_minus1[1] + 1 }; this.frameOut = frameOut; } public Frame[][] getRefList() { Frame[][] refList = null; if (sh.slice_type == SliceType.P) { refList = new Frame[][] { buildRefListP(), null }; } else if (sh.slice_type == SliceType.B) { refList = buildRefListB(); } debugPrint("------"); if (refList != null) { for (int l = 0; l < 2; l++) { if (refList[l] != null) for (int i = 0; i < refList[l].length; i++) if (refList[l][i] != null) debugPrint("REF[%d][%d]: ", l, i, ((Frame) refList[l][i]).getPOC()); } } return refList; } private Frame[] buildRefListP() { int frame_num = sh.frame_num; int maxFrames = 1 << (sh.sps.log2_max_frame_num_minus4 + 4); // int nLongTerm = Math.min(lRefs.size(), numRef[0] - 1); Frame[] result = new Frame[numRef[0]]; int refs = 0; for (int i = frame_num - 1; i >= frame_num - maxFrames && refs < numRef[0]; i--) { int fn = i < 0 ? i + maxFrames : i; if (sRefs[fn] != null) { result[refs] = sRefs[fn] == H264Const.NO_PIC ? null : sRefs[fn]; ++refs; } } int[] keys = lRefs.keys(); Arrays.sort(keys); for (int i = 0; i < keys.length && refs < numRef[0]; i++) { result[refs++] = lRefs.get(keys[i]); } reorder(result, 0); return result; } private Frame[][] buildRefListB() { Frame[] l0 = buildList(Frame.POCDesc, Frame.POCAsc); Frame[] l1 = buildList(Frame.POCAsc, Frame.POCDesc); if (Platform.arrayEqualsObj(l0, l1) && count(l1) > 1) { Frame frame = l1[1]; l1[1] = l1[0]; l1[0] = frame; } Frame[][] result = { Platform.copyOfObj(l0, numRef[0]), Platform.copyOfObj(l1, numRef[1]) }; reorder(result[0], 0); reorder(result[1], 1); return result; } private Frame[] buildList(Comparator<Frame> cmpFwd, Comparator<Frame> cmpInv) { Frame[] refs = new Frame[sRefs.length + lRefs.size()]; Frame[] fwd = copySort(cmpFwd, frameOut); Frame[] inv = copySort(cmpInv, frameOut); int nFwd = count(fwd); int nInv = count(inv); int ref = 0; for (int i = 0; i < nFwd; i++, ref++) refs[ref] = fwd[i]; for (int i = 0; i < nInv; i++, ref++) refs[ref] = inv[i]; int[] keys = lRefs.keys(); Arrays.sort(keys); for (int i = 0; i < keys.length; i++, ref++) refs[ref] = lRefs.get(keys[i]); return refs; } private int count(Frame[] arr) { for (int nn = 0; nn < arr.length; nn++) if (arr[nn] == null) return nn; return arr.length; } private Frame[] copySort(Comparator<Frame> fwd, Frame dummy) { Frame[] copyOf = Platform.copyOfObj(sRefs, sRefs.length); for (int i = 0; i < copyOf.length; i++) if (fwd.compare(dummy, copyOf[i]) > 0) copyOf[i] = null; Arrays.sort(copyOf, fwd); return copyOf; } private void reorder(Picture8Bit[] result, int list) { if (sh.refPicReordering[list] == null) return; int predict = sh.frame_num; int maxFrames = 1 << (sh.sps.log2_max_frame_num_minus4 + 4); for (int ind = 0; ind < sh.refPicReordering[list][0].length; ind++) { switch (sh.refPicReordering[list][0][ind]) { case 0: predict = wrap(predict - sh.refPicReordering[list][1][ind] - 1, maxFrames); break; case 1: predict = wrap(predict + sh.refPicReordering[list][1][ind] + 1, maxFrames); break; case 2: throw new RuntimeException("long term"); } for (int i = numRef[list] - 1; i > ind; i--) result[i] = result[i - 1]; result[ind] = sRefs[predict]; for (int i = ind + 1, j = i; i < numRef[list] && result[i] != null; i++) { if (result[i] != sRefs[predict]) result[j++] = result[i]; } } } }