Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
tobbee committed Feb 13, 2024
1 parent 7da3674 commit 78b1ef3
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 150 deletions.
151 changes: 1 addition & 150 deletions examples/add-sidx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func run(inPath, outPath string, nonZeroEPT bool) error {
return err
}

err = updateSidx(inFile, nonZeroEPT)
err = inFile.UpdateSidx(true /* addIfNotExists */, nonZeroEPT)
if err != nil {
return fmt.Errorf("addSidx failed: %w", err)
}
Expand All @@ -51,152 +51,3 @@ func run(inPath, outPath string, nonZeroEPT bool) error {
}
return nil
}

type segData struct {
startPos uint64
presentationTime uint64
baseDecodeTime uint64
dur uint32
size uint32
}

// updateSidx updates or adds a top-level sidx box to a fragmented mp4 file.
func updateSidx(inFile *mp4.File, nonZeroEPT bool) error {

if !inFile.IsFragmented() {
return fmt.Errorf("input file is not fragmented")
}

initSeg := inFile.Init
if initSeg == nil {
return fmt.Errorf("input file does not have an init segment")
}

segs := inFile.Segments
if len(segs) == 0 {
return fmt.Errorf("input file does not have any media segments")
}
update := inFile.Sidx != nil

refTrak := findReferenceTrak(initSeg)
trex, ok := initSeg.Moov.Mvex.GetTrex(refTrak.Tkhd.TrackID)
if !ok {
return fmt.Errorf("no trex box found for track %d", refTrak.Tkhd.TrackID)
}
segDatas, err := findSegmentData(segs, refTrak, trex)
if err != nil {
return fmt.Errorf("failed to find segment data: %w", err)
}

var sidx *mp4.SidxBox
if update {
sidx = inFile.Sidx
} else {
sidx = &mp4.SidxBox{}
}
fillSidx(sidx, refTrak, segDatas, nonZeroEPT)
if !update {
err = insertSidx(inFile, segDatas, sidx)
if err != nil {
return fmt.Errorf("failed to insert sidx box: %w", err)
}
}
return nil
}

func findReferenceTrak(initSeg *mp4.InitSegment) *mp4.TrakBox {
var trak *mp4.TrakBox
for _, trak = range initSeg.Moov.Traks {
if trak.Mdia.Hdlr.HandlerType == "vide" {
return trak
}
}
for _, trak = range initSeg.Moov.Traks {
if trak.Mdia.Hdlr.HandlerType == "soun" {
return trak
}
}
return initSeg.Moov.Traks[0]
}

func findSegmentData(segs []*mp4.MediaSegment, refTrak *mp4.TrakBox, trex *mp4.TrexBox) ([]segData, error) {
segDatas := make([]segData, 0, len(segs))
for _, seg := range segs {
frag := seg.Fragments[0]
for _, traf := range frag.Moof.Trafs {
tfhd := traf.Tfhd
if tfhd.TrackID == refTrak.Tkhd.TrackID {
// Found the track that the sidx should be based on
baseTime := traf.Tfdt.BaseMediaDecodeTime()
dur := uint32(0)
var firstCompositionTimeOffest int64
for i, trun := range traf.Truns {
trun.AddSampleDefaultValues(tfhd, trex)
samples := trun.GetSamples()
for j, sample := range samples {
if i == 0 && j == 0 {
firstCompositionTimeOffest = int64(sample.CompositionTimeOffset)
}
dur += sample.Dur
}
}
sd := segData{
startPos: seg.StartPos,
presentationTime: uint64(int64(baseTime) + firstCompositionTimeOffest),
baseDecodeTime: baseTime,
dur: dur,
size: uint32(seg.Size()),
}
segDatas = append(segDatas, sd)
break
}
}
}
return segDatas, nil
}

func fillSidx(sidx *mp4.SidxBox, refTrak *mp4.TrakBox, segDatas []segData, nonZeroEPT bool) {
ept := uint64(0)
if nonZeroEPT {
ept = segDatas[0].presentationTime
}
sidx.Version = 1
sidx.Timescale = refTrak.Mdia.Mdhd.Timescale
sidx.ReferenceID = 1
sidx.EarliestPresentationTime = ept
sidx.FirstOffset = 0
sidx.SidxRefs = make([]mp4.SidxRef, 0, len(segDatas))

for _, segData := range segDatas {
size := segData.size
sidx.SidxRefs = append(sidx.SidxRefs, mp4.SidxRef{
ReferencedSize: size,
SubSegmentDuration: segData.dur,
StartsWithSAP: 1,
SAPType: 1,
})
}
}

func insertSidx(inFile *mp4.File, segDatas []segData, sidx *mp4.SidxBox) error {
// insert sidx box before first media segment
// TODO. Handle case where startPos is not reliable. Maybe first box of first segment
firstMediaBox, err := inFile.Segments[0].FirstBox()
if err != nil {
return fmt.Errorf("could not find position to insert sidx box: %w", err)
}
var mediaStartIdx = 0
for i, ch := range inFile.Children {
if ch == firstMediaBox {
mediaStartIdx = i
break
}
}
if mediaStartIdx == 0 {
return fmt.Errorf("could not find position to insert sidx box")
}
inFile.Children = append(inFile.Children[:mediaStartIdx], append([]mp4.Box{sidx}, inFile.Children[mediaStartIdx:]...)...)
inFile.Sidx = sidx
inFile.Sidxs = []*mp4.SidxBox{sidx}
return nil
}
151 changes: 151 additions & 0 deletions mp4/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,157 @@ func (f *File) CopySampleData(w io.Writer, rs io.ReadSeeker, trak *TrakBox,
return nil
}

