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
17package com.googlecode.mp4parser.boxes.mp4.samplegrouping;
18
19import com.coremedia.iso.IsoTypeReader;
20import com.coremedia.iso.IsoTypeWriter;
21import com.googlecode.mp4parser.AbstractFullBox;
22
23import java.nio.ByteBuffer;
24import java.util.LinkedList;
25import java.util.List;
26
27import 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 */
42public 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