summaryrefslogtreecommitdiff
path: root/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping
diff options
context:
space:
mode:
Diffstat (limited to 'isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping')
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops59
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries334
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base125
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base28
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base246
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base77
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base200
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base174
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base82
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base83
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base94
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java125
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java28
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java246
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java77
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java200
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java174
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java82
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java83
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java94
20 files changed, 2611 insertions, 0 deletions
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops
new file mode 100644
index 0000000..7ac0085
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/all-wcprops
@@ -0,0 +1,59 @@
+K 25
+svn:wc:ra_dav:version-url
+V 97
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping
+END
+RateShareEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java
+END
+UnknownEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java
+END
+VisualRandomAccessEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java
+END
+RollRecoveryEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java
+END
+SampleToGroupBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java
+END
+CencSampleEncryptionInformationGroupEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 144
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java
+END
+SampleGroupDescriptionBox.java
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java
+END
+TemporalLevelEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/svn/!svn/ver/770/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java
+END
+GroupEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/726/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries
new file mode 100644
index 0000000..05c761e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/entries
@@ -0,0 +1,334 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+RateShareEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+800d0a2f813152ba00d293052be8b937
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8043
+
+UnknownEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+92c8a49a5fb8f163a1a38714e8776de1
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2046
+
+VisualRandomAccessEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+544b84d4026c3b8926ba2a44b11ebfcb
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3781
+
+RollRecoveryEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+9233a1017082e95aff5d23c84d635589
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2417
+
+SampleToGroupBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+5eca2acada7e010970a7037166d46bc1
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5685
+
+CencSampleEncryptionInformationGroupEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+fc1a2ad8992cd88b104626d531df492b
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3641
+
+SampleGroupDescriptionBox.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+07cd2c76b48eea326d9865d4ba5320e3
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7107
+
+TemporalLevelEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+4657c50c8e3abaf12747fc9413eb83a0
+2012-08-31T05:20:57.236953Z
+770
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3101
+
+GroupEntry.java
+file
+
+
+
+
+2012-09-14T17:27:51.127228Z
+a699e1a559c1de4043b594f65d462a82
+2012-07-31T09:37:53.699964Z
+726
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+877
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base
new file mode 100644
index 0000000..b54f4d9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/CencSampleEncryptionInformationGroupEntry.java.svn-base
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Each sample in a protected track shall be associated with an IsEncrypted flag, IV_Size, and KID.
+ * This can be accomplished by (a) relying on the default values in the TrackEncryptionBox
+ * (see 8.2), or (b) specifying the parameters by sample group, or (c) using a combination of these two techniques.
+ * <p/>
+ * When specifying the parameters by sample group, the SampleToGroupBox in the sample table or track
+ * fragment specifies which samples use which sample group description from the SampleGroupDescriptionBox.
+ */
+public class CencSampleEncryptionInformationGroupEntry extends GroupEntry {
+ public static final String TYPE = "seig";
+
+ private int isEncrypted;
+ private byte ivSize;
+ private byte[] kid = new byte[16];
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ isEncrypted = IsoTypeReader.readUInt24(byteBuffer);
+ ivSize = (byte) IsoTypeReader.readUInt8(byteBuffer);
+ kid = new byte[16];
+ byteBuffer.get(kid);
+
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(20);
+ IsoTypeWriter.writeUInt24(byteBuffer, isEncrypted);
+ IsoTypeWriter.writeUInt8(byteBuffer, ivSize);
+ byteBuffer.put(kid);
+ byteBuffer.rewind();
+ return byteBuffer;
+ }
+
+ public int getEncrypted() {
+ return isEncrypted;
+ }
+
+ public void setEncrypted(int encrypted) {
+ isEncrypted = encrypted;
+ }
+
+ public byte getIvSize() {
+ return ivSize;
+ }
+
+ public void setIvSize(byte ivSize) {
+ this.ivSize = ivSize;
+ }
+
+ public byte[] getKid() {
+ return kid;
+ }
+
+ public void setKid(byte[] kid) {
+ assert kid.length == 16;
+ this.kid = kid;
+ }
+
+ @Override
+ public String toString() {
+ return "CencSampleEncryptionInformationGroupEntry{" +
+ "isEncrypted=" + isEncrypted +
+ ", ivSize=" + ivSize +
+ ", kid=" + Hex.encodeHex(kid) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CencSampleEncryptionInformationGroupEntry that = (CencSampleEncryptionInformationGroupEntry) o;
+
+ if (isEncrypted != that.isEncrypted) {
+ return false;
+ }
+ if (ivSize != that.ivSize) {
+ return false;
+ }
+ if (!Arrays.equals(kid, that.kid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = isEncrypted;
+ result = 31 * result + (int) ivSize;
+ result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base
new file mode 100644
index 0000000..0d78d25
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/GroupEntry.java.svn-base
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+public abstract class GroupEntry {
+ public abstract void parse(ByteBuffer byteBuffer);
+ public abstract ByteBuffer get();
+
+ public int size() {
+ return get().limit();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base
new file mode 100644
index 0000000..ae5d380
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RateShareEntry.java.svn-base
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Each sample of a track may be associated to (zero or) one of a number of sample group descriptions, each of
+ * which defines a record of rate-share information. Typically the same rate-share information applies to many
+ * consecutive samples and it may therefore be enough to define two or three sample group descriptions that
+ * can be used at different time intervals.
+ * <p/>
+ * The grouping type 'rash' (short for rate share) is defined as the grouping criterion for rate share information.
+ * Zero or one sample-to-group box ('sbgp') for the grouping type 'rash' can be contained in the sample
+ * table box ('stbl') of a track. It shall reside in a hint track, if a hint track is used, otherwise in a media track.
+ * <p/>
+ * Target rate share may be specified for several operation points that are defined in terms of the total available
+ * bitrate, i.e., the bitrate that should be shared. If only one operation point is defined, the target rate share
+ * applies to all available bitrates. If several operation points are defined, then each operation point specifies a
+ * target rate share. Target rate share values specified for the first and the last operation points also specify the
+ * target rate share values at lower and higher available bitrates, respectively. The target rate share between two
+ * operation points is specified to be in the range between the target rate shares of those operation points. One
+ * possibility is to estimate with linear interpolation.
+ */
+public class RateShareEntry extends GroupEntry {
+ public static final String TYPE = "rash";
+
+ private short operationPointCut;
+ private short targetRateShare;
+ private List<Entry> entries = new LinkedList<Entry>();
+ private int maximumBitrate;
+ private int minimumBitrate;
+ private short discardPriority;
+
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ operationPointCut = byteBuffer.getShort();
+ if (operationPointCut == 1) {
+ targetRateShare = byteBuffer.getShort();
+ } else {
+ int entriesLeft = operationPointCut;
+ while (entriesLeft-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(byteBuffer)), byteBuffer.getShort()));
+ }
+ }
+ maximumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ minimumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ discardPriority = (short) IsoTypeReader.readUInt8(byteBuffer);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer buf = ByteBuffer.allocate(operationPointCut == 1?13:(operationPointCut * 6 + 11 ));
+ buf.putShort(operationPointCut);
+ if (operationPointCut == 1) {
+ buf.putShort(targetRateShare );
+ } else {
+ for (Entry entry : entries) {
+ buf.putInt(entry.getAvailableBitrate());
+ buf.putShort(entry.getTargetRateShare());
+ }
+ }
+ buf.putInt(maximumBitrate);
+ buf.putInt(minimumBitrate);
+ IsoTypeWriter.writeUInt8(buf, discardPriority);
+ buf.rewind();
+ return buf;
+ }
+
+ public static class Entry {
+ public Entry(int availableBitrate, short targetRateShare) {
+ this.availableBitrate = availableBitrate;
+ this.targetRateShare = targetRateShare;
+ }
+
+ int availableBitrate;
+ short targetRateShare;
+
+ @Override
+ public String toString() {
+ return "{" +
+ "availableBitrate=" + availableBitrate +
+ ", targetRateShare=" + targetRateShare +
+ '}';
+ }
+
+ public int getAvailableBitrate() {
+ return availableBitrate;
+ }
+
+ public void setAvailableBitrate(int availableBitrate) {
+ this.availableBitrate = availableBitrate;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (availableBitrate != entry.availableBitrate) {
+ return false;
+ }
+ if (targetRateShare != entry.targetRateShare) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = availableBitrate;
+ result = 31 * result + (int) targetRateShare;
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RateShareEntry that = (RateShareEntry) o;
+
+ if (discardPriority != that.discardPriority) {
+ return false;
+ }
+ if (maximumBitrate != that.maximumBitrate) {
+ return false;
+ }
+ if (minimumBitrate != that.minimumBitrate) {
+ return false;
+ }
+ if (operationPointCut != that.operationPointCut) {
+ return false;
+ }
+ if (targetRateShare != that.targetRateShare) {
+ return false;
+ }
+ if (entries != null ? !entries.equals(that.entries) : that.entries != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) operationPointCut;
+ result = 31 * result + (int) targetRateShare;
+ result = 31 * result + (entries != null ? entries.hashCode() : 0);
+ result = 31 * result + maximumBitrate;
+ result = 31 * result + minimumBitrate;
+ result = 31 * result + (int) discardPriority;
+ return result;
+ }
+
+ public short getOperationPointCut() {
+ return operationPointCut;
+ }
+
+ public void setOperationPointCut(short operationPointCut) {
+ this.operationPointCut = operationPointCut;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public int getMaximumBitrate() {
+ return maximumBitrate;
+ }
+
+ public void setMaximumBitrate(int maximumBitrate) {
+ this.maximumBitrate = maximumBitrate;
+ }
+
+ public int getMinimumBitrate() {
+ return minimumBitrate;
+ }
+
+ public void setMinimumBitrate(int minimumBitrate) {
+ this.minimumBitrate = minimumBitrate;
+ }
+
+ public short getDiscardPriority() {
+ return discardPriority;
+ }
+
+ public void setDiscardPriority(short discardPriority) {
+ this.discardPriority = discardPriority;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base
new file mode 100644
index 0000000..bd5b89e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/RollRecoveryEntry.java.svn-base
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * roll_distance is a signed integer that gives the number of samples that must be decoded in order for
+ * a sample to be decoded correctly. A positive value indicates the number of samples after the sample
+ * that is a group member that must be decoded such that at the last of these recovery is complete, i.e.
+ * the last sample is correct. A negative value indicates the number of samples before the sample that is
+ * a group member that must be decoded in order for recovery to be complete at the marked sample.
+ * The value zero must not be used; the sync sample table documents random access points for which
+ * no recovery roll is needed.
+ */
+public class RollRecoveryEntry extends GroupEntry {
+ public static final String TYPE = "roll";
+ private short rollDistance;
+
+ public short getRollDistance() {
+ return rollDistance;
+ }
+
+ public void setRollDistance(short rollDistance) {
+ this.rollDistance = rollDistance;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ rollDistance = byteBuffer.getShort();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(2);
+ content.putShort(rollDistance);
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RollRecoveryEntry entry = (RollRecoveryEntry) o;
+
+ if (rollDistance != entry.rollDistance) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) rollDistance;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base
new file mode 100644
index 0000000..df4a96f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleGroupDescriptionBox.java.svn-base
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This description table gives information about the characteristics of sample groups. The descriptive
+ * information is any other information needed to define or characterize the sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleGroupDescription box has a type code that distinguishes different
+ * sample groupings. Within a track, there shall be at most one instance of this box with a particular grouping
+ * type. The associated SampleToGroup shall indicate the same value for the grouping type.
+ * <p/>
+ * The information is stored in the sample group description box after the entry-count. An abstract entry type is
+ * defined and sample groupings shall define derived types to represent the description of each sample group.
+ * For video tracks, an abstract VisualSampleGroupEntry is used with similar types for audio and hint tracks.
+ */
+public class SampleGroupDescriptionBox extends AbstractFullBox {
+ public static final String TYPE = "sgpd";
+
+ private String groupingType;
+ private int defaultLength;
+ private List<GroupEntry> groupEntries = new LinkedList<GroupEntry>();
+ private int descriptionLength;
+
+ public SampleGroupDescriptionBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 8;
+ if (getVersion() == 1) {
+ size += 4;
+ }
+ size += 4; // entryCount
+ for (GroupEntry groupEntry : groupEntries) {
+ if (getVersion() == 1 && defaultLength == 0) {
+ size += 4;
+ }
+ size += groupEntry.size();
+ }
+ return size;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ IsoTypeWriter.writeUInt32(byteBuffer, defaultLength);
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, this.groupEntries.size());
+ for (GroupEntry entry : groupEntries) {
+ if (this.getVersion() == 1 && defaultLength == 0) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.get().limit());
+ }
+ byteBuffer.put(entry.get());
+ }
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ if (this.getVersion() != 1) {
+ throw new RuntimeException("SampleGroupDescriptionBox are only supported in version 1");
+ }
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ defaultLength = l2i(IsoTypeReader.readUInt32(content));
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ int length = defaultLength;
+ if (this.getVersion() == 1) {
+ if (defaultLength == 0) {
+ descriptionLength = l2i(IsoTypeReader.readUInt32(content));
+ length = descriptionLength;
+ }
+ } else {
+ throw new RuntimeException("This should be implemented");
+ }
+ int finalPos = content.position() + length;
+ ByteBuffer parseMe = content.slice();
+ parseMe.limit(length);
+ groupEntries.add(parseGroupEntry(parseMe, groupingType));
+ content.position(finalPos);
+ }
+
+ }
+
+ private GroupEntry parseGroupEntry(ByteBuffer content, String groupingType) {
+ GroupEntry groupEntry;
+ if (RollRecoveryEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RollRecoveryEntry();
+ } else if (RateShareEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RateShareEntry();
+ } else if (CencSampleEncryptionInformationGroupEntry.TYPE.equals(groupingType)) {
+ groupEntry = new CencSampleEncryptionInformationGroupEntry();
+ } else if (VisualRandomAccessEntry.TYPE.equals(groupingType)) {
+ groupEntry = new VisualRandomAccessEntry();
+ } else if (TemporalLevelEntry.TYPE.equals(groupingType)) {
+ groupEntry = new TemporalLevelEntry();
+ } else {
+ groupEntry = new UnknownEntry();
+ }
+ groupEntry.parse(content);
+ return groupEntry;
+ }
+
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public int getDefaultLength() {
+ return defaultLength;
+ }
+
+ public void setDefaultLength(int defaultLength) {
+ this.defaultLength = defaultLength;
+ }
+
+ public List<GroupEntry> getGroupEntries() {
+ return groupEntries;
+ }
+
+ public void setGroupEntries(List<GroupEntry> groupEntries) {
+ this.groupEntries = groupEntries;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ SampleGroupDescriptionBox that = (SampleGroupDescriptionBox) o;
+
+ if (defaultLength != that.defaultLength) {
+ return false;
+ }
+ if (groupEntries != null ? !groupEntries.equals(that.groupEntries) : that.groupEntries != null) {
+ return false;
+ }
+ if (groupingType != null ? !groupingType.equals(that.groupingType) : that.groupingType != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = groupingType != null ? groupingType.hashCode() : 0;
+ result = 31 * result + defaultLength;
+ result = 31 * result + (groupEntries != null ? groupEntries.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "SampleGroupDescriptionBox{" +
+ "groupingType='" + groupingType + '\'' +
+ ", defaultLength=" + defaultLength +
+ ", groupEntries=" + groupEntries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base
new file mode 100644
index 0000000..0fa059e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/SampleToGroupBox.java.svn-base
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This table can be used to find the group that a sample belongs to and the associated description of that
+ * sample group. The table is compactly coded with each entry giving the index of the first sample of a run of
+ * samples with the same sample group descriptor. The sample group description ID is an index that refers to a
+ * SampleGroupDescription box, which contains entries describing the characteristics of each sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleToGroup box has a type code that distinguishes different sample
+ * groupings. Within a track, there shall be at most one instance of this box with a particular grouping type. The
+ * associated SampleGroupDescription shall indicate the same value for the grouping type.
+ * <p/>
+ * Version 1 of this box should only be used if a grouping type parameter is needed.
+ */
+public class SampleToGroupBox extends AbstractFullBox {
+ public static final String TYPE = "sbgp";
+
+
+ private String groupingType;
+ private String groupingTypeParameter;
+
+ List<Entry> entries = new LinkedList<Entry>();
+
+ public SampleToGroupBox() {
+ super(TYPE);
+
+ }
+
+ @Override
+ protected long getContentSize() {
+ return this.getVersion() == 1 ? entries.size() * 8 + 16 : entries.size() * 8 + 12;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ byteBuffer.put(groupingTypeParameter.getBytes());
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getSampleCount());
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getGroupDescriptionIndex());
+ }
+
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ groupingTypeParameter = IsoTypeReader.read4cc(content);
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(content)), l2i(IsoTypeReader.readUInt32(content))));
+ }
+ }
+
+ public static class Entry {
+ private long sampleCount;
+ private int groupDescriptionIndex;
+
+ public Entry(long sampleCount, int groupDescriptionIndex) {
+ this.sampleCount = sampleCount;
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ public long getSampleCount() {
+ return sampleCount;
+ }
+
+ public void setSampleCount(long sampleCount) {
+ this.sampleCount = sampleCount;
+ }
+
+ public int getGroupDescriptionIndex() {
+ return groupDescriptionIndex;
+ }
+
+ public void setGroupDescriptionIndex(int groupDescriptionIndex) {
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "sampleCount=" + sampleCount +
+ ", groupDescriptionIndex=" + groupDescriptionIndex +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (groupDescriptionIndex != entry.groupDescriptionIndex) {
+ return false;
+ }
+ if (sampleCount != entry.sampleCount) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (sampleCount ^ (sampleCount >>> 32));
+ result = 31 * result + groupDescriptionIndex;
+ return result;
+ }
+ }
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public String getGroupingTypeParameter() {
+ return groupingTypeParameter;
+ }
+
+ public void setGroupingTypeParameter(String groupingTypeParameter) {
+ this.groupingTypeParameter = groupingTypeParameter;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base
new file mode 100644
index 0000000..798fd9c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/TemporalLevelEntry.java.svn-base
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The Temporal Level sample grouping ('tele') provides a codec-independent sample grouping that can be used to group samples (access units) in a track (and potential track fragments) according to temporal level, where samples of one temporal level have no coding dependencies on samples of higher temporal levels. The temporal level equals the sample group description index (taking values 1, 2, 3, etc). The bitstream containing only the access units from the first temporal level to a higher temporal level remains conforming to the coding standard.
+ *
+ * A grouping according to temporal level facilitates easy extraction of temporal subsequences, for instance using the Subsegment Indexing box in 0.
+ *
+ */
+public class TemporalLevelEntry extends GroupEntry {
+ public static final String TYPE = "tele";
+ private boolean levelIndependentlyDecodable;
+ private short reserved;
+
+ public boolean isLevelIndependentlyDecodable() {
+ return levelIndependentlyDecodable;
+ }
+
+ public void setLevelIndependentlyDecodable(boolean levelIndependentlyDecodable) {
+ this.levelIndependentlyDecodable = levelIndependentlyDecodable;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ levelIndependentlyDecodable = ((b & 0x80) == 0x80);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) (levelIndependentlyDecodable ? 0x80 : 0x00));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TemporalLevelEntry that = (TemporalLevelEntry) o;
+
+ if (levelIndependentlyDecodable != that.levelIndependentlyDecodable) return false;
+ if (reserved != that.reserved) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (levelIndependentlyDecodable ? 1 : 0);
+ result = 31 * result + (int) reserved;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TemporalLevelEntry");
+ sb.append("{levelIndependentlyDecodable=").append(levelIndependentlyDecodable);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base
new file mode 100644
index 0000000..9efcbea
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/UnknownEntry.java.svn-base
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ */
+public class UnknownEntry extends GroupEntry {
+ private ByteBuffer content;
+
+ public UnknownEntry() {
+ }
+
+ public ByteBuffer getContent() {
+ return content;
+ }
+
+ public void setContent(ByteBuffer content) {
+ this.content = (ByteBuffer) content.duplicate().rewind();
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ this.content = (ByteBuffer) byteBuffer.duplicate().rewind();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ return content.duplicate();
+ }
+
+ @Override
+ public String toString() {
+ ByteBuffer bb = content.duplicate();
+ bb.rewind();
+ byte[] b = new byte[bb.limit()];
+ bb.get(b);
+ return "UnknownEntry{" +
+ "content=" + Hex.encodeHex(b) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ UnknownEntry that = (UnknownEntry) o;
+
+ if (content != null ? !content.equals(that.content) : that.content != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return content != null ? content.hashCode() : 0;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base
new file mode 100644
index 0000000..ed5d199
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/.svn/text-base/VisualRandomAccessEntry.java.svn-base
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.nio.ByteBuffer;
+
+/**
+ * For some coding systems a sync sample is specified to be a random access point after which all samples in decoding order can be correctly decoded. However, it may be possible to encode an “open” random access point, after which all samples in output order can be correctly decoded, but some samples following the random access point in decoding order and preceding the random access point in output order need not be correctly decodable. For example, an intra picture starting an open group of pictures can be followed in decoding order by (bi-)predicted pictures that however precede the intra picture in output order; though they possibly cannot be correctly decoded if the decoding starts from the intra picture, they are not needed.
+ *
+ * Such “open” random-access samples can be marked by being a member of this group. Samples marked by this group must be random access points, and may also be sync points (i.e. it is not required that samples marked by the sync sample table be excluded).
+ *
+ */
+public class VisualRandomAccessEntry extends GroupEntry {
+ public static final String TYPE = "rap ";
+ private boolean numLeadingSamplesKnown;
+ private short numLeadingSamples;
+
+ public boolean isNumLeadingSamplesKnown() {
+ return numLeadingSamplesKnown;
+ }
+
+ public void setNumLeadingSamplesKnown(boolean numLeadingSamplesKnown) {
+ this.numLeadingSamplesKnown = numLeadingSamplesKnown;
+ }
+
+ public short getNumLeadingSamples() {
+ return numLeadingSamples;
+ }
+
+ public void setNumLeadingSamples(short numLeadingSamples) {
+ this.numLeadingSamples = numLeadingSamples;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ numLeadingSamplesKnown = ((b & 0x80) == 0x80);
+ numLeadingSamples = (short) (b & 0x7f);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) ((numLeadingSamplesKnown? 0x80 : 0x00)| (numLeadingSamples & 0x7f)));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ VisualRandomAccessEntry that = (VisualRandomAccessEntry) o;
+
+ if (numLeadingSamples != that.numLeadingSamples) return false;
+ if (numLeadingSamplesKnown != that.numLeadingSamplesKnown) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (numLeadingSamplesKnown ? 1 : 0);
+ result = 31 * result + (int) numLeadingSamples;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("VisualRandomAccessEntry");
+ sb.append("{numLeadingSamplesKnown=").append(numLeadingSamplesKnown);
+ sb.append(", numLeadingSamples=").append(numLeadingSamples);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java
new file mode 100644
index 0000000..b54f4d9
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/CencSampleEncryptionInformationGroupEntry.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Each sample in a protected track shall be associated with an IsEncrypted flag, IV_Size, and KID.
+ * This can be accomplished by (a) relying on the default values in the TrackEncryptionBox
+ * (see 8.2), or (b) specifying the parameters by sample group, or (c) using a combination of these two techniques.
+ * <p/>
+ * When specifying the parameters by sample group, the SampleToGroupBox in the sample table or track
+ * fragment specifies which samples use which sample group description from the SampleGroupDescriptionBox.
+ */
+public class CencSampleEncryptionInformationGroupEntry extends GroupEntry {
+ public static final String TYPE = "seig";
+
+ private int isEncrypted;
+ private byte ivSize;
+ private byte[] kid = new byte[16];
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ isEncrypted = IsoTypeReader.readUInt24(byteBuffer);
+ ivSize = (byte) IsoTypeReader.readUInt8(byteBuffer);
+ kid = new byte[16];
+ byteBuffer.get(kid);
+
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(20);
+ IsoTypeWriter.writeUInt24(byteBuffer, isEncrypted);
+ IsoTypeWriter.writeUInt8(byteBuffer, ivSize);
+ byteBuffer.put(kid);
+ byteBuffer.rewind();
+ return byteBuffer;
+ }
+
+ public int getEncrypted() {
+ return isEncrypted;
+ }
+
+ public void setEncrypted(int encrypted) {
+ isEncrypted = encrypted;
+ }
+
+ public byte getIvSize() {
+ return ivSize;
+ }
+
+ public void setIvSize(byte ivSize) {
+ this.ivSize = ivSize;
+ }
+
+ public byte[] getKid() {
+ return kid;
+ }
+
+ public void setKid(byte[] kid) {
+ assert kid.length == 16;
+ this.kid = kid;
+ }
+
+ @Override
+ public String toString() {
+ return "CencSampleEncryptionInformationGroupEntry{" +
+ "isEncrypted=" + isEncrypted +
+ ", ivSize=" + ivSize +
+ ", kid=" + Hex.encodeHex(kid) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CencSampleEncryptionInformationGroupEntry that = (CencSampleEncryptionInformationGroupEntry) o;
+
+ if (isEncrypted != that.isEncrypted) {
+ return false;
+ }
+ if (ivSize != that.ivSize) {
+ return false;
+ }
+ if (!Arrays.equals(kid, that.kid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = isEncrypted;
+ result = 31 * result + (int) ivSize;
+ result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0);
+ return result;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java
new file mode 100644
index 0000000..0d78d25
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/GroupEntry.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+public abstract class GroupEntry {
+ public abstract void parse(ByteBuffer byteBuffer);
+ public abstract ByteBuffer get();
+
+ public int size() {
+ return get().limit();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java
new file mode 100644
index 0000000..ae5d380
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RateShareEntry.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * Each sample of a track may be associated to (zero or) one of a number of sample group descriptions, each of
+ * which defines a record of rate-share information. Typically the same rate-share information applies to many
+ * consecutive samples and it may therefore be enough to define two or three sample group descriptions that
+ * can be used at different time intervals.
+ * <p/>
+ * The grouping type 'rash' (short for rate share) is defined as the grouping criterion for rate share information.
+ * Zero or one sample-to-group box ('sbgp') for the grouping type 'rash' can be contained in the sample
+ * table box ('stbl') of a track. It shall reside in a hint track, if a hint track is used, otherwise in a media track.
+ * <p/>
+ * Target rate share may be specified for several operation points that are defined in terms of the total available
+ * bitrate, i.e., the bitrate that should be shared. If only one operation point is defined, the target rate share
+ * applies to all available bitrates. If several operation points are defined, then each operation point specifies a
+ * target rate share. Target rate share values specified for the first and the last operation points also specify the
+ * target rate share values at lower and higher available bitrates, respectively. The target rate share between two
+ * operation points is specified to be in the range between the target rate shares of those operation points. One
+ * possibility is to estimate with linear interpolation.
+ */
+public class RateShareEntry extends GroupEntry {
+ public static final String TYPE = "rash";
+
+ private short operationPointCut;
+ private short targetRateShare;
+ private List<Entry> entries = new LinkedList<Entry>();
+ private int maximumBitrate;
+ private int minimumBitrate;
+ private short discardPriority;
+
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ operationPointCut = byteBuffer.getShort();
+ if (operationPointCut == 1) {
+ targetRateShare = byteBuffer.getShort();
+ } else {
+ int entriesLeft = operationPointCut;
+ while (entriesLeft-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(byteBuffer)), byteBuffer.getShort()));
+ }
+ }
+ maximumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ minimumBitrate = l2i(IsoTypeReader.readUInt32(byteBuffer));
+ discardPriority = (short) IsoTypeReader.readUInt8(byteBuffer);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer buf = ByteBuffer.allocate(operationPointCut == 1?13:(operationPointCut * 6 + 11 ));
+ buf.putShort(operationPointCut);
+ if (operationPointCut == 1) {
+ buf.putShort(targetRateShare );
+ } else {
+ for (Entry entry : entries) {
+ buf.putInt(entry.getAvailableBitrate());
+ buf.putShort(entry.getTargetRateShare());
+ }
+ }
+ buf.putInt(maximumBitrate);
+ buf.putInt(minimumBitrate);
+ IsoTypeWriter.writeUInt8(buf, discardPriority);
+ buf.rewind();
+ return buf;
+ }
+
+ public static class Entry {
+ public Entry(int availableBitrate, short targetRateShare) {
+ this.availableBitrate = availableBitrate;
+ this.targetRateShare = targetRateShare;
+ }
+
+ int availableBitrate;
+ short targetRateShare;
+
+ @Override
+ public String toString() {
+ return "{" +
+ "availableBitrate=" + availableBitrate +
+ ", targetRateShare=" + targetRateShare +
+ '}';
+ }
+
+ public int getAvailableBitrate() {
+ return availableBitrate;
+ }
+
+ public void setAvailableBitrate(int availableBitrate) {
+ this.availableBitrate = availableBitrate;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (availableBitrate != entry.availableBitrate) {
+ return false;
+ }
+ if (targetRateShare != entry.targetRateShare) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = availableBitrate;
+ result = 31 * result + (int) targetRateShare;
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RateShareEntry that = (RateShareEntry) o;
+
+ if (discardPriority != that.discardPriority) {
+ return false;
+ }
+ if (maximumBitrate != that.maximumBitrate) {
+ return false;
+ }
+ if (minimumBitrate != that.minimumBitrate) {
+ return false;
+ }
+ if (operationPointCut != that.operationPointCut) {
+ return false;
+ }
+ if (targetRateShare != that.targetRateShare) {
+ return false;
+ }
+ if (entries != null ? !entries.equals(that.entries) : that.entries != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) operationPointCut;
+ result = 31 * result + (int) targetRateShare;
+ result = 31 * result + (entries != null ? entries.hashCode() : 0);
+ result = 31 * result + maximumBitrate;
+ result = 31 * result + minimumBitrate;
+ result = 31 * result + (int) discardPriority;
+ return result;
+ }
+
+ public short getOperationPointCut() {
+ return operationPointCut;
+ }
+
+ public void setOperationPointCut(short operationPointCut) {
+ this.operationPointCut = operationPointCut;
+ }
+
+ public short getTargetRateShare() {
+ return targetRateShare;
+ }
+
+ public void setTargetRateShare(short targetRateShare) {
+ this.targetRateShare = targetRateShare;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+
+ public int getMaximumBitrate() {
+ return maximumBitrate;
+ }
+
+ public void setMaximumBitrate(int maximumBitrate) {
+ this.maximumBitrate = maximumBitrate;
+ }
+
+ public int getMinimumBitrate() {
+ return minimumBitrate;
+ }
+
+ public void setMinimumBitrate(int minimumBitrate) {
+ this.minimumBitrate = minimumBitrate;
+ }
+
+ public short getDiscardPriority() {
+ return discardPriority;
+ }
+
+ public void setDiscardPriority(short discardPriority) {
+ this.discardPriority = discardPriority;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java
new file mode 100644
index 0000000..bd5b89e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/RollRecoveryEntry.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * roll_distance is a signed integer that gives the number of samples that must be decoded in order for
+ * a sample to be decoded correctly. A positive value indicates the number of samples after the sample
+ * that is a group member that must be decoded such that at the last of these recovery is complete, i.e.
+ * the last sample is correct. A negative value indicates the number of samples before the sample that is
+ * a group member that must be decoded in order for recovery to be complete at the marked sample.
+ * The value zero must not be used; the sync sample table documents random access points for which
+ * no recovery roll is needed.
+ */
+public class RollRecoveryEntry extends GroupEntry {
+ public static final String TYPE = "roll";
+ private short rollDistance;
+
+ public short getRollDistance() {
+ return rollDistance;
+ }
+
+ public void setRollDistance(short rollDistance) {
+ this.rollDistance = rollDistance;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ rollDistance = byteBuffer.getShort();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(2);
+ content.putShort(rollDistance);
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RollRecoveryEntry entry = (RollRecoveryEntry) o;
+
+ if (rollDistance != entry.rollDistance) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) rollDistance;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java
new file mode 100644
index 0000000..df4a96f
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleGroupDescriptionBox.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This description table gives information about the characteristics of sample groups. The descriptive
+ * information is any other information needed to define or characterize the sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleGroupDescription box has a type code that distinguishes different
+ * sample groupings. Within a track, there shall be at most one instance of this box with a particular grouping
+ * type. The associated SampleToGroup shall indicate the same value for the grouping type.
+ * <p/>
+ * The information is stored in the sample group description box after the entry-count. An abstract entry type is
+ * defined and sample groupings shall define derived types to represent the description of each sample group.
+ * For video tracks, an abstract VisualSampleGroupEntry is used with similar types for audio and hint tracks.
+ */
+public class SampleGroupDescriptionBox extends AbstractFullBox {
+ public static final String TYPE = "sgpd";
+
+ private String groupingType;
+ private int defaultLength;
+ private List<GroupEntry> groupEntries = new LinkedList<GroupEntry>();
+ private int descriptionLength;
+
+ public SampleGroupDescriptionBox() {
+ super(TYPE);
+ }
+
+ @Override
+ protected long getContentSize() {
+ long size = 8;
+ if (getVersion() == 1) {
+ size += 4;
+ }
+ size += 4; // entryCount
+ for (GroupEntry groupEntry : groupEntries) {
+ if (getVersion() == 1 && defaultLength == 0) {
+ size += 4;
+ }
+ size += groupEntry.size();
+ }
+ return size;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ IsoTypeWriter.writeUInt32(byteBuffer, defaultLength);
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, this.groupEntries.size());
+ for (GroupEntry entry : groupEntries) {
+ if (this.getVersion() == 1 && defaultLength == 0) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.get().limit());
+ }
+ byteBuffer.put(entry.get());
+ }
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ if (this.getVersion() != 1) {
+ throw new RuntimeException("SampleGroupDescriptionBox are only supported in version 1");
+ }
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ defaultLength = l2i(IsoTypeReader.readUInt32(content));
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ int length = defaultLength;
+ if (this.getVersion() == 1) {
+ if (defaultLength == 0) {
+ descriptionLength = l2i(IsoTypeReader.readUInt32(content));
+ length = descriptionLength;
+ }
+ } else {
+ throw new RuntimeException("This should be implemented");
+ }
+ int finalPos = content.position() + length;
+ ByteBuffer parseMe = content.slice();
+ parseMe.limit(length);
+ groupEntries.add(parseGroupEntry(parseMe, groupingType));
+ content.position(finalPos);
+ }
+
+ }
+
+ private GroupEntry parseGroupEntry(ByteBuffer content, String groupingType) {
+ GroupEntry groupEntry;
+ if (RollRecoveryEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RollRecoveryEntry();
+ } else if (RateShareEntry.TYPE.equals(groupingType)) {
+ groupEntry = new RateShareEntry();
+ } else if (CencSampleEncryptionInformationGroupEntry.TYPE.equals(groupingType)) {
+ groupEntry = new CencSampleEncryptionInformationGroupEntry();
+ } else if (VisualRandomAccessEntry.TYPE.equals(groupingType)) {
+ groupEntry = new VisualRandomAccessEntry();
+ } else if (TemporalLevelEntry.TYPE.equals(groupingType)) {
+ groupEntry = new TemporalLevelEntry();
+ } else {
+ groupEntry = new UnknownEntry();
+ }
+ groupEntry.parse(content);
+ return groupEntry;
+ }
+
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public int getDefaultLength() {
+ return defaultLength;
+ }
+
+ public void setDefaultLength(int defaultLength) {
+ this.defaultLength = defaultLength;
+ }
+
+ public List<GroupEntry> getGroupEntries() {
+ return groupEntries;
+ }
+
+ public void setGroupEntries(List<GroupEntry> groupEntries) {
+ this.groupEntries = groupEntries;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ SampleGroupDescriptionBox that = (SampleGroupDescriptionBox) o;
+
+ if (defaultLength != that.defaultLength) {
+ return false;
+ }
+ if (groupEntries != null ? !groupEntries.equals(that.groupEntries) : that.groupEntries != null) {
+ return false;
+ }
+ if (groupingType != null ? !groupingType.equals(that.groupingType) : that.groupingType != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = groupingType != null ? groupingType.hashCode() : 0;
+ result = 31 * result + defaultLength;
+ result = 31 * result + (groupEntries != null ? groupEntries.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "SampleGroupDescriptionBox{" +
+ "groupingType='" + groupingType + '\'' +
+ ", defaultLength=" + defaultLength +
+ ", groupEntries=" + groupEntries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java
new file mode 100644
index 0000000..0fa059e
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/SampleToGroupBox.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+import com.coremedia.iso.IsoTypeWriter;
+import com.googlecode.mp4parser.AbstractFullBox;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+
+/**
+ * This table can be used to find the group that a sample belongs to and the associated description of that
+ * sample group. The table is compactly coded with each entry giving the index of the first sample of a run of
+ * samples with the same sample group descriptor. The sample group description ID is an index that refers to a
+ * SampleGroupDescription box, which contains entries describing the characteristics of each sample group.
+ * <p/>
+ * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
+ * track. Each instance of the SampleToGroup box has a type code that distinguishes different sample
+ * groupings. Within a track, there shall be at most one instance of this box with a particular grouping type. The
+ * associated SampleGroupDescription shall indicate the same value for the grouping type.
+ * <p/>
+ * Version 1 of this box should only be used if a grouping type parameter is needed.
+ */
+public class SampleToGroupBox extends AbstractFullBox {
+ public static final String TYPE = "sbgp";
+
+
+ private String groupingType;
+ private String groupingTypeParameter;
+
+ List<Entry> entries = new LinkedList<Entry>();
+
+ public SampleToGroupBox() {
+ super(TYPE);
+
+ }
+
+ @Override
+ protected long getContentSize() {
+ return this.getVersion() == 1 ? entries.size() * 8 + 16 : entries.size() * 8 + 12;
+ }
+
+ @Override
+ protected void getContent(ByteBuffer byteBuffer) {
+ writeVersionAndFlags(byteBuffer);
+ byteBuffer.put(groupingType.getBytes());
+ if (this.getVersion() == 1) {
+ byteBuffer.put(groupingTypeParameter.getBytes());
+ }
+ IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
+ for (Entry entry : entries) {
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getSampleCount());
+ IsoTypeWriter.writeUInt32(byteBuffer, entry.getGroupDescriptionIndex());
+ }
+
+ }
+
+ @Override
+ protected void _parseDetails(ByteBuffer content) {
+ parseVersionAndFlags(content);
+ groupingType = IsoTypeReader.read4cc(content);
+ if (this.getVersion() == 1) {
+ groupingTypeParameter = IsoTypeReader.read4cc(content);
+ }
+ long entryCount = IsoTypeReader.readUInt32(content);
+ while (entryCount-- > 0) {
+ entries.add(new Entry(l2i(IsoTypeReader.readUInt32(content)), l2i(IsoTypeReader.readUInt32(content))));
+ }
+ }
+
+ public static class Entry {
+ private long sampleCount;
+ private int groupDescriptionIndex;
+
+ public Entry(long sampleCount, int groupDescriptionIndex) {
+ this.sampleCount = sampleCount;
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ public long getSampleCount() {
+ return sampleCount;
+ }
+
+ public void setSampleCount(long sampleCount) {
+ this.sampleCount = sampleCount;
+ }
+
+ public int getGroupDescriptionIndex() {
+ return groupDescriptionIndex;
+ }
+
+ public void setGroupDescriptionIndex(int groupDescriptionIndex) {
+ this.groupDescriptionIndex = groupDescriptionIndex;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{" +
+ "sampleCount=" + sampleCount +
+ ", groupDescriptionIndex=" + groupDescriptionIndex +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+
+ if (groupDescriptionIndex != entry.groupDescriptionIndex) {
+ return false;
+ }
+ if (sampleCount != entry.sampleCount) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (sampleCount ^ (sampleCount >>> 32));
+ result = 31 * result + groupDescriptionIndex;
+ return result;
+ }
+ }
+
+ public String getGroupingType() {
+ return groupingType;
+ }
+
+ public void setGroupingType(String groupingType) {
+ this.groupingType = groupingType;
+ }
+
+ public String getGroupingTypeParameter() {
+ return groupingTypeParameter;
+ }
+
+ public void setGroupingTypeParameter(String groupingTypeParameter) {
+ this.groupingTypeParameter = groupingTypeParameter;
+ }
+
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ public void setEntries(List<Entry> entries) {
+ this.entries = entries;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java
new file mode 100644
index 0000000..798fd9c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/TemporalLevelEntry.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The Temporal Level sample grouping ('tele') provides a codec-independent sample grouping that can be used to group samples (access units) in a track (and potential track fragments) according to temporal level, where samples of one temporal level have no coding dependencies on samples of higher temporal levels. The temporal level equals the sample group description index (taking values 1, 2, 3, etc). The bitstream containing only the access units from the first temporal level to a higher temporal level remains conforming to the coding standard.
+ *
+ * A grouping according to temporal level facilitates easy extraction of temporal subsequences, for instance using the Subsegment Indexing box in 0.
+ *
+ */
+public class TemporalLevelEntry extends GroupEntry {
+ public static final String TYPE = "tele";
+ private boolean levelIndependentlyDecodable;
+ private short reserved;
+
+ public boolean isLevelIndependentlyDecodable() {
+ return levelIndependentlyDecodable;
+ }
+
+ public void setLevelIndependentlyDecodable(boolean levelIndependentlyDecodable) {
+ this.levelIndependentlyDecodable = levelIndependentlyDecodable;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ levelIndependentlyDecodable = ((b & 0x80) == 0x80);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) (levelIndependentlyDecodable ? 0x80 : 0x00));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TemporalLevelEntry that = (TemporalLevelEntry) o;
+
+ if (levelIndependentlyDecodable != that.levelIndependentlyDecodable) return false;
+ if (reserved != that.reserved) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (levelIndependentlyDecodable ? 1 : 0);
+ result = 31 * result + (int) reserved;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TemporalLevelEntry");
+ sb.append("{levelIndependentlyDecodable=").append(levelIndependentlyDecodable);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java
new file mode 100644
index 0000000..9efcbea
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/UnknownEntry.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.Hex;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ */
+public class UnknownEntry extends GroupEntry {
+ private ByteBuffer content;
+
+ public UnknownEntry() {
+ }
+
+ public ByteBuffer getContent() {
+ return content;
+ }
+
+ public void setContent(ByteBuffer content) {
+ this.content = (ByteBuffer) content.duplicate().rewind();
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ this.content = (ByteBuffer) byteBuffer.duplicate().rewind();
+ }
+
+ @Override
+ public ByteBuffer get() {
+ return content.duplicate();
+ }
+
+ @Override
+ public String toString() {
+ ByteBuffer bb = content.duplicate();
+ bb.rewind();
+ byte[] b = new byte[bb.limit()];
+ bb.get(b);
+ return "UnknownEntry{" +
+ "content=" + Hex.encodeHex(b) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ UnknownEntry that = (UnknownEntry) o;
+
+ if (content != null ? !content.equals(that.content) : that.content != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return content != null ? content.hashCode() : 0;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java
new file mode 100644
index 0000000..ed5d199
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/boxes/mp4/samplegrouping/VisualRandomAccessEntry.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 castLabs, Berlin
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
+
+import com.coremedia.iso.IsoTypeReader;
+
+import java.nio.ByteBuffer;
+
+/**
+ * For some coding systems a sync sample is specified to be a random access point after which all samples in decoding order can be correctly decoded. However, it may be possible to encode an “open” random access point, after which all samples in output order can be correctly decoded, but some samples following the random access point in decoding order and preceding the random access point in output order need not be correctly decodable. For example, an intra picture starting an open group of pictures can be followed in decoding order by (bi-)predicted pictures that however precede the intra picture in output order; though they possibly cannot be correctly decoded if the decoding starts from the intra picture, they are not needed.
+ *
+ * Such “open” random-access samples can be marked by being a member of this group. Samples marked by this group must be random access points, and may also be sync points (i.e. it is not required that samples marked by the sync sample table be excluded).
+ *
+ */
+public class VisualRandomAccessEntry extends GroupEntry {
+ public static final String TYPE = "rap ";
+ private boolean numLeadingSamplesKnown;
+ private short numLeadingSamples;
+
+ public boolean isNumLeadingSamplesKnown() {
+ return numLeadingSamplesKnown;
+ }
+
+ public void setNumLeadingSamplesKnown(boolean numLeadingSamplesKnown) {
+ this.numLeadingSamplesKnown = numLeadingSamplesKnown;
+ }
+
+ public short getNumLeadingSamples() {
+ return numLeadingSamples;
+ }
+
+ public void setNumLeadingSamples(short numLeadingSamples) {
+ this.numLeadingSamples = numLeadingSamples;
+ }
+
+ @Override
+ public void parse(ByteBuffer byteBuffer) {
+ final byte b = byteBuffer.get();
+ numLeadingSamplesKnown = ((b & 0x80) == 0x80);
+ numLeadingSamples = (short) (b & 0x7f);
+ }
+
+ @Override
+ public ByteBuffer get() {
+ ByteBuffer content = ByteBuffer.allocate(1);
+ content.put((byte) ((numLeadingSamplesKnown? 0x80 : 0x00)| (numLeadingSamples & 0x7f)));
+ content.rewind();
+ return content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ VisualRandomAccessEntry that = (VisualRandomAccessEntry) o;
+
+ if (numLeadingSamples != that.numLeadingSamples) return false;
+ if (numLeadingSamplesKnown != that.numLeadingSamplesKnown) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (numLeadingSamplesKnown ? 1 : 0);
+ result = 31 * result + (int) numLeadingSamples;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("VisualRandomAccessEntry");
+ sb.append("{numLeadingSamplesKnown=").append(numLeadingSamplesKnown);
+ sb.append(", numLeadingSamples=").append(numLeadingSamples);
+ sb.append('}');
+ return sb.toString();
+ }
+}