From 5a13e6352dd94f5abbb125f2adc21204c7308f4f Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Tue, 3 Dec 2024 19:24:00 +0100 Subject: [PATCH] new box scoping --- Box.go | 55 ++++++--------------------- DataInformationBox.go | 17 +++++++-- DataReferenceBox.go | 47 +++++++++++++++++++---- FileTypeBox.go | 15 +++++++- HandlerReferenceBox.go | 15 +++++++- MediaBox.go | 17 +++++++-- MediaDataBox.go | 15 +++++++- MediaHeaderBox.go | 15 +++++++- MediaInformationBox.go | 17 +++++++-- MovieBox.go | 17 +++++++-- MovieFragmentBox.go | 17 +++++++-- MovieFragmentHeaderBox.go | 15 +++++++- MovieHeaderBox.go | 15 +++++++- Parser.go | 79 ++++++++++++--------------------------- SampleDescriptionBox.go | 16 +++++++- SampleTableBox.go | 17 +++++++-- TrackBox.go | 17 +++++++-- TrackFragmentBox.go | 17 +++++++-- TrackFragmentHeaderBox.go | 15 +++++++- TrackFragmentRunBox.go | 13 ++++++- TrackHeaderBox.go | 15 +++++++- VideoMediaHeaderBox.go | 15 +++++++- go.mod | 2 +- 23 files changed, 332 insertions(+), 151 deletions(-) diff --git a/Box.go b/Box.go index 4d63089..de5c281 100644 --- a/Box.go +++ b/Box.go @@ -20,50 +20,7 @@ const ( boxSizeLength uint32 = 4 boxTypeLength uint32 = 4 - // BoxTypeFileType File Type Box - BoxTypeFileType = "ftyp" - // BoxTypeMediaData Media Data Box - BoxTypeMediaData = "mdat" - // BoxTypeMovie Movie Box - BoxTypeMovie = "moov" - // BoxTypeMovieHeader Movie Header Box - BoxTypeMovieHeader = "mvhd" - // BoxTypeTrack Track Box - BoxTypeTrack = "trak" - // BoxTypeTrackHeader Track Header Box - BoxTypeTrackHeader = "tkhd" - // BoxTypeMedia Media Box - BoxTypeMedia = "mdia" - // BoxTypeMediaHeader Media Header Box - BoxTypeMediaHeader = "mdhd" - // BoxTypeHandlerReference Handler Reference Box - BoxTypeHandlerReference = "hdlr" - // BoxTypeMediaInformation Media Information Box - BoxTypeMediaInformation = "minf" - // BoxTypeSampleTable Sample Table Box - BoxTypeSampleTable = "stbl" - // BoxTypeSampleDescription Sample Description Box - BoxTypeSampleDescription = "stsd" - // BoxTypeDataInformation Data Information Box - BoxTypeDataInformation = "dinf" - // BoxTypeDataReferenceBox Data Reference Box - BoxTypeDataReferenceBox = "dref" - // BoxTypeDataEntryUrlBox Data Entry URL Box - BoxTypeDataEntryUrlBox = "url " - // BoxTypeDataEntryUrnBox Data Entry URN Box - BoxTypeDataEntryUrnBox = "urn " - // BoxTypeMovieFragment Movie Fragment Box - BoxTypeMovieFragment = "moof" - // BoxTypeMovieFragmentHeader Movie Fragment Header Box - BoxTypeMovieFragmentHeader = "mfhd" - // BoxTypeTrackFragment Track Fragment Box - BoxTypeTrackFragment = "traf" - // BoxTypeTrackFragmentHeader Track Fragment Header - BoxTypeTrackFragmentHeader = "tfhd" - // BoxTypeTrackFragmentRun Track Fragment Run Box - BoxTypeTrackFragmentRun = "trun" - // BoxTypeVideoMediaHeader Video Media Header Box - BoxTypeVideoMediaHeader = "vmhd" + boxTypeParentFile = "__file" ) // Box is an abstract box struct @@ -93,3 +50,13 @@ func newFullBox(box *Box, data []byte) *FullBox { return fullBox } + +type BoxDefinition struct { + Type string + ParentTypes []string + Parser BoxDefinitionParser +} + +type BoxDefinitionParser func(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) + +var BoxDefinitions []BoxDefinition diff --git a/DataInformationBox.go b/DataInformationBox.go index 6b56ea7..dd91339 100644 --- a/DataInformationBox.go +++ b/DataInformationBox.go @@ -26,15 +26,26 @@ package gomp4 // The data information box contains objects that declare the location of the media information in a track. type DataInformationBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeDataInformation Data Information Box +const BoxTypeDataInformation = "dinf" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeDataInformation, + ParentTypes: []string{BoxTypeMediaInformation}, // TODO: add Meta Box `meta` + Parser: ParseDataInformationBox, + }) } // ParseDataInformationBox creates a new data information box struct based on bytes -func ParseDataInformationBox(filePosition uint64, headerSize uint32, content []byte) (*DataInformationBox, error) { +func ParseDataInformationBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &DataInformationBox{Box: &Box{filePosition, headerSize}} // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeDataInformation, filePosition, content) return box, err } diff --git a/DataReferenceBox.go b/DataReferenceBox.go index 204a2cd..a6efa6e 100644 --- a/DataReferenceBox.go +++ b/DataReferenceBox.go @@ -46,11 +46,22 @@ type DataReferenceBox struct { *FullBox // EntryCount is an integer that counts the actual entries EntryCount uint32 - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeDataReference Data Reference Box +const BoxTypeDataReference = "dref" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeDataReference, + ParentTypes: []string{BoxTypeDataInformation}, + Parser: ParseDataReferenceBox, + }) } // ParseDataReferenceBox creates a new data reference box struct based on bytes -func ParseDataReferenceBox(filePosition uint64, headerSize uint32, content []byte) (*DataReferenceBox, error) { +func ParseDataReferenceBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &DataReferenceBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -60,7 +71,7 @@ func ParseDataReferenceBox(filePosition uint64, headerSize uint32, content []byt // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content[8:]) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeDataReference, filePosition, content[8:]) if err != nil { return box, err } @@ -91,15 +102,26 @@ type DataEntryUrlBox struct { Location string } +// BoxTypeDataEntryUrl Data Entry URL Box +const BoxTypeDataEntryUrl = "url " + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeDataEntryUrl, + ParentTypes: []string{BoxTypeDataReference}, + Parser: ParseDataEntryUrlBox, + }) +} + // ParseDataEntryUrlBox creates a data entry URL box struct based on bytes -func ParseDataEntryUrlBox(filePosition uint64, headerSize uint32, content []byte) *DataEntryUrlBox { +func ParseDataEntryUrlBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &DataEntryUrlBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } box.Location, _ = ParseStringNullTerminated(content[4:]) - return box + return box, nil } // DataEntryUrnBox data entry URN box struct @@ -122,8 +144,19 @@ type DataEntryUrnBox struct { Location string } +// BoxTypeDataEntryUrn Data Entry URN Box +const BoxTypeDataEntryUrn = "urn " + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeDataEntryUrn, + ParentTypes: []string{BoxTypeDataReference}, + Parser: ParseDataEntryUrnBox, + }) +} + // ParseDataEntryUrnBox creates a data entry URN box struct based on bytes -func ParseDataEntryUrnBox(filePosition uint64, headerSize uint32, content []byte) *DataEntryUrnBox { +func ParseDataEntryUrnBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &DataEntryUrnBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -132,5 +165,5 @@ func ParseDataEntryUrnBox(filePosition uint64, headerSize uint32, content []byte box.Name = name box.Location, _ = ParseStringNullTerminated(content[4+endIndex:]) - return box + return box, nil } diff --git a/FileTypeBox.go b/FileTypeBox.go index 5a26c57..9454262 100644 --- a/FileTypeBox.go +++ b/FileTypeBox.go @@ -62,8 +62,19 @@ type FileTypeBox struct { CompatibleBrands []string } +// BoxTypeFileType File Type Box +const BoxTypeFileType = "ftyp" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeFileType, + ParentTypes: []string{boxTypeParentFile}, + Parser: ParseFileTypeBox, + }) +} + // ParseFileTypeBox creates new file type box based on bytes -func ParseFileTypeBox(filePosition uint64, headerSize uint32, content []byte) *FileTypeBox { +func ParseFileTypeBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { // create new box box := &FileTypeBox{ Box: &Box{filePosition, headerSize}, @@ -83,5 +94,5 @@ func ParseFileTypeBox(filePosition uint64, headerSize uint32, content []byte) *F box.CompatibleBrands = append(box.CompatibleBrands, brand) } - return box + return box, nil } diff --git a/HandlerReferenceBox.go b/HandlerReferenceBox.go index 9fd917d..a60c9eb 100644 --- a/HandlerReferenceBox.go +++ b/HandlerReferenceBox.go @@ -49,7 +49,18 @@ type HandlerReferenceBox struct { Name string } -func ParseHandlerReferenceBox(filePosition uint64, headerSize uint32, content []byte) *HandlerReferenceBox { +// BoxTypeHandlerReference Handler Reference Box +const BoxTypeHandlerReference = "hdlr" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeHandlerReference, + ParentTypes: []string{BoxTypeMedia}, // TODO: add box type `meta` (Meta Box) + Parser: ParseHandlerReferenceBox, + }) +} + +func ParseHandlerReferenceBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &HandlerReferenceBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -67,5 +78,5 @@ func ParseHandlerReferenceBox(filePosition uint64, headerSize uint32, content [] // parse name box.Name, _ = ParseStringNullTerminated(content[position:]) - return box + return box, nil } diff --git a/MediaBox.go b/MediaBox.go index 41edd19..8e164c1 100644 --- a/MediaBox.go +++ b/MediaBox.go @@ -27,15 +27,26 @@ package gomp4 // within a track. type MediaBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeMedia Media Box +const BoxTypeMedia = "mdia" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMedia, + ParentTypes: []string{BoxTypeTrack}, + Parser: ParseMediaBox, + }) } // ParseMediaBox creates a new media box struct based on bytes -func ParseMediaBox(filePosition uint64, headerSize uint32, content []byte) (*MediaBox, error) { +func ParseMediaBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MediaBox{Box: &Box{filePosition, headerSize}} // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeMedia, filePosition, content) return box, err } diff --git a/MediaDataBox.go b/MediaDataBox.go index 94d0194..2df9acc 100644 --- a/MediaDataBox.go +++ b/MediaDataBox.go @@ -40,13 +40,24 @@ type MediaDataBox struct { ContentEndPosition uint64 } +// BoxTypeMediaData Media Data Box +const BoxTypeMediaData = "mdat" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMediaData, + ParentTypes: []string{boxTypeParentFile}, + Parser: ParseMediaDataBox, + }) +} + // ParseMediaDataBox creates a new media data box struct -func ParseMediaDataBox(filePosition uint64, headerSize uint32, content []byte) *MediaDataBox { +func ParseMediaDataBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MediaDataBox{Box: &Box{filePosition, headerSize}} // parse positions of content box.ContentStartPosition = filePosition + uint64(headerSize) box.ContentEndPosition = box.ContentStartPosition + uint64(len(content)) - return box + return box, nil } diff --git a/MediaHeaderBox.go b/MediaHeaderBox.go index e419995..eaf55a1 100644 --- a/MediaHeaderBox.go +++ b/MediaHeaderBox.go @@ -58,8 +58,19 @@ type MediaHeaderBox struct { // TODO: parse language language } +// BoxTypeMediaHeader Media Header Box +const BoxTypeMediaHeader = "mdhd" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMediaHeader, + ParentTypes: []string{BoxTypeMedia}, + Parser: ParseMediaHeaderBox, + }) +} + // ParseMediaHeaderBox creates a new Media Header Box struct -func ParseMediaHeaderBox(filePosition uint64, headerSize uint32, content []byte) *MediaHeaderBox { +func ParseMediaHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MediaHeaderBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -81,5 +92,5 @@ func ParseMediaHeaderBox(filePosition uint64, headerSize uint32, content []byte) // TODO: parse language language - return box + return box, nil } diff --git a/MediaInformationBox.go b/MediaInformationBox.go index c4f306a..2c09b7e 100644 --- a/MediaInformationBox.go +++ b/MediaInformationBox.go @@ -25,15 +25,26 @@ package gomp4 // This box contains all the objects that declare characteristic information of the media in the track. type MediaInformationBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeMediaInformation Media Information Box +const BoxTypeMediaInformation = "minf" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMediaInformation, + ParentTypes: []string{BoxTypeMedia}, + Parser: ParseMediaInformationBox, + }) } // ParseMediaInformationBox creates a new media information box struct based on bytes -func ParseMediaInformationBox(filePosition uint64, headerSize uint32, content []byte) (*MediaBox, error) { +func ParseMediaInformationBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MediaBox{Box: &Box{filePosition, headerSize}} // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeMediaInformation, filePosition, content) return box, err } diff --git a/MovieBox.go b/MovieBox.go index 6a305de..751c8a5 100644 --- a/MovieBox.go +++ b/MovieBox.go @@ -26,15 +26,26 @@ package gomp4 // Normally this box is close to the beginning or end of the file, though this is not required. type MovieBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeMovie Movie Box +const BoxTypeMovie = "moov" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMovie, + ParentTypes: []string{boxTypeParentFile}, + Parser: ParseMovieBox, + }) } // ParseMovieBox creates a new movie box struct based on bytes -func ParseMovieBox(filePosition uint64, headerSize uint32, content []byte) (*MovieBox, error) { +func ParseMovieBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MovieBox{Box: &Box{filePosition, headerSize}} // parse content boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeMovie, filePosition, content) return box, err } diff --git a/MovieFragmentBox.go b/MovieFragmentBox.go index d86925c..5ed091c 100644 --- a/MovieFragmentBox.go +++ b/MovieFragmentBox.go @@ -35,17 +35,28 @@ package gomp4 // However, derived specifications may make such restrictions. type MovieFragmentBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeMovieFragment Movie Fragment Box +const BoxTypeMovieFragment = "moof" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMovieFragment, + ParentTypes: []string{boxTypeParentFile}, + Parser: ParseMovieFragmentBox, + }) } // ParseMovieFragmentBox creates a new movie fragment box struct based on bytes -func ParseMovieFragmentBox(filePosition uint64, headerSize uint32, content []byte) (*MovieFragmentBox, error) { +func ParseMovieFragmentBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MovieFragmentBox{ Box: &Box{filePosition, headerSize}, } // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeMovieFragment, filePosition, content) return box, err } diff --git a/MovieFragmentHeaderBox.go b/MovieFragmentHeaderBox.go index ef978a6..16b4571 100644 --- a/MovieFragmentHeaderBox.go +++ b/MovieFragmentHeaderBox.go @@ -36,8 +36,19 @@ type MovieFragmentHeaderBox struct { SequenceNumber uint32 } +// BoxTypeMovieFragmentHeader Movie Fragment Header Box +const BoxTypeMovieFragmentHeader = "mfhd" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMovieFragmentHeader, + ParentTypes: []string{BoxTypeMovieFragment}, + Parser: ParseMovieFragmentHeaderBox, + }) +} + // ParseMovieFragmentHeaderBox creates a new Movie Fragment Header Box struct -func ParseMovieFragmentHeaderBox(filePosition uint64, headerSize uint32, content []byte) *MovieFragmentHeaderBox { +func ParseMovieFragmentHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MovieFragmentHeaderBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -45,5 +56,5 @@ func ParseMovieFragmentHeaderBox(filePosition uint64, headerSize uint32, content // parse sequence number box.SequenceNumber = binary.BigEndian.Uint32(content[4:8]) - return box + return box, nil } diff --git a/MovieHeaderBox.go b/MovieHeaderBox.go index 66486f3..96598dc 100644 --- a/MovieHeaderBox.go +++ b/MovieHeaderBox.go @@ -74,8 +74,19 @@ type MovieHeaderBox struct { NextTrackID uint32 } +// BoxTypeMovieHeader Movie Header Box +const BoxTypeMovieHeader = "mvhd" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeMovieHeader, + ParentTypes: []string{BoxTypeMovie}, + Parser: ParseMovieHeaderBox, + }) +} + // ParseMovieHeaderBox creates a new Movie Header Box struct -func ParseMovieHeaderBox(filePosition uint64, headerSize uint32, content []byte) *MovieHeaderBox { +func ParseMovieHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &MovieHeaderBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -107,5 +118,5 @@ func ParseMovieHeaderBox(filePosition uint64, headerSize uint32, content []byte) position += 6 * 4 box.NextTrackID = binary.BigEndian.Uint32(content[position : position+4]) - return box + return box, nil } diff --git a/Parser.go b/Parser.go index 659d7bf..e10196d 100644 --- a/Parser.go +++ b/Parser.go @@ -24,20 +24,32 @@ import ( // Parser struct for mp4 container parsing type Parser struct { - reader io.Reader - Content []interface{} + reader io.Reader + childBoxDefinitions map[string]map[string]BoxDefinition + Content []any } // NewParser creates a new parser with byte data func NewParser(reader io.Reader) *Parser { - return &Parser{reader: reader} + // prepare valid box type combinations + types := make(map[string]map[string]BoxDefinition, 0) + for _, definition := range BoxDefinitions { + for _, parent := range definition.ParentTypes { + if _, exists := types[parent]; !exists { + types[parent] = make(map[string]BoxDefinition, 0) + } + types[parent][definition.Type] = definition + } + } + + return &Parser{reader: reader, childBoxDefinitions: types} } // Parse starts the file parsing func (parser *Parser) Parse() error { var filePosition uint64 = 0 for { - box, endPosition, endOfFile, err := parseNextBox(parser.reader, filePosition) + box, endPosition, endOfFile, err := parseNextBox(parser, parser.reader, boxTypeParentFile, filePosition) if box != nil { parser.Content = append(parser.Content, box) } @@ -52,7 +64,7 @@ func (parser *Parser) Parse() error { } } -func parseNextBox(reader io.Reader, filePosition uint64) (box interface{}, endPosition uint64, endOfFile bool, err error) { +func parseNextBox(parser *Parser, reader io.Reader, currentBoxType string, filePosition uint64) (box any, endPosition uint64, endOfFile bool, err error) { // first 4 bytes are the size of the box sizeBytes := make([]byte, boxSizeLength) sizeBytesCount, err := reader.Read(sizeBytes) @@ -103,54 +115,11 @@ func parseNextBox(reader io.Reader, filePosition uint64) (box interface{}, endPo } // parse struct of box type - switch boxType { - case BoxTypeFileType: - box = ParseFileTypeBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMediaData: - box = ParseMediaDataBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMovie: - box, err = ParseMovieBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMovieHeader: - box = ParseMovieHeaderBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeTrack: - box, err = ParseTrackBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeTrackHeader: - box = ParseTrackHeaderBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMedia: - box, err = ParseMediaBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMediaHeader: - box = ParseMediaHeaderBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeHandlerReference: - box = ParseHandlerReferenceBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMediaInformation: - box, err = ParseMediaInformationBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeSampleTable: - box, err = ParseSampleTableBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeSampleDescription: - box = ParseSampleDescriptionBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeDataInformation: - box, err = ParseDataInformationBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeDataReferenceBox: - box, err = ParseDataReferenceBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeDataEntryUrlBox: - box = ParseDataEntryUrlBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeDataEntryUrnBox: - box = ParseDataEntryUrnBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMovieFragment: - box, err = ParseMovieFragmentBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeMovieFragmentHeader: - box = ParseMovieFragmentHeaderBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeTrackFragment: - box, err = ParseTrackFragmentBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeTrackFragmentHeader: - box = ParseTrackFragmentHeaderBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeTrackFragmentRun: - box, err = ParseTrackFragmentRunBox(filePosition, boxHeaderSize, boxContentBytes) - case BoxTypeVideoMediaHeader: - box = ParseVideoMediaHeaderBox(filePosition, boxHeaderSize, boxContentBytes) - default: - logger.Println("unknown box type", boxType, "at", filePosition) - return nil, filePosition, false, errors.New("unknown box type " + boxType) + if boxTypeDefinition, found := parser.childBoxDefinitions[currentBoxType][boxType]; found { + box, err = boxTypeDefinition.Parser(parser, filePosition, boxHeaderSize, boxContentBytes) + } else { + logger.Println("unknown box type", boxType, "at", filePosition, "in", currentBoxType) + return nil, filePosition, false, errors.New("unknown box type " + boxType + " in " + currentBoxType) } // check for box errors @@ -163,13 +132,13 @@ func parseNextBox(reader io.Reader, filePosition uint64) (box interface{}, endPo return } -func (box *Box) parseChildBoxes(filePosition uint64, content []byte) (subBoxes []interface{}, e error) { +func (box *Box) parseChildBoxes(parser *Parser, currentBoxType string, filePosition uint64, content []byte) (subBoxes []any, e error) { byteReader := bytes.NewReader(content) endPosition := filePosition + uint64(box.HeaderSize) + uint64(len(content)) var currentFilePosition = filePosition + uint64(box.HeaderSize) for { // parse next packet - currentBox, currentEndPosition, _, err := parseNextBox(byteReader, currentFilePosition) + currentBox, currentEndPosition, _, err := parseNextBox(parser, byteReader, currentBoxType, currentFilePosition) if err != nil { e = err logger.Println("error at", currentEndPosition, err) diff --git a/SampleDescriptionBox.go b/SampleDescriptionBox.go index 3a10552..90c6687 100644 --- a/SampleDescriptionBox.go +++ b/SampleDescriptionBox.go @@ -16,6 +16,8 @@ package gomp4 import ( "encoding/binary" + "fmt" + "log" ) // SampleDescriptionBox sample description box struct @@ -64,10 +66,22 @@ 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(filePosition uint64, headerSize uint32, content []byte) *SampleDescriptionBox { +func ParseSampleDescriptionBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &SampleDescriptionBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } diff --git a/SampleTableBox.go b/SampleTableBox.go index a605d0a..86fccde 100644 --- a/SampleTableBox.go +++ b/SampleTableBox.go @@ -39,15 +39,26 @@ package gomp4 // are sync samples. type SampleTableBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeSampleTable Sample Table Box +const BoxTypeSampleTable = "stbl" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeSampleTable, + ParentTypes: []string{BoxTypeMediaInformation}, + Parser: ParseSampleTableBox, + }) } // ParseSampleTableBox creates a new sample table box struct based on bytes -func ParseSampleTableBox(filePosition uint64, headerSize uint32, content []byte) (*SampleTableBox, error) { +func ParseSampleTableBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &SampleTableBox{Box: &Box{filePosition, headerSize}} // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeSampleTable, filePosition, content) return box, err } diff --git a/TrackBox.go b/TrackBox.go index 69bc9b4..58c90ea 100644 --- a/TrackBox.go +++ b/TrackBox.go @@ -34,15 +34,26 @@ package gomp4 // tracks; after deleting all hint tracks, the entire un‐hinted presentation shall remain. type TrackBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeTrack Track Box +const BoxTypeTrack = "trak" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeTrack, + ParentTypes: []string{BoxTypeMovie}, + Parser: ParseTrackBox, + }) } // ParseTrackBox creates a new track box struct based on bytes -func ParseTrackBox(filePosition uint64, headerSize uint32, content []byte) (*TrackBox, error) { +func ParseTrackBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &TrackBox{Box: &Box{filePosition, headerSize}} // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeTrack, filePosition, content) return box, err } diff --git a/TrackFragmentBox.go b/TrackFragmentBox.go index 1b661f6..13e70ee 100644 --- a/TrackFragmentBox.go +++ b/TrackFragmentBox.go @@ -30,15 +30,26 @@ package gomp4 // inserts can be used in audio tracks doing silence suppression, for example. type TrackFragmentBox struct { *Box - ChildBoxes []interface{} + ChildBoxes []any +} + +// BoxTypeTrackFragment Track Fragment Box +const BoxTypeTrackFragment = "traf" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeTrackFragment, + ParentTypes: []string{BoxTypeMovieFragment}, + Parser: ParseTrackFragmentBox, + }) } // ParseTrackFragmentBox creates a new Track Fragment Box struct -func ParseTrackFragmentBox(filePosition uint64, headerSize uint32, content []byte) (*TrackFragmentBox, error) { +func ParseTrackFragmentBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &TrackFragmentBox{Box: &Box{filePosition, headerSize}} // parse child boxes var err error - box.ChildBoxes, err = box.parseChildBoxes(filePosition, content) + box.ChildBoxes, err = box.parseChildBoxes(parser, BoxTypeTrackFragment, filePosition, content) return box, err } diff --git a/TrackFragmentHeaderBox.go b/TrackFragmentHeaderBox.go index b228322..f8bb47b 100644 --- a/TrackFragmentHeaderBox.go +++ b/TrackFragmentHeaderBox.go @@ -43,6 +43,17 @@ type TrackFragmentHeaderBox struct { DefaultSampleFlags uint32 } +// BoxTypeTrackFragmentHeader Track Fragment Header +const BoxTypeTrackFragmentHeader = "tfhd" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeTrackFragmentHeader, + ParentTypes: []string{BoxTypeTrackFragment}, + Parser: ParseTrackFragmentHeaderBox, + }) +} + const ( // TrackFragmentHeaderBoxFlagBaseDataOffsetPresent Base Offset Present Flag // @@ -74,7 +85,7 @@ const ( ) // ParseTrackFragmentHeaderBox creates a new track fragment header box struct -func ParseTrackFragmentHeaderBox(filePosition uint64, headerSize uint32, content []byte) *TrackFragmentHeaderBox { +func ParseTrackFragmentHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &TrackFragmentHeaderBox{FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4])} position := 4 @@ -84,5 +95,5 @@ func ParseTrackFragmentHeaderBox(filePosition uint64, headerSize uint32, content // TODO: check other flags - return box + return box, nil } diff --git a/TrackFragmentRunBox.go b/TrackFragmentRunBox.go index aa95831..644a2c5 100644 --- a/TrackFragmentRunBox.go +++ b/TrackFragmentRunBox.go @@ -54,6 +54,17 @@ type TrackFragmentRunBox struct { Samples []TrackFragmentRunBoxSample } +// BoxTypeTrackFragmentRun Track Fragment Run Box +const BoxTypeTrackFragmentRun = "trun" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeTrackFragmentRun, + ParentTypes: []string{BoxTypeTrackFragment}, + Parser: ParseTrackFragmentRunBox, + }) +} + const ( // TrackFragmentRunBoxFlagDataOffsetPresent Data Offset Flag TrackFragmentRunBoxFlagDataOffsetPresent uint32 = 0x00000001 @@ -84,7 +95,7 @@ const ( ) // ParseTrackFragmentRunBox creates a new Track Fragment Run Box struct -func ParseTrackFragmentRunBox(filePosition uint64, headerSize uint32, content []byte) (*TrackFragmentRunBox, error) { +func ParseTrackFragmentRunBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { // build full box with additional 4 bytes header fullBox := newFullBox(&Box{filePosition, headerSize}, content[0:4]) position := 4 diff --git a/TrackHeaderBox.go b/TrackHeaderBox.go index d81dc34..abb1611 100644 --- a/TrackHeaderBox.go +++ b/TrackHeaderBox.go @@ -148,8 +148,19 @@ const ( TrackHeaderBoxFlagTrackSizeIsAspectRatio uint32 = 0x00000008 ) +// BoxTypeTrackHeader Track Header Box +const BoxTypeTrackHeader = "tkhd" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeTrackHeader, + ParentTypes: []string{BoxTypeTrack}, + Parser: ParseTrackHeaderBox, + }) +} + // ParseTrackHeaderBox creates a new Track Header Box struct -func ParseTrackHeaderBox(filePosition uint64, headerSize uint32, content []byte) *TrackHeaderBox { +func ParseTrackHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &TrackHeaderBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -190,5 +201,5 @@ func ParseTrackHeaderBox(filePosition uint64, headerSize uint32, content []byte) box.Width = NewFixed1616ByBytes(content[position : position+4]) box.Height = NewFixed1616ByBytes(content[position+4 : position+8]) - return box + return box, nil } diff --git a/VideoMediaHeaderBox.go b/VideoMediaHeaderBox.go index a81004d..525d79f 100644 --- a/VideoMediaHeaderBox.go +++ b/VideoMediaHeaderBox.go @@ -37,8 +37,19 @@ type VideoMediaHeaderBox struct { CPColor []uint16 } +// BoxTypeVideoMediaHeader Video Media Header Box +const BoxTypeVideoMediaHeader = "vmhd" + +func init() { + BoxDefinitions = append(BoxDefinitions, BoxDefinition{ + Type: BoxTypeVideoMediaHeader, + ParentTypes: []string{BoxTypeMediaInformation}, + Parser: ParseVideoMediaHeaderBox, + }) +} + // ParseVideoMediaHeaderBox creates a new Video Media Header Box struct -func ParseVideoMediaHeaderBox(filePosition uint64, headerSize uint32, content []byte) *VideoMediaHeaderBox { +func ParseVideoMediaHeaderBox(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) { box := &VideoMediaHeaderBox{ FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), } @@ -49,5 +60,5 @@ func ParseVideoMediaHeaderBox(filePosition uint64, headerSize uint32, content [] box.CPColor = append(box.CPColor, binary.BigEndian.Uint16(content[position+4:position+6])) box.CPColor = append(box.CPColor, binary.BigEndian.Uint16(content[position+6:position+8])) - return box + return box, nil } diff --git a/go.mod b/go.mod index f4cba21..0830a8f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module gitlab.com/martinr92/gomp4 -go 1.15 +go 1.18