func (f *File) UpdateSidx(addIfNotExists, nonZeroEPT bool) error {

if !f.IsFragmented() {
return fmt.Errorf("input file is not fragmented")
}

initSeg := f.Init
if initSeg == nil {
return fmt.Errorf("input file does not have an init segment")
}

segs := f.Segments
if len(segs) == 0 {
return fmt.Errorf("input file does not have any media segments")
}
exists := f.Sidx != nil
if !exists && !addIfNotExists {
return nil
}

refTrak := findReferenceTrak(initSeg)
trex, ok := initSeg.Moov.Mvex.GetTrex(refTrak.Tkhd.TrackID)
if !ok {
return fmt.Errorf("no trex box found for track %d", refTrak.Tkhd.TrackID)
}
segDatas, err := findSegmentData(segs, refTrak, trex)
if err != nil {
return fmt.Errorf("failed to find segment data: %w", err)
}

var sidx *SidxBox
if exists {
sidx = f.Sidx
} else {
sidx = &SidxBox{}
}
fillSidx(sidx, refTrak, segDatas, nonZeroEPT)
if !exists {
err = insertSidx(f, segDatas, sidx)
if err != nil {
return fmt.Errorf("failed to insert sidx box: %w", err)
}
}
return nil
}

func findReferenceTrak(initSeg *InitSegment) *TrakBox {
var trak *TrakBox
for _, trak = range initSeg.Moov.Traks {
if trak.Mdia.Hdlr.HandlerType == "vide" {
return trak
}
}
for _, trak = range initSeg.Moov.Traks {
if trak.Mdia.Hdlr.HandlerType == "soun" {
return trak
}
}
return initSeg.Moov.Traks[0]
}

type segData struct {
startPos uint64
presentationTime uint64
baseDecodeTime uint64
dur uint32
size uint32
}

func findSegmentData(segs []*MediaSegment, refTrak *TrakBox, trex *TrexBox) ([]segData, error) {
segDatas := make([]segData, 0, len(segs))
for _, seg := range segs {
frag := seg.Fragments[0]
for _, traf := range frag.Moof.Trafs {
tfhd := traf.Tfhd
if tfhd.TrackID == refTrak.Tkhd.TrackID {
// Found the track that the sidx should be based on
baseTime := traf.Tfdt.BaseMediaDecodeTime()
dur := uint32(0)
var firstCompositionTimeOffest int64
for i, trun := range traf.Truns {
trun.AddSampleDefaultValues(tfhd, trex)
samples := trun.GetSamples()
for j, sample := range samples {
if i == 0 && j == 0 {
firstCompositionTimeOffest = int64(sample.CompositionTimeOffset)
}
dur += sample.Dur
}
}
sd := segData{
startPos: seg.StartPos,
presentationTime: uint64(int64(baseTime) + firstCompositionTimeOffest),
baseDecodeTime: baseTime,
dur: dur,
size: uint32(seg.Size()),
}
segDatas = append(segDatas, sd)
break
}
}
}
return segDatas, nil
}

func fillSidx(sidx *SidxBox, refTrak *TrakBox, segDatas []segData, nonZeroEPT bool) {
ept := uint64(0)
if nonZeroEPT {
ept = segDatas[0].presentationTime
}
sidx.Version = 1
sidx.Timescale = refTrak.Mdia.Mdhd.Timescale
sidx.ReferenceID = 1
sidx.EarliestPresentationTime = ept
sidx.FirstOffset = 0
sidx.SidxRefs = make([]SidxRef, 0, len(segDatas))

for _, segData := range segDatas {
size := segData.size
sidx.SidxRefs = append(sidx.SidxRefs, SidxRef{
ReferencedSize: size,
SubSegmentDuration: segData.dur,
StartsWithSAP: 1,
SAPType: 1,
})
}
}

func insertSidx(inFile *File, segDatas []segData, sidx *SidxBox) error {
// insert sidx box before first media segment
// TODO. Handle case where startPos is not reliable. Maybe first box of first segment
firstMediaBox, err := inFile.Segments[0].FirstBox()
if err != nil {
return fmt.Errorf("could not find position to insert sidx box: %w", err)
}
var mediaStartIdx = 0
for i, ch := range inFile.Children {
if ch == firstMediaBox {
mediaStartIdx = i
break
}
}
if mediaStartIdx == 0 {
return fmt.Errorf("could not find position to insert sidx box")
}
inFile.Children = append(inFile.Children[:mediaStartIdx], append([]Box{sidx}, inFile.Children[mediaStartIdx:]...)...)
inFile.Sidx = sidx
inFile.Sidxs = []*SidxBox{sidx}
return nil
}

func min(a, b int) int {
if a < b {
return a
Expand Down
26 changes: 26 additions & 0 deletions mp4/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,29 @@ func TestGetSegmentBoundariesFromMfra(t *testing.T) {
t.Errorf("not 3 segments in file but %d", len(parsedFile.Segments))
}
}

func TestUpdateSidx(t *testing.T) {
file, err := os.Open("./testdata/prog_8s_dec_dashinit.mp4")
if err != nil {
t.Error(err)
}

parsedFile, err := DecodeFile(file)
if err != nil {
t.Error(err)
}
err = parsedFile.UpdateSidx(false, false)
if err != nil {
t.Error(err)
}
if parsedFile.Sidx != nil {
t.Error("sidx should not be present")
}
err = parsedFile.UpdateSidx(true, false)
if err != nil {
t.Error(err)
}
if parsedFile.Sidx == nil {
t.Error("sidx should be present")
}
}

0 comments on commit 78b1ef3

Please sign in to comment.