1 /* 2 * Copyright 2012 castLabs, Berlin 3 * 4 * Licensed under the Apache License, Version 2.0 (the License); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an AS IS BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.googlecode.mp4parser.boxes.mp4.samplegrouping; 18 19 import com.coremedia.iso.IsoTypeReader; 20 import com.coremedia.iso.IsoTypeWriter; 21 import com.googlecode.mp4parser.AbstractFullBox; 22 23 import java.nio.ByteBuffer; 24 import java.util.LinkedList; 25 import java.util.List; 26 27 import static com.googlecode.mp4parser.util.CastUtils.l2i; 28 29 /** 30 * This description table gives information about the characteristics of sample groups. The descriptive 31 * information is any other information needed to define or characterize the sample group. 32 * <p/> 33 * There may be multiple instances of this box if there is more than one sample grouping for the samples in a 34 * track. Each instance of the SampleGroupDescription box has a type code that distinguishes different 35 * sample groupings. Within a track, there shall be at most one instance of this box with a particular grouping 36 * type. The associated SampleToGroup shall indicate the same value for the grouping type. 37 * <p/> 38 * The information is stored in the sample group description box after the entry-count. An abstract entry type is 39 * defined and sample groupings shall define derived types to represent the description of each sample group. 40 * For video tracks, an abstract VisualSampleGroupEntry is used with similar types for audio and hint tracks. 41 */ 42 public class SampleGroupDescriptionBox extends AbstractFullBox { 43 public static final String TYPE = "sgpd"; 44 45 private String groupingType; 46 private int defaultLength; 47 private List<GroupEntry> groupEntries = new LinkedList<GroupEntry>(); 48 private int descriptionLength; 49 50 public SampleGroupDescriptionBox() { 51 super(TYPE); 52 } 53 54 @Override 55 protected long getContentSize() { 56 long size = 8; 57 if (getVersion() == 1) { 58 size += 4; 59 } 60 size += 4; // entryCount 61 for (GroupEntry groupEntry : groupEntries) { 62 if (getVersion() == 1 && defaultLength == 0) { 63 size += 4; 64 } 65 size += groupEntry.size(); 66 } 67 return size; 68 } 69 70 @Override 71 protected void getContent(ByteBuffer byteBuffer) { 72 writeVersionAndFlags(byteBuffer); 73 byteBuffer.put(groupingType.getBytes()); 74 if (this.getVersion() == 1) { 75 IsoTypeWriter.writeUInt32(byteBuffer, defaultLength); 76 } 77 IsoTypeWriter.writeUInt32(byteBuffer, this.groupEntries.size()); 78 for (GroupEntry entry : groupEntries) { 79 if (this.getVersion() == 1 && defaultLength == 0) { 80 IsoTypeWriter.writeUInt32(byteBuffer, entry.get().limit()); 81 } 82 byteBuffer.put(entry.get()); 83 } 84 } 85 86 @Override 87 protected void _parseDetails(ByteBuffer content) { 88 parseVersionAndFlags(content); 89 if (this.getVersion() != 1) { 90 throw new RuntimeException("SampleGroupDescriptionBox are only supported in version 1"); 91 } 92 groupingType = IsoTypeReader.read4cc(content); 93 if (this.getVersion() == 1) { 94 defaultLength = l2i(IsoTypeReader.readUInt32(content)); 95 } 96 long entryCount = IsoTypeReader.readUInt32(content); 97 while (entryCount-- > 0) { 98 int length = defaultLength; 99 if (this.getVersion() == 1) { 100 if (defaultLength == 0) { 101 descriptionLength = l2i(IsoTypeReader.readUInt32(content)); 102 length = descriptionLength; 103 } 104 } else { 105 throw new RuntimeException("This should be implemented"); 106 } 107 int finalPos = content.position() + length; 108 ByteBuffer parseMe = content.slice(); 109 parseMe.limit(length); 110 groupEntries.add(parseGroupEntry(parseMe, groupingType)); 111 content.position(finalPos); 112 } 113 114 } 115 116 private GroupEntry parseGroupEntry(ByteBuffer content, String groupingType) { 117 GroupEntry groupEntry; 118 if (RollRecoveryEntry.TYPE.equals(groupingType)) { 119 groupEntry = new RollRecoveryEntry(); 120 } else if (RateShareEntry.TYPE.equals(groupingType)) { 121 groupEntry = new RateShareEntry(); 122 } else if (CencSampleEncryptionInformationGroupEntry.TYPE.equals(groupingType)) { 123 groupEntry = new CencSampleEncryptionInformationGroupEntry(); 124 } else if (VisualRandomAccessEntry.TYPE.equals(groupingType)) { 125 groupEntry = new VisualRandomAccessEntry(); 126 } else if (TemporalLevelEntry.TYPE.equals(groupingType)) { 127 groupEntry = new TemporalLevelEntry(); 128 } else { 129 groupEntry = new UnknownEntry(); 130 } 131 groupEntry.parse(content); 132 return groupEntry; 133 } 134 135 136 public String getGroupingType() { 137 return groupingType; 138 } 139 140 public void setGroupingType(String groupingType) { 141 this.groupingType = groupingType; 142 } 143 144 public int getDefaultLength() { 145 return defaultLength; 146 } 147 148 public void setDefaultLength(int defaultLength) { 149 this.defaultLength = defaultLength; 150 } 151 152 public List<GroupEntry> getGroupEntries() { 153 return groupEntries; 154 } 155 156 public void setGroupEntries(List<GroupEntry> groupEntries) { 157 this.groupEntries = groupEntries; 158 } 159 160 @Override 161 public boolean equals(Object o) { 162 if (this == o) { 163 return true; 164 } 165 if (o == null || getClass() != o.getClass()) { 166 return false; 167 } 168 169 SampleGroupDescriptionBox that = (SampleGroupDescriptionBox) o; 170 171 if (defaultLength != that.defaultLength) { 172 return false; 173 } 174 if (groupEntries != null ? !groupEntries.equals(that.groupEntries) : that.groupEntries != null) { 175 return false; 176 } 177 if (groupingType != null ? !groupingType.equals(that.groupingType) : that.groupingType != null) { 178 return false; 179 } 180 181 return true; 182 } 183 184 @Override 185 public int hashCode() { 186 int result = groupingType != null ? groupingType.hashCode() : 0; 187 result = 31 * result + defaultLength; 188 result = 31 * result + (groupEntries != null ? groupEntries.hashCode() : 0); 189 return result; 190 } 191 192 @Override 193 public String toString() { 194 return "SampleGroupDescriptionBox{" + 195 "groupingType='" + groupingType + '\'' + 196 ", defaultLength=" + defaultLength + 197 ", groupEntries=" + groupEntries + 198 '}'; 199 } 200 } 201