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 table can be used to find the group that a sample belongs to and the associated description of that
31 * sample group. The table is compactly coded with each entry giving the index of the first sample of a run of
32 * samples with the same sample group descriptor. The sample group description ID is an index that refers to a
33 * SampleGroupDescription box, which contains entries describing the characteristics of each sample group.
34 * <p/>
35 * There may be multiple instances of this box if there is more than one sample grouping for the samples in a
36 * track. Each instance of the SampleToGroup box has a type code that distinguishes different sample
37 * groupings. Within a track, there shall be at most one instance of this box with a particular grouping type. The
38 * associated SampleGroupDescription shall indicate the same value for the grouping type.
39 * <p/>
40 * Version 1 of this box should only be used if a grouping type parameter is needed.
41 */
42public class SampleToGroupBox extends AbstractFullBox {
43    public static final String TYPE = "sbgp";
44
45
46    private String groupingType;
47    private String groupingTypeParameter;
48
49    List<Entry> entries = new LinkedList<Entry>();
50
51    public SampleToGroupBox() {
52        super(TYPE);
53
54    }
55
56    @Override
57    protected long getContentSize() {
58        return this.getVersion() == 1 ? entries.size() * 8 + 16 : entries.size() * 8 + 12;
59    }
60
61    @Override
62    protected void getContent(ByteBuffer byteBuffer) {
63        writeVersionAndFlags(byteBuffer);
64        byteBuffer.put(groupingType.getBytes());
65        if (this.getVersion() == 1) {
66            byteBuffer.put(groupingTypeParameter.getBytes());
67        }
68        IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
69        for (Entry entry : entries) {
70            IsoTypeWriter.writeUInt32(byteBuffer, entry.getSampleCount());
71            IsoTypeWriter.writeUInt32(byteBuffer, entry.getGroupDescriptionIndex());
72        }
73
74    }
75
76    @Override
77    protected void _parseDetails(ByteBuffer content) {
78        parseVersionAndFlags(content);
79        groupingType = IsoTypeReader.read4cc(content);
80        if (this.getVersion() == 1) {
81            groupingTypeParameter = IsoTypeReader.read4cc(content);
82        }
83        long entryCount = IsoTypeReader.readUInt32(content);
84        while (entryCount-- > 0) {
85            entries.add(new Entry(l2i(IsoTypeReader.readUInt32(content)), l2i(IsoTypeReader.readUInt32(content))));
86        }
87    }
88
89    public static class Entry {
90        private long sampleCount;
91        private int groupDescriptionIndex;
92
93        public Entry(long sampleCount, int groupDescriptionIndex) {
94            this.sampleCount = sampleCount;
95            this.groupDescriptionIndex = groupDescriptionIndex;
96        }
97
98        public long getSampleCount() {
99            return sampleCount;
100        }
101
102        public void setSampleCount(long sampleCount) {
103            this.sampleCount = sampleCount;
104        }
105
106        public int getGroupDescriptionIndex() {
107            return groupDescriptionIndex;
108        }
109
110        public void setGroupDescriptionIndex(int groupDescriptionIndex) {
111            this.groupDescriptionIndex = groupDescriptionIndex;
112        }
113
114        @Override
115        public String toString() {
116            return "Entry{" +
117                    "sampleCount=" + sampleCount +
118                    ", groupDescriptionIndex=" + groupDescriptionIndex +
119                    '}';
120        }
121
122        @Override
123        public boolean equals(Object o) {
124            if (this == o) {
125                return true;
126            }
127            if (o == null || getClass() != o.getClass()) {
128                return false;
129            }
130
131            Entry entry = (Entry) o;
132
133            if (groupDescriptionIndex != entry.groupDescriptionIndex) {
134                return false;
135            }
136            if (sampleCount != entry.sampleCount) {
137                return false;
138            }
139
140            return true;
141        }
142
143        @Override
144        public int hashCode() {
145            int result = (int) (sampleCount ^ (sampleCount >>> 32));
146            result = 31 * result + groupDescriptionIndex;
147            return result;
148        }
149    }
150
151    public String getGroupingType() {
152        return groupingType;
153    }
154
155    public void setGroupingType(String groupingType) {
156        this.groupingType = groupingType;
157    }
158
159    public String getGroupingTypeParameter() {
160        return groupingTypeParameter;
161    }
162
163    public void setGroupingTypeParameter(String groupingTypeParameter) {
164        this.groupingTypeParameter = groupingTypeParameter;
165    }
166
167    public List<Entry> getEntries() {
168        return entries;
169    }
170
171    public void setEntries(List<Entry> entries) {
172        this.entries = entries;
173    }
174}
175