mp4/TrackFragmentRunBox.go
2024-12-03 19:24:00 +01:00

174 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 durationisempty 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 dataoffset is not present, then the data for this run starts immediately after the data of the
// previous run, or at the basedataoffset defined by the track fragment header if this is the first run in a
// track fragment, If the dataoffset is present, it is relative to the basedataoffset established in the track
// fragment header.
//
// The composition offset values in the composition timetosample box and in the track run box may be
// signed or unsigned. The recommendations given in the composition timetosample 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 overrides 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
}