// Copyright 2024 Martin Riedl // // 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 gomp4 import ( "encoding/binary" "fmt" ) // SampleToChunkBox sample to chunk box struct // // 8.7.4 Sample To Chunk Box // // Box Type: ‘stsc’ // Container: Sample Table Box (‘stbl’) // Mandatory: Yes // Quantity: Exactly one // // Samples within the media data are grouped into chunks. Chunks can be of different sizes, and the // samples within a chunk can have different sizes. This table can be used to find the chunk that contains a // sample, its position, and the associated sample description. // // The table is compactly coded. Each entry gives the index of the first chunk of a run of chunks with the // same characteristics. By subtracting one entry here from the previous one, you can compute how many // chunks are in this run. You can convert this to a sample count by multiplying by the appropriate // samples‐per‐chunk. type SampleToChunkBox struct { *FullBox // is an integer that gives the number of entries in the following table EntryCount uint32 Entries []SampleToChunkBoxEntry } type SampleToChunkBoxEntry struct { // is an integer that gives the index of the first chunk in this run of chunks that share // the same samples‐per‐chunk and sample‐description‐index; the index of the first chunk in a // track has the value 1 (the first_chunk field in the first record of this box has the value 1, // identifying that the first sample maps to the first chunk). FirstChunk uint32 // is an integer that gives the number of samples in each of these chunks SamplesPerChunk uint32 // is an integer that gives the index of the sample entry that // describes the samples in this chunk. The index ranges from 1 to the number of sample entries in // the Sample Description Box SampleDescriptionIndex uint32 } // BoxTypeSampleToChunk Sample To Chunk Box const BoxTypeSampleToChunk = "stsc" func init() { BoxDefinitions = append(BoxDefinitions, BoxDefinition{ Type: BoxTypeSampleToChunk, ParentTypes: []string{BoxTypeSampleTable}, Parser: ParseSampleToChunkBox, }) } // ParseSampleToChunkBox creates a new sample to chunk box struct based on bytes func ParseSampleToChunkBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &SampleToChunkBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } // parse entry counter box.EntryCount = binary.BigEndian.Uint32(content[4:8]) // parse entries position := 8 for i := 0; i < int(box.EntryCount); i++ { newSampleEntry := SampleToChunkBoxEntry{ FirstChunk: binary.BigEndian.Uint32(content[position+(i*12) : position+4+(i*12)]), SamplesPerChunk: binary.BigEndian.Uint32(content[position+4+(i*12) : position+8+(i*12)]), SampleDescriptionIndex: binary.BigEndian.Uint32(content[position+8+(i*12) : position+12+(i*12)]), } box.Entries = append(box.Entries, newSampleEntry) } // validate entries amount if len(box.Entries) != int(box.EntryCount) { return box, fmt.Errorf("invalid amount of entries at %d; got %d but expected %d", filePosition, len(box.Entries), box.EntryCount) } return box, nil }