/** * Build a {@link SegmentIndex} containing recently gathered Cues information. * * <p>{@link #cues} is guaranteed to not be null after calling this method. In * the event that it can't be built, an {@link ParserException} will be thrown. */ private void buildCues() throws ParserException { if (segmentStartOffsetBytes == UNKNOWN) { throw new ParserException("Segment start/end offsets unknown"); } else if (durationUs == UNKNOWN) { throw new ParserException("Duration unknown"); } else if (cuesSizeBytes == UNKNOWN) { throw new ParserException("Cues size unknown"); } else if (cueTimesUs == null || cueClusterPositions == null || cueTimesUs.size() == 0 || cueTimesUs.size() != cueClusterPositions.size()) { throw new ParserException("Invalid/missing cue points"); } int cuePointsSize = cueTimesUs.size(); int[] sizes = new int[cuePointsSize]; long[] offsets = new long[cuePointsSize]; long[] durationsUs = new long[cuePointsSize]; long[] timesUs = new long[cuePointsSize]; for (int i = 0; i < cuePointsSize; i++) { timesUs[i] = cueTimesUs.get(i); offsets[i] = segmentStartOffsetBytes + cueClusterPositions.get(i); } for (int i = 0; i < cuePointsSize - 1; i++) { sizes[i] = (int) (offsets[i + 1] - offsets[i]); durationsUs[i] = timesUs[i + 1] - timesUs[i]; } sizes[cuePointsSize - 1] = (int) (segmentEndOffsetBytes - offsets[cuePointsSize - 1]); durationsUs[cuePointsSize - 1] = durationUs - timesUs[cuePointsSize - 1]; cues = new SegmentIndex((int) cuesSizeBytes, sizes, offsets, durationsUs, timesUs); cueTimesUs = null; cueClusterPositions = null; readResults |= RESULT_READ_INDEX; }
/** * Build a {@link SegmentIndex} containing recently gathered Cues information. * * <p>{@link #cues} is guaranteed to not be null after calling this method. In * the event that it can't be built, an {@link IllegalStateException} will be thrown. */ private void buildCues() { if (segmentStartOffsetBytes == UNKNOWN) { throw new IllegalStateException("Segment start/end offsets unknown"); } else if (durationUs == UNKNOWN) { throw new IllegalStateException("Duration unknown"); } else if (cuesSizeBytes == UNKNOWN) { throw new IllegalStateException("Cues size unknown"); } else if (cueTimesUs == null || cueClusterPositions == null || cueTimesUs.size() == 0 || cueTimesUs.size() != cueClusterPositions.size()) { throw new IllegalStateException("Invalid/missing cue points"); } int cuePointsSize = cueTimesUs.size(); int[] sizes = new int[cuePointsSize]; long[] offsets = new long[cuePointsSize]; long[] durationsUs = new long[cuePointsSize]; long[] timesUs = new long[cuePointsSize]; for (int i = 0; i < cuePointsSize; i++) { timesUs[i] = cueTimesUs.get(i); offsets[i] = segmentStartOffsetBytes + cueClusterPositions.get(i); } for (int i = 0; i < cuePointsSize - 1; i++) { sizes[i] = (int) (offsets[i + 1] - offsets[i]); durationsUs[i] = timesUs[i + 1] - timesUs[i]; } sizes[cuePointsSize - 1] = (int) (segmentEndOffsetBytes - offsets[cuePointsSize - 1]); durationsUs[cuePointsSize - 1] = durationUs - timesUs[cuePointsSize - 1]; cues = new SegmentIndex((int) cuesSizeBytes, sizes, offsets, durationsUs, timesUs); cueTimesUs = null; cueClusterPositions = null; readResults |= RESULT_READ_INDEX; }
@Override public SegmentIndex getIndex() { return segmentIndex; }
/** * Parses a sidx atom (defined in 14496-12). */ private static SegmentIndex parseSidx(ParsableByteArray atom) { atom.setPosition(ATOM_HEADER_SIZE); int fullAtom = atom.readInt(); int version = parseFullAtomVersion(fullAtom); atom.skip(4); long timescale = atom.readUnsignedInt(); long earliestPresentationTime; long firstOffset; if (version == 0) { earliestPresentationTime = atom.readUnsignedInt(); firstOffset = atom.readUnsignedInt(); } else { earliestPresentationTime = atom.readUnsignedLongToLong(); firstOffset = atom.readUnsignedLongToLong(); } atom.skip(2); int referenceCount = atom.readUnsignedShort(); int[] sizes = new int[referenceCount]; long[] offsets = new long[referenceCount]; long[] durationsUs = new long[referenceCount]; long[] timesUs = new long[referenceCount]; long offset = firstOffset; long time = earliestPresentationTime; long timeUs = Util.scaleLargeTimestamp(time, C.MICROS_PER_SECOND, timescale); for (int i = 0; i < referenceCount; i++) { int firstInt = atom.readInt(); int type = 0x80000000 & firstInt; if (type != 0) { throw new IllegalStateException("Unhandled indirect reference"); } long referenceDuration = atom.readUnsignedInt(); sizes[i] = 0x7fffffff & firstInt; offsets[i] = offset; // Calculate time and duration values such that any rounding errors are consistent. i.e. That // timesUs[i] + durationsUs[i] == timesUs[i + 1]. timesUs[i] = timeUs; time += referenceDuration; timeUs = Util.scaleLargeTimestamp(time, C.MICROS_PER_SECOND, timescale); durationsUs[i] = timeUs - timesUs[i]; atom.skip(4); offset += sizes[i]; } return new SegmentIndex(atom.length(), sizes, offsets, durationsUs, timesUs); }
@Override public SegmentIndex getIndex() { return cues; }
/** * Parses a sidx atom (defined in 14496-12). */ private static SegmentIndex parseSidx(ParsableByteArray atom) { atom.setPosition(ATOM_HEADER_SIZE); int fullAtom = atom.readInt(); int version = parseFullAtomVersion(fullAtom); atom.skip(4); long timescale = atom.readUnsignedInt(); long earliestPresentationTime; long firstOffset; if (version == 0) { earliestPresentationTime = atom.readUnsignedInt(); firstOffset = atom.readUnsignedInt(); } else { earliestPresentationTime = atom.readUnsignedLongToLong(); firstOffset = atom.readUnsignedLongToLong(); } atom.skip(2); int referenceCount = atom.readUnsignedShort(); int[] sizes = new int[referenceCount]; long[] offsets = new long[referenceCount]; long[] durationsUs = new long[referenceCount]; long[] timesUs = new long[referenceCount]; long offset = firstOffset; long time = earliestPresentationTime; for (int i = 0; i < referenceCount; i++) { int firstInt = atom.readInt(); int type = 0x80000000 & firstInt; if (type != 0) { throw new IllegalStateException("Unhandled indirect reference"); } long referenceDuration = atom.readUnsignedInt(); sizes[i] = 0x7fffffff & firstInt; offsets[i] = offset; // Calculate time and duration values such that any rounding errors are consistent. i.e. That // timesUs[i] + durationsUs[i] == timesUs[i + 1]. timesUs[i] = (time * 1000000L) / timescale; long nextTimeUs = ((time + referenceDuration) * 1000000L) / timescale; durationsUs[i] = nextTimeUs - timesUs[i]; time += referenceDuration; atom.skip(4); offset += sizes[i]; } return new SegmentIndex(atom.length(), sizes, offsets, durationsUs, timesUs); }
/** * @param segmentIndex The {@link SegmentIndex} to wrap. * @param uri The {@link Uri} where the data is located. * @param indexAnchor The index anchor point. This value is added to the byte offsets specified * in the wrapped {@link SegmentIndex}. */ public DashWrappingSegmentIndex(SegmentIndex segmentIndex, Uri uri, long indexAnchor) { this.segmentIndex = segmentIndex; this.uri = uri; this.indexAnchor = indexAnchor; }