new box scoping
This commit is contained in:
parent
b3dc1b253d
commit
5a13e6352d
23 changed files with 332 additions and 151 deletions
55
Box.go
55
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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
17
MediaBox.go
17
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
17
MovieBox.go
17
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
79
Parser.go
79
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)
|
||||
|
|
|
@ -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]),
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
17
TrackBox.go
17
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,3 +1,3 @@
|
|||
module gitlab.com/martinr92/gomp4
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
|
Loading…
Add table
Reference in a new issue