From 199f7730fbd593668a3c4e533a05defb70d95ccf Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Wed, 16 Oct 2024 18:58:58 +0200 Subject: [PATCH] new parser for movie header box --- Box.go | 2 + MovieHeaderBox.go | 110 ++++++++++++++++++++++++++++++++++++++++++++++ Parser.go | 2 + README.md | 2 +- 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 MovieHeaderBox.go diff --git a/Box.go b/Box.go index 3bb6bb0..6e7da36 100644 --- a/Box.go +++ b/Box.go @@ -26,6 +26,8 @@ const ( BoxTypeMediaData = "mdat" // BoxTypeMovie Movie Box BoxTypeMovie = "moov" + // BoxTypeMovieHeader Movie Header Box + BoxTypeMovieHeader = "mvhd" // BoxTypeMovieFragment Movie Fragment Box BoxTypeMovieFragment = "moof" // BoxTypeMovieFragmentHeader Movie Fragment Header Box diff --git a/MovieHeaderBox.go b/MovieHeaderBox.go new file mode 100644 index 0000000..1d31f55 --- /dev/null +++ b/MovieHeaderBox.go @@ -0,0 +1,110 @@ +// 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" +) + +// MovieHeaderBox Movie Header Box struct +// +// 8.2.2 Movie Header Box +// Box Type: ‘mvhd’ +// Container: Movie Box (‘moov’) +// Mandatory: Yes +// Quantity: Exactly one +// +// This box defines overall information which is media‐independent, and relevant to the entire +// presentation considered as a whole. +type MovieHeaderBox struct { + *FullBox + // is an integer that declares the creation time of the presentation (in seconds + // since midnight, Jan. 1, 1904, in UTC time) + CreationTimeV0 uint32 + // is an integer that declares the creation time of the presentation (in seconds + // since midnight, Jan. 1, 1904, in UTC time) + CreationTimeV1 uint64 + // is an integer that declares the most recent time the presentation was + // modified (in seconds since midnight, Jan. 1, 1904, in UTC time) + ModificationTimeV0 uint32 + // is an integer that declares the most recent time the presentation was + // modified (in seconds since midnight, Jan. 1, 1904, in UTC time) + ModificationTimeV1 uint64 + // is an integer that specifies the time‐scale for the entire presentation; this is the + // number of time units that pass in one second. For example, a time coordinate system that + // measures time in sixtieths of a second has a time scale of 60. + Timescale uint32 + // is an integer that declares length of the presentation (in the indicated timescale). This + // property is derived from the presentation’s tracks: the value of this field corresponds to the + // duration of the longest track in the presentation. If the duration cannot be determined then + // duration is set to all 1s. + DurationV0 uint32 + // is an integer that declares length of the presentation (in the indicated timescale). This + // property is derived from the presentation’s tracks: the value of this field corresponds to the + // duration of the longest track in the presentation. If the duration cannot be determined then + // duration is set to all 1s. + DurationV1 uint64 + // is a fixed point 16.16 number that indicates the preferred rate to play the presentation; 1.0 + // (0x00010000) is normal forward playback + Rate int32 + // is a fixed point 8.8 number that indicates the preferred playback volume. 1.0 (0x0100) is + // full volume. + Volume int16 + // provides a transformation matrix for the video; (u,v,w) are restricted here to (0,0,1), hex + // values (0,0,0x40000000). + Matrix []int32 + // is a non‐zero integer that indicates a value to use for the track ID of the next track + // to be added to this presentation. Zero is not a valid track ID value. The value of + // next_track_ID shall be larger than the largest track‐ID in use. If this value is equal to all 1s + // (32‐bit maxint), and a new media track is to be added, then a search must be made in the file for + // an unused track identifier. + NextTrackID uint32 +} + +// ParseMovieHeaderBox creates a new Movie Header Box struct +func ParseMovieHeaderBox(filePosition uint64, headerSize uint32, content []byte) *MovieHeaderBox { + box := &MovieHeaderBox{ + FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]), + } + + position := 4 + if box.Version == 1 { + box.CreationTimeV1 = binary.BigEndian.Uint64(content[4:12]) + box.ModificationTimeV1 = binary.BigEndian.Uint64(content[12:20]) + box.Timescale = binary.BigEndian.Uint32(content[20:24]) + box.DurationV1 = binary.BigEndian.Uint64(content[24:32]) + position += 28 + } else { // version == 0 + box.CreationTimeV0 = binary.BigEndian.Uint32(content[4:8]) + box.ModificationTimeV0 = binary.BigEndian.Uint32(content[8:12]) + box.Timescale = binary.BigEndian.Uint32(content[12:16]) + box.DurationV0 = binary.BigEndian.Uint32(content[16:20]) + position += 16 + } + + box.Rate = int32(binary.BigEndian.Uint32(content[position : position+4])) + box.Volume = int16(binary.BigEndian.Uint16(content[position+4 : position+6])) + position += 4 + 2 + 2 + 8 // 4 bytes for rate, 2 bytes for volume, 2 bytes reserved and 8 bytes reserved + for i := 0; i < 9; i++ { + box.Matrix = append(box.Matrix, int32(binary.BigEndian.Uint32(content[position:position+4]))) + position += 4 + } + + // skip 6*32 bit "pre-defined" + position += 6 * 4 + box.NextTrackID = binary.BigEndian.Uint32(content[position : position+4]) + + return box +} diff --git a/Parser.go b/Parser.go index e1eb9e3..06b68ec 100644 --- a/Parser.go +++ b/Parser.go @@ -110,6 +110,8 @@ func parseNextBox(reader io.Reader, filePosition uint64) (box interface{}, endPo box = ParseMediaDataBox(filePosition, boxHeaderSize, boxContentBytes) case BoxTypeMovie: box, err = ParseMovieBox(filePosition, boxHeaderSize, boxContentBytes) + case BoxTypeMovieHeader: + box = ParseMovieHeaderBox(filePosition, boxHeaderSize, boxContentBytes) case BoxTypeMovieFragment: box, err = ParseMovieFragmentBox(filePosition, boxHeaderSize, boxContentBytes) case BoxTypeMovieFragmentHeader: diff --git a/README.md b/README.md index 562bb49..9534be1 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Implementation progress | 8.1.2 Free Space Box | free, skip | - | | 8.1.3 Progressive Download Information Box | pdin | - | | 8.2.1 Movie Box | moov | 100% | -| 8.2.2 Movie Header Box | mvhd | - | +| 8.2.2 Movie Header Box | mvhd | 100% | | 8.3.1 Track Box | trak | - | | 8.3.2 Track Header Box | tkhd | - | | 8.3.3 Track Reference Box | tref | - |