From 00105f514251a40a3c9f41edc097ed5c7a13179e Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Wed, 20 Nov 2024 17:53:24 +0100 Subject: [PATCH] feat: new data reference box --- Box.go | 6 ++ DataReferenceBox.go | 136 +++++++++++++++++++++++++++++++++++++++++ HandlerReferenceBox.go | 2 +- Parser.go | 6 ++ README.md | 2 +- String.go | 6 +- 6 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 DataReferenceBox.go diff --git a/Box.go b/Box.go index e701337..994a957 100644 --- a/Box.go +++ b/Box.go @@ -42,6 +42,12 @@ const ( BoxTypeMediaInformation = "minf" // 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 diff --git a/DataReferenceBox.go b/DataReferenceBox.go new file mode 100644 index 0000000..204a2cd --- /dev/null +++ b/DataReferenceBox.go @@ -0,0 +1,136 @@ +// 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" +) + +// DataReferenceBox data reference box struct +// +// 8.7.2 Data Reference Box +// +// Box Types: ‘dref’ +// Container: Data Information Box (‘dinf’) +// Mandatory: Yes +// Quantity: Exactly one +// +// The data reference object contains a table of data references (normally URLs) that declare the +// location(s) of the media data used within the presentation. The data reference index in the sample +// description ties entries in this table to the samples in the track. A track may be split over several +// sources in this way. +// +// If the flag is set indicating that the data is in the same file as this box, then no string (not even an empty +// one) shall be supplied in the entry field. +// +// The entry_count in the DataReferenceBox shall be 1 or greater; each DataEntryBox within the +// DataReferenceBox shall be either a DataEntryUrnBox or a DataEntryUrlBox. +// +// When a file that has data entries with the flag set indicating that the media data is in the same file, is +// split into segments for transport, the value of this flag does not change, as the file is (logically) +// reassembled after the transport operation. +type DataReferenceBox struct { + *FullBox + // EntryCount is an integer that counts the actual entries + EntryCount uint32 + ChildBoxes []interface{} +} + +// ParseDataReferenceBox creates a new data reference box struct based on bytes +func ParseDataReferenceBox(filePosition uint64, headerSize uint32, content []byte) (*DataReferenceBox, error) { + box := &DataReferenceBox{ + FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), + } + + // parse entry counter + box.EntryCount = binary.BigEndian.Uint32(content[4:8]) + + // parse child boxes + var err error + box.ChildBoxes, err = box.parseChildBoxes(filePosition, content[8:]) + if err != nil { + return box, err + } + + // validate entries amount + if len(box.ChildBoxes) != int(box.EntryCount) { + return box, fmt.Errorf("invalid amount of boxes at %d", filePosition) + } + + return box, err +} + +// DataEntryUrlBox data entry URL box struct +// +// Box Types: ‘url ‘ +// Container: Data Information Box (‘dref’) +// Mandatory: Yes (at least one of ‘url ‘ or ‘urn ‘ shall be present) +// Quantity: One or more +type DataEntryUrlBox struct { + *FullBox + // Location is a URL, and is required in a URL entry and optional in a URN entry, where it gives a location to find + // the resource with the given name. If the + // self‐contained flag is set, the URL form is used and no string is present; the box terminates with + // the entry‐flags field. The URL type should be of a service that delivers a file (e.g. URLs of type file, + // http, ftp etc.), and which services ideally also permit random access. Relative URLs are + // permissible and are relative to the file containing the Movie Box that contains this data + // reference. + Location string +} + +// ParseDataEntryUrlBox creates a data entry URL box struct based on bytes +func ParseDataEntryUrlBox(filePosition uint64, headerSize uint32, content []byte) *DataEntryUrlBox { + box := &DataEntryUrlBox{ + FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), + } + + box.Location, _ = ParseStringNullTerminated(content[4:]) + + return box +} + +// DataEntryUrnBox data entry URN box struct +// +// Box Types: ‘urn ‘ +// Container: Data Information Box (‘dref’) +// Mandatory: Yes (at least one of ‘url ‘ or ‘urn ‘ shall be present) +// Quantity: One or more +type DataEntryUrnBox struct { + *FullBox + // Name is a URN, and is required in a URN entry. + Name string + // Location is a URL, and is required in a URL entry and optional in a URN entry, where it gives a location to find + // the resource with the given name. If the + // self‐contained flag is set, the URL form is used and no string is present; the box terminates with + // the entry‐flags field. The URL type should be of a service that delivers a file (e.g. URLs of type file, + // http, ftp etc.), and which services ideally also permit random access. Relative URLs are + // permissible and are relative to the file containing the Movie Box that contains this data + // reference. + Location string +} + +// ParseDataEntryUrnBox creates a data entry URN box struct based on bytes +func ParseDataEntryUrnBox(filePosition uint64, headerSize uint32, content []byte) *DataEntryUrnBox { + box := &DataEntryUrnBox{ + FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), + } + + name, endIndex := ParseStringNullTerminated(content[4:]) + box.Name = name + box.Location, _ = ParseStringNullTerminated(content[4+endIndex:]) + + return box +} diff --git a/HandlerReferenceBox.go b/HandlerReferenceBox.go index b57775e..9fd917d 100644 --- a/HandlerReferenceBox.go +++ b/HandlerReferenceBox.go @@ -65,7 +65,7 @@ func ParseHandlerReferenceBox(filePosition uint64, headerSize uint32, content [] position += 3 * 4 // parse name - box.Name = ParseStringNullTerminated(content[position:]) + box.Name, _ = ParseStringNullTerminated(content[position:]) return box } diff --git a/Parser.go b/Parser.go index c393672..07328e3 100644 --- a/Parser.go +++ b/Parser.go @@ -126,6 +126,12 @@ func parseNextBox(reader io.Reader, filePosition uint64) (box interface{}, endPo box, err = ParseMediaInformationBox(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: diff --git a/README.md b/README.md index 1423d14..080381e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Implementation progress | 8.6.5 Edit Box | edts | - | | 8.6.6 Edit List Box | elst | - | | 8.7.1 Data Information Box | dinf | 100% | -| 8.7.2 Data Reference Box | dref, url, urn | - | +| 8.7.2 Data Reference Box | dref, url, urn | 100% | | 8.7.3 Sample Size Boxes | stsz, stz2 | - | | 8.7.4 Sample To Chunk Box | stsc | - | | 8.7.5 Chunk Offset Box | stco, co64 | - | diff --git a/String.go b/String.go index 8a324d5..05f5403 100644 --- a/String.go +++ b/String.go @@ -18,11 +18,11 @@ import ( "bytes" ) -func ParseStringNullTerminated(b []byte) string { +func ParseStringNullTerminated(b []byte) (string, int) { endIndex := bytes.IndexByte(b, '\x00') if endIndex == -1 { - return "" + return "", -1 } - return string(b[:endIndex]) + return string(b[:endIndex]), endIndex }