205 lines
10 KiB
Go
205 lines
10 KiB
Go
// 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"
|
||
"time"
|
||
)
|
||
|
||
// TrackHeaderBox track header box struct
|
||
//
|
||
// 8.3.2 Track Header Box
|
||
//
|
||
// Box Type: ‘tkhd’
|
||
// Container: Track Box (‘trak’)
|
||
// Mandatory: Yes
|
||
// Quantity: Exactly one
|
||
//
|
||
// This box specifies the characteristics of a single track. Exactly one Track Header Box is contained in a
|
||
// track.
|
||
//
|
||
// In the absence of an edit list, the presentation of a track starts at the beginning of the overall
|
||
// presentation. An empty edit is used to offset the start time of a track.
|
||
//
|
||
// The default value of the track header flags for media tracks is 7 (track_enabled, track_in_movie,
|
||
// track_in_preview). If in a presentation all tracks have neither track_in_movie nor track_in_preview set,
|
||
// then all tracks shall be treated as if both flags were set on all tracks. Server hint tracks should have the
|
||
// track_in_movie and track_in_preview set to 0, so that they are ignored for local playback and
|
||
// preview.
|
||
//
|
||
// Under the ‘iso3’ brand or brands that share its requirements, the width and height in the track header
|
||
// are measured on a notional 'square' (uniform) grid. Track video data is normalized to these dimensions
|
||
// (logically) before any transformation or placement caused by a layup or composition system. Track
|
||
// (and movie) matrices, if used, also operate in this uniformly‐scaled space.
|
||
//
|
||
// The duration field here does not include the duration of following movie fragments, if any, but only of
|
||
// the media in the enclosing Movie Box. The Movie Extends Header box may be used to document the
|
||
// duration including movie fragments, when desired and possible.
|
||
type TrackHeaderBox struct {
|
||
*FullBox
|
||
// is an integer that declares the creation time of this track (in seconds since
|
||
// midnight, Jan. 1, 1904, in UTC time).
|
||
CreationTimeV0 time.Time
|
||
// is an integer that declares the creation time of this track (in seconds since
|
||
// midnight, Jan. 1, 1904, in UTC time).
|
||
CreationTimeV1 time.Time
|
||
// is an integer that declares the most recent time the track was modified (in
|
||
// seconds since midnight, Jan. 1, 1904, in UTC time).
|
||
ModificationTimeV0 time.Time
|
||
// is an integer that declares the most recent time the track was modified (in
|
||
// seconds since midnight, Jan. 1, 1904, in UTC time).
|
||
ModificationTimeV1 time.Time
|
||
// is an integer that uniquely identifies this track over the entire life‐time of this
|
||
// presentation. Track IDs are never re‐used and cannot be zero.
|
||
TrackID uint32
|
||
// is an integer that indicates the duration of this track (in the timescale indicated in the
|
||
// Movie Header Box). The value of this field is equal to the sum of the durations of all of the track’s
|
||
// edits. If there is no edit list, then the duration is the sum of the sample durations, converted into
|
||
// the timescale in the Movie Header Box. If the duration of this track cannot be determined then
|
||
// duration is set to all 1s.
|
||
DurationV0 uint32
|
||
// is an integer that indicates the duration of this track (in the timescale indicated in the
|
||
// Movie Header Box). The value of this field is equal to the sum of the durations of all of the track’s
|
||
// edits. If there is no edit list, then the duration is the sum of the sample durations, converted into
|
||
// the timescale in the Movie Header Box. If the duration of this track cannot be determined then
|
||
// duration is set to all 1s.
|
||
DurationV1 uint64
|
||
// specifies the front‐to‐back ordering of video tracks; tracks with lower numbers are closer
|
||
// to the viewer. 0 is the normal value, and ‐1 would be in front of track 0, and so on.
|
||
Layer int16
|
||
// is an integer that specifies a group or collection of tracks. If this field is 0
|
||
// there is no information on possible relations to other tracks. If this field is not 0, it should be the
|
||
// same for tracks that contain alternate data for one another and different for tracks belonging to
|
||
// different such groups. Only one track within an alternate group should be played or streamed at
|
||
// any one time, and must be distinguishable from other tracks in the group via attributes such as
|
||
// bitrate, codec, language, packet size etc. A group may have only one member.
|
||
AlternativeGroup int16
|
||
// is a fixed 8.8 value specifying the track's relative audio volume. Full volume is 1.0
|
||
// (0x0100) and is the normal value. Its value is irrelevant for a purely visual track. Tracks may be
|
||
// composed by combining them according to their volume, and then using the overall Movie
|
||
// Header Box volume setting; or more complex audio composition (e.g. MPEG‐4 BIFS) may be
|
||
// used.
|
||
Volume Fixed88
|
||
// provides a transformation matrix for the video; (u,v,w) are restricted here to (0,0,1), hex
|
||
// (0,0,0x40000000).
|
||
Matrix []int32
|
||
// fixed‐point 16.16 values are track‐dependent as follows:
|
||
//
|
||
// For text and subtitle tracks, they may, depending on the coding format, describe the suggested
|
||
// size of the rendering area. For such tracks, the value 0x0 may also be used to indicate that the
|
||
// data may be rendered at any size, that no preferred size has been indicated and that the actual
|
||
// size may be determined by the external context or by reusing the width and height of another
|
||
// track. For those tracks, the flag track_size_is_aspect_ratio may also be used.
|
||
// For non‐visual tracks (e.g. audio), they should be set to zero.
|
||
// For all other tracks, they specify the track's visual presentation size. These need not be the same
|
||
// as the pixel dimensions of the images, which is documented in the sample description(s); all
|
||
// images in the sequence are scaled to this size, before any overall transformation of the track
|
||
// represented by the matrix. The pixel dimensions of the images are the default values.
|
||
Width Fixed1616
|
||
// fixed‐point 16.16 values are track‐dependent as follows:
|
||
//
|
||
// For text and subtitle tracks, they may, depending on the coding format, describe the suggested
|
||
// size of the rendering area. For such tracks, the value 0x0 may also be used to indicate that the
|
||
// data may be rendered at any size, that no preferred size has been indicated and that the actual
|
||
// size may be determined by the external context or by reusing the width and height of another
|
||
// track. For those tracks, the flag track_size_is_aspect_ratio may also be used.
|
||
// For non‐visual tracks (e.g. audio), they should be set to zero.
|
||
// For all other tracks, they specify the track's visual presentation size. These need not be the same
|
||
// as the pixel dimensions of the images, which is documented in the sample description(s); all
|
||
// images in the sequence are scaled to this size, before any overall transformation of the track
|
||
// represented by the matrix. The pixel dimensions of the images are the default values.
|
||
Height Fixed1616
|
||
}
|
||
|
||
const (
|
||
// TrackHeaderBoxFlagTrackEnabled Track Enabled Flag
|
||
// Indicates that the track is enabled. Flag value is 0x000001. A disabled track (the
|
||
// low bit is zero) is treated as if it were not present.
|
||
TrackHeaderBoxFlagTrackEnabled uint32 = 0x00000001
|
||
|
||
// TrackHeaderBoxFlagTrackInMovie Track in Movie
|
||
// Indicates that the track is used in the presentation. Flag value is 0x000002.
|
||
TrackHeaderBoxFlagTrackInMovie uint32 = 0x00000002
|
||
|
||
// TrackHeaderBoxFlagTrackInPreview Track in Preview
|
||
// Indicates that the track is used when previewing the presentation. Flag value
|
||
// is 0x000004.
|
||
TrackHeaderBoxFlagTrackInPreview uint32 = 0x00000004
|
||
|
||
// TrackHeaderBoxFlagTrackSizeIsAspectRatio Track Size is Aspect Ratio
|
||
// Indicates that the width and height fields are not expressed in
|
||
// pixel units. The values have the same units but these units are not specified. The values are
|
||
// only an indication of the desired aspect ratio. If the aspect ratios of this track and other
|
||
// related tracks are not identical, then the respective positioning of the tracks is undefined,
|
||
// possibly defined by external contexts. Flag value is 0x000008.
|
||
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(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
|
||
box := &TrackHeaderBox{
|
||
FullBox: newFullBox(&Box{filePosition, headerSize}, content[0:4]),
|
||
}
|
||
|
||
position := 4
|
||
if box.Version == 1 {
|
||
box.CreationTimeV1 = since1904ToTime(int64(binary.BigEndian.Uint64(content[4:12])))
|
||
box.ModificationTimeV1 = since1904ToTime(int64(binary.BigEndian.Uint64(content[12:20])))
|
||
box.TrackID = binary.BigEndian.Uint32(content[20:24])
|
||
// uint32 reserved
|
||
box.DurationV1 = binary.BigEndian.Uint64(content[28:36])
|
||
position += 32
|
||
} else { // version == 0
|
||
box.CreationTimeV0 = since1904ToTime(int64(binary.BigEndian.Uint32(content[4:8])))
|
||
box.ModificationTimeV0 = since1904ToTime(int64(binary.BigEndian.Uint32(content[8:12])))
|
||
box.TrackID = binary.BigEndian.Uint32(content[12:16])
|
||
// uint32 reserved
|
||
box.DurationV0 = binary.BigEndian.Uint32(content[20:24])
|
||
position += 20
|
||
}
|
||
|
||
// skip 2 reserved uint32
|
||
position += 4 + 4
|
||
|
||
// parse next fields
|
||
box.Layer = int16(binary.BigEndian.Uint16(content[position : position+2]))
|
||
box.AlternativeGroup = int16(binary.BigEndian.Uint16(content[position+2 : position+4]))
|
||
box.Volume = NewFixed88ByBytes(content[position+4 : position+6])
|
||
position += 2 + 2 + 2 + 2 // 4 bytes for layer, 2 bytes for alternative group, 2 bytes for volume and 2 bytes reserved
|
||
|
||
// parse matrix
|
||
for i := 0; i < 9; i++ {
|
||
box.Matrix = append(box.Matrix, int32(binary.BigEndian.Uint32(content[position:position+4])))
|
||
position += 4
|
||
}
|
||
|
||
// parse width/height
|
||
box.Width = NewFixed1616ByBytes(content[position : position+4])
|
||
box.Height = NewFixed1616ByBytes(content[position+4 : position+8])
|
||
|
||
return box, nil
|
||
}
|