174 lines
6.8 KiB
Go
174 lines
6.8 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"
|
||
"fmt"
|
||
)
|
||
|
||
// TrackFragmentRunBox Track Fragment Run Box struct
|
||
//
|
||
// 8.8.8 Track Fragment Run Box
|
||
// Box Type: ‘trun’
|
||
// Container: Track Fragment Box ('traf')
|
||
// Mandatory: No
|
||
// Quantity: Zero or more
|
||
//
|
||
// Within the Track Fragment Box, there are zero or more Track Run Boxes. If the duration‐is‐empty flag is
|
||
// set in the tf_flags, there are no track runs. A track run documents a contiguous set of samples for a track.
|
||
//
|
||
// The number of optional fields is determined from the number of bits set in the lower byte of the flags,
|
||
// and the size of a record from the bits set in the second byte of the flags. This procedure shall be
|
||
// followed, to allow for new fields to be defined.
|
||
//
|
||
// If the data‐offset is not present, then the data for this run starts immediately after the data of the
|
||
// previous run, or at the base‐data‐offset defined by the track fragment header if this is the first run in a
|
||
// track fragment, If the data‐offset is present, it is relative to the base‐data‐offset established in the track
|
||
// fragment header.
|
||
//
|
||
// The composition offset values in the composition time‐to‐sample box and in the track run box may be
|
||
// signed or unsigned. The recommendations given in the composition time‐to‐sample box concerning the
|
||
// use of signed composition offsets also apply here.
|
||
type TrackFragmentRunBox struct {
|
||
*FullBox
|
||
// the number of samples being added in this run; also the number of rows in the
|
||
// following table (the rows can be empty)
|
||
SampleCount uint32
|
||
// is added to the implicit or explicit data_offset established in the track fragment header.
|
||
DataOffset int32
|
||
// provides a set of flags for the first sample only of this run.
|
||
FirstSampleFlags uint32
|
||
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
|
||
|
||
// TrackFragmentRunBoxFlagFirstSampleFlagsPresent First Sample Flags
|
||
// this over‐rides the default flags for the first sample only. This
|
||
// makes it possible to record a group of frames where the first is a key and the rest are difference
|
||
// frames, without supplying explicit flags for every sample. If this flag and field are used, sample‐
|
||
// flags shall not be present.
|
||
TrackFragmentRunBoxFlagFirstSampleFlagsPresent uint32 = 0x00000004
|
||
|
||
// TrackFragmentRunBoxFlagSampleDurationPresent Sample Duration Flag
|
||
// indicates that each sample has its own duration, otherwise the default is used.
|
||
TrackFragmentRunBoxFlagSampleDurationPresent uint32 = 0x00000100
|
||
|
||
// TrackFragmentRunBoxFlagSampleSizePresent Sample Size Flag
|
||
// each sample has its own size, otherwise the default is used.
|
||
TrackFragmentRunBoxFlagSampleSizePresent uint32 = 0x00000200
|
||
|
||
// TrackFragmentRunBoxFlagSampleFlagsPresent Sample Flags Present Flag
|
||
// each sample has its own flags, otherwise the default is used.
|
||
TrackFragmentRunBoxFlagSampleFlagsPresent uint32 = 0x00000400
|
||
|
||
// TrackFragmentRunBoxFlagSampleCompositionTimeOffsetPresent Sample Composition Time Offset
|
||
// each sample has a composition time offset
|
||
// (e.g. as used for I/P/B video in MPEG).
|
||
TrackFragmentRunBoxFlagSampleCompositionTimeOffsetPresent uint32 = 0x00000800
|
||
)
|
||
|
||
// ParseTrackFragmentRunBox creates a new Track Fragment Run Box struct
|
||
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
|
||
|
||
// create track fragment run box
|
||
box := &TrackFragmentRunBox{FullBox: fullBox}
|
||
|
||
// parse sample counter
|
||
box.SampleCount = binary.BigEndian.Uint32(content[position : position+4])
|
||
position += 4
|
||
|
||
// parse data offset
|
||
if fullBox.flagUInt32&TrackFragmentRunBoxFlagDataOffsetPresent == TrackFragmentRunBoxFlagDataOffsetPresent {
|
||
box.DataOffset = int32(binary.BigEndian.Uint32(content[position : position+4]))
|
||
position += 4
|
||
}
|
||
|
||
// parse first sample flags
|
||
if fullBox.flagUInt32&TrackFragmentRunBoxFlagFirstSampleFlagsPresent == TrackFragmentRunBoxFlagFirstSampleFlagsPresent {
|
||
box.FirstSampleFlags = binary.BigEndian.Uint32(content[position : position+4])
|
||
position += 4
|
||
}
|
||
|
||
// parse samples
|
||
for i := uint32(0); i < box.SampleCount; i++ {
|
||
sample := TrackFragmentRunBoxSample{}
|
||
|
||
// parse duration
|
||
if fullBox.flagUInt32&TrackFragmentRunBoxFlagSampleDurationPresent == TrackFragmentRunBoxFlagSampleDurationPresent {
|
||
sample.Duration = binary.BigEndian.Uint32(content[position : position+4])
|
||
position += 4
|
||
}
|
||
|
||
// parse size
|
||
if fullBox.flagUInt32&TrackFragmentRunBoxFlagSampleSizePresent == TrackFragmentRunBoxFlagSampleSizePresent {
|
||
sample.Size = binary.BigEndian.Uint32(content[position : position+4])
|
||
position += 4
|
||
}
|
||
|
||
// parse flags
|
||
if fullBox.flagUInt32&TrackFragmentRunBoxFlagSampleFlagsPresent == TrackFragmentRunBoxFlagSampleFlagsPresent {
|
||
sample.Flags = binary.BigEndian.Uint32(content[position : position+4])
|
||
position += 4
|
||
}
|
||
|
||
// parse composition time offset
|
||
if fullBox.flagUInt32&TrackFragmentRunBoxFlagSampleCompositionTimeOffsetPresent == TrackFragmentRunBoxFlagSampleCompositionTimeOffsetPresent {
|
||
if fullBox.Version == 0 {
|
||
sample.CompositionTimeOffsetV0 = binary.BigEndian.Uint32(content[position : position+4])
|
||
position += 4
|
||
} else {
|
||
sample.CompositionTimeOffset = int32(binary.BigEndian.Uint32(content[position : position+4]))
|
||
position += 4
|
||
}
|
||
}
|
||
|
||
// store new sample
|
||
box.Samples = append(box.Samples, sample)
|
||
}
|
||
|
||
// check, if everything has been parsed
|
||
if position != len(content) {
|
||
return nil, fmt.Errorf("invalid byte position %d; expected position %d in file box position %d", position, len(content), filePosition)
|
||
}
|
||
|
||
return box, nil
|
||
}
|
||
|
||
// TrackFragmentRunBoxSample contains sample information
|
||
type TrackFragmentRunBoxSample struct {
|
||
Duration uint32
|
||
Size uint32
|
||
Flags uint32
|
||
CompositionTimeOffsetV0 uint32
|
||
CompositionTimeOffset int32
|
||
}
|