// 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" "log" ) // SampleDescriptionBox sample description box struct // // 8.5.2 Sample Description Box // // Box Types: ‘stsd’ // Container: Sample Table Box (‘stbl’) // Mandatory: Yes // Quantity: Exactly one // // The sample description table gives detailed information about the coding type used, and any // initialization information needed for that coding. // // The information stored in the sample description box after the entry‐count is both track‐type specific // as documented here, and can also have variants within a track type (e.g. different codings may use // different specific information after some common fields, even within a video track). // // Which type of sample entry form is used is determined by the media handler, using a suitable form, // such as one defined in clause 12, or defined in a derived specification, or registration. // // Multiple descriptions may be used within a track. // // If the ‘format’ field of a SampleEntry is unrecognized, neither the sample description itself, nor the // associated media samples, shall be decoded. // // All string fields shall be null‐terminated, even if unused. “Optional” means there is at least one null byte. // // Entries that identify the format by MIME type, such as a TextSubtitleSampleEntry, // TextMetaDataSampleEntry, or SimpleTextSampleEntry, all of which contain a MIME type, may be used // to identify the format of streams for which a MIME type applies. A MIME type applies if the contents of // the string in the optional configuration box (without its null termination), followed by the contents of a // set of samples, starting with a sync sample and ending at the sample immediately preceding a sync // sample, are concatenated in their entirety, and the result meets the decoding requirements for // documents of that MIME type. Non‐sync samples should be used only if that format specifies the // behaviour of ‘progressive decoding’, and then the sample times indicate when the results of such // progressive decoding should be presented (according to the media type). // // In some classes derived from SampleEntry, namespace and schema_location are used both to identify // the XML document content and to declare “brand” or profile compatibility. Multiple namespace // identifiers indicate that the track conforms to the specification represented by each of the identifiers, // some of which may identify supersets of the features present. A decoder should be able to decode all the // namespaces in order to be able to decode and present correctly the media associated with this sample // entry. type SampleDescriptionBox struct { *FullBox // is an integer that gives the number of entries in the following table EntryCount uint32 ChildBoxes []any } // BoxTypeSampleDescription Sample Description Box const BoxTypeSampleDescription = "stsd" func init() { BoxDefinitions = append(BoxDefinitions, BoxDefinition{ Type: BoxTypeSampleDescription, ParentTypes: []string{BoxTypeSampleTable}, Parser: ParseSampleDescriptionBox, }) } // ParseSampleDescriptionBox creates a new sample description box struct based on bytes func ParseSampleDescriptionBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &SampleDescriptionBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } // entry counter box.EntryCount = binary.BigEndian.Uint32(content[4:8]) // parse child boxes var err error box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeSampleDescription, filePosition, content[8:]) if err != nil { return box, err } // validate entry count if int(box.EntryCount) != len(box.ChildBoxes) { return box, fmt.Errorf("invalid amount of boxes at %d; found %d but expected %d", filePosition, len(box.ChildBoxes), box.EntryCount) } return box, nil } type SampleEntry struct { *Box // is an integer that contains the index of the data reference to use to // retrieve data associated with samples that use this sample description. Data references are // stored in Data Reference Boxes. The index ranges from 1 to the number of data references. DataReferenceIndex uint16 } func ParseSampleEntry(filePosition uint64, headerSize uint32, content []byte) *SampleEntry { box := &SampleEntry{ Box: &Box{filePosition, headerSize}, } // skip reserved position := 2 * 8 // parse reference index box.DataReferenceIndex = binary.BigEndian.Uint16(content[position : position+2]) log.Println("data reference index", box.DataReferenceIndex) // TODO: remove me return box }