1 /* 2 * Copyright 2008 CoreMedia AG, Hamburg 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.coremedia.iso.boxes; 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.ArrayList; 25 import java.util.Collections; 26 import java.util.Iterator; 27 import java.util.LinkedList; 28 import java.util.List; 29 30 import static com.googlecode.mp4parser.util.CastUtils.l2i; 31 32 /** 33 * Samples within the media data are grouped into chunks. Chunks can be of different sizes, and the 34 * samples within a chunk can have different sizes. This table can be used to find the chunk that 35 * contains a sample, its position, and the associated sample description. Defined in ISO/IEC 14496-12. 36 */ 37 public class SampleToChunkBox extends AbstractFullBox { 38 List<Entry> entries = Collections.emptyList(); 39 40 public static final String TYPE = "stsc"; 41 SampleToChunkBox()42 public SampleToChunkBox() { 43 super(TYPE); 44 } 45 getEntries()46 public List<Entry> getEntries() { 47 return entries; 48 } 49 setEntries(List<Entry> entries)50 public void setEntries(List<Entry> entries) { 51 this.entries = entries; 52 } 53 getContentSize()54 protected long getContentSize() { 55 return entries.size() * 12 + 8; 56 } 57 58 @Override _parseDetails(ByteBuffer content)59 public void _parseDetails(ByteBuffer content) { 60 parseVersionAndFlags(content); 61 62 int entryCount = l2i(IsoTypeReader.readUInt32(content)); 63 entries = new ArrayList<Entry>(entryCount); 64 for (int i = 0; i < entryCount; i++) { 65 entries.add(new Entry( 66 IsoTypeReader.readUInt32(content), 67 IsoTypeReader.readUInt32(content), 68 IsoTypeReader.readUInt32(content))); 69 } 70 } 71 72 @Override getContent(ByteBuffer byteBuffer)73 protected void getContent(ByteBuffer byteBuffer) { 74 writeVersionAndFlags(byteBuffer); 75 IsoTypeWriter.writeUInt32(byteBuffer, entries.size()); 76 for (Entry entry : entries) { 77 IsoTypeWriter.writeUInt32(byteBuffer, entry.getFirstChunk()); 78 IsoTypeWriter.writeUInt32(byteBuffer, entry.getSamplesPerChunk()); 79 IsoTypeWriter.writeUInt32(byteBuffer, entry.getSampleDescriptionIndex()); 80 } 81 } 82 toString()83 public String toString() { 84 return "SampleToChunkBox[entryCount=" + entries.size() + "]"; 85 } 86 87 /** 88 * Decompresses the list of entries and returns the number of samples per chunk for 89 * every single chunk. 90 * 91 * @param chunkCount overall number of chunks 92 * @return number of samples per chunk 93 */ blowup(int chunkCount)94 public long[] blowup(int chunkCount) { 95 long[] numberOfSamples = new long[chunkCount]; 96 int j = 0; 97 List<SampleToChunkBox.Entry> sampleToChunkEntries = new LinkedList<Entry>(entries); 98 Collections.reverse(sampleToChunkEntries); 99 Iterator<Entry> iterator = sampleToChunkEntries.iterator(); 100 SampleToChunkBox.Entry currentEntry = iterator.next(); 101 102 for (int i = numberOfSamples.length; i > 1; i--) { 103 numberOfSamples[i - 1] = currentEntry.getSamplesPerChunk(); 104 if (i == currentEntry.getFirstChunk()) { 105 currentEntry = iterator.next(); 106 } 107 } 108 numberOfSamples[0] = currentEntry.getSamplesPerChunk(); 109 return numberOfSamples; 110 } 111 112 public static class Entry { 113 long firstChunk; 114 long samplesPerChunk; 115 long sampleDescriptionIndex; 116 Entry(long firstChunk, long samplesPerChunk, long sampleDescriptionIndex)117 public Entry(long firstChunk, long samplesPerChunk, long sampleDescriptionIndex) { 118 this.firstChunk = firstChunk; 119 this.samplesPerChunk = samplesPerChunk; 120 this.sampleDescriptionIndex = sampleDescriptionIndex; 121 } 122 getFirstChunk()123 public long getFirstChunk() { 124 return firstChunk; 125 } 126 setFirstChunk(long firstChunk)127 public void setFirstChunk(long firstChunk) { 128 this.firstChunk = firstChunk; 129 } 130 getSamplesPerChunk()131 public long getSamplesPerChunk() { 132 return samplesPerChunk; 133 } 134 setSamplesPerChunk(long samplesPerChunk)135 public void setSamplesPerChunk(long samplesPerChunk) { 136 this.samplesPerChunk = samplesPerChunk; 137 } 138 getSampleDescriptionIndex()139 public long getSampleDescriptionIndex() { 140 return sampleDescriptionIndex; 141 } 142 setSampleDescriptionIndex(long sampleDescriptionIndex)143 public void setSampleDescriptionIndex(long sampleDescriptionIndex) { 144 this.sampleDescriptionIndex = sampleDescriptionIndex; 145 } 146 147 @Override toString()148 public String toString() { 149 return "Entry{" + 150 "firstChunk=" + firstChunk + 151 ", samplesPerChunk=" + samplesPerChunk + 152 ", sampleDescriptionIndex=" + sampleDescriptionIndex + 153 '}'; 154 } 155 } 156 } 157