// 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" ) // TimeToSampleBox time to sample box struct // // 8.6.1.2 Decoding Time to Sample Box // // Box Type: ‘stts’ // Container: Sample Table Box (‘stbl’) // Mandatory: Yes // Quantity: Exactly one // // This box contains a compact version of a table that allows indexing from decoding time to sample // number. Other tables give sample sizes and pointers, from the sample number. Each entry in the table // gives the number of consecutive samples with the same time delta, and the delta of those samples. By // adding the deltas a complete time‐to‐sample map may be built. // // The Decoding Time to Sample Box contains decode time delta's: DT(n+1) = DT(n) + STTS(n) where // STTS(n) is the (uncompressed) table entry for sample n. // // The sample entries are ordered by decoding time stamps; therefore the deltas are all non‐negative. // // The DT axis has a zero origin; DT(i) = SUM(for j=0 to i‐1 of delta(j)), and the sum of all deltas gives the // length of the media in the track (not mapped to the overall timescale, and not considering any edit list). // // The Edit List Box provides the initial CT value if it is non‐empty (non‐zero). type TimeToSampleBox struct { *FullBox // is an integer that gives the number of entries in the following table. EntryCount uint32 Entries []TimeToSampleBoxEntry } type TimeToSampleBoxEntry struct { // s an integer that counts the number of consecutive samples that have the given duration. SampleCount uint32 // is an integer that gives the delta of these samples in the time‐scale of the media. SampleDelta uint32 } // BoxTypeDecodingTimeToSample Decoding Time To Sample Box const BoxTypeDecodingTimeToSample = "stts" func init() { BoxDefinitions = append(BoxDefinitions, BoxDefinition{ Type: BoxTypeDecodingTimeToSample, ParentTypes: []string{BoxTypeSampleTable}, Parser: ParseDecodingTimeToSampleBox, }) } // ParseDecodingTimeToSampleBox creates a new decoding time to sample box struct based on bytes func ParseDecodingTimeToSampleBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &TimeToSampleBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } // parse entry counter box.EntryCount = binary.BigEndian.Uint32(content[4:8]) // parse sample entries position := 8 for i := 0; i < int(box.EntryCount); i++ { newSampleEntry := TimeToSampleBoxEntry{ SampleCount: binary.BigEndian.Uint32(content[position+(i*8) : position+4+(i*8)]), SampleDelta: binary.BigEndian.Uint32(content[position+4+(i*8) : position+8+(i*8)]), } box.Entries = append(box.Entries, newSampleEntry) } // validate entries amount if len(box.Entries) != int(box.EntryCount) { return box, fmt.Errorf("invalid amount of sample entries at %d; got %d but expected %d", filePosition, len(box.Entries), box.EntryCount) } return box, nil }