Skip to content

Commit

Permalink
playback: increase sequence number of fMP4 parts
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Apr 7, 2024
1 parent b64e082 commit d25aef9
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 143 deletions.
10 changes: 10 additions & 0 deletions internal/playback/muxer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package playback

import "github.com/bluenviron/mediacommon/pkg/formats/fmp4"

type muxer interface {
writeInit(init []byte)
setTrack(trackID int)
writeSample(normalizedElapsed int64, sample *fmp4.PartSample)
flush() error
}
120 changes: 120 additions & 0 deletions internal/playback/muxer_fmp4.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package playback

import (
"io"

"github.com/bluenviron/mediacommon/pkg/formats/fmp4"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4/seekablebuffer"
)

type muxerFMP4Track struct {
started bool
fmp4.PartTrack
}

func findTrack(tracks []*muxerFMP4Track, id int) *muxerFMP4Track {
for _, track := range tracks {
if track.ID == id {
return track
}
}
return nil
}

type muxerFMP4 struct {
w io.Writer

init []byte
nextSequenceNumber uint32
tracks []*muxerFMP4Track
curTrack *muxerFMP4Track
outBuf seekablebuffer.Buffer
}

func (w *muxerFMP4) writeInit(init []byte) {
w.init = init
}

func (w *muxerFMP4) setTrack(trackID int) {
w.curTrack = findTrack(w.tracks, trackID)
if w.curTrack == nil {
w.curTrack = &muxerFMP4Track{
PartTrack: fmp4.PartTrack{
ID: trackID,
},
}
w.tracks = append(w.tracks, w.curTrack)
}
}

func (w *muxerFMP4) writeSample(normalizedElapsed int64, sample *fmp4.PartSample) {
if !w.curTrack.started {
if normalizedElapsed >= 0 {
w.curTrack.started = true
w.curTrack.BaseTime = uint64(normalizedElapsed)

if !sample.IsNonSyncSample {
w.curTrack.Samples = []*fmp4.PartSample{sample}

Check warning on line 57 in internal/playback/muxer_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/playback/muxer_fmp4.go#L57

Added line #L57 was not covered by tests
} else {
w.curTrack.Samples = append(w.curTrack.Samples, sample)
}
} else {
sample.Duration = 0
sample.PTSOffset = 0

if !sample.IsNonSyncSample {
w.curTrack.Samples = []*fmp4.PartSample{sample}
} else {
w.curTrack.Samples = append(w.curTrack.Samples, sample)
}

Check warning on line 69 in internal/playback/muxer_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/playback/muxer_fmp4.go#L68-L69

Added lines #L68 - L69 were not covered by tests
}
} else {
if w.curTrack.Samples == nil {
w.curTrack.BaseTime = uint64(normalizedElapsed)
}
w.curTrack.Samples = append(w.curTrack.Samples, sample)
}
}

func (w *muxerFMP4) flush() error {
var part fmp4.Part

for _, track := range w.tracks {
if track.started && track.Samples != nil {
part.Tracks = append(part.Tracks, &track.PartTrack)
}
}

if part.Tracks != nil {
part.SequenceNumber = w.nextSequenceNumber
w.nextSequenceNumber++

if w.init != nil {
_, err := w.w.Write(w.init)
if err != nil {
return err
}

Check warning on line 96 in internal/playback/muxer_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/playback/muxer_fmp4.go#L95-L96

Added lines #L95 - L96 were not covered by tests
w.init = nil
}

err := part.Marshal(&w.outBuf)
if err != nil {
return err
}

Check warning on line 103 in internal/playback/muxer_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/playback/muxer_fmp4.go#L102-L103

Added lines #L102 - L103 were not covered by tests

_, err = w.w.Write(w.outBuf.Bytes())
if err != nil {
return err
}

Check warning on line 108 in internal/playback/muxer_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/playback/muxer_fmp4.go#L107-L108

Added lines #L107 - L108 were not covered by tests

w.outBuf.Reset()
}

for _, track := range w.tracks {
if track.started {
track.Samples = nil
}
}

return nil
}
32 changes: 24 additions & 8 deletions internal/playback/on_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package playback
import (
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
Expand All @@ -17,6 +16,20 @@ import (

var errStopIteration = errors.New("stop iteration")

type writerWrapper struct {
ctx *gin.Context
written bool
}

func (w *writerWrapper) Write(p []byte) (int, error) {
if !w.written {
w.written = true
w.ctx.Header("Accept-Ranges", "none")
w.ctx.Header("Content-Type", "video/mp4")
}
return w.ctx.Writer.Write(p)
}

func parseDuration(raw string) (time.Duration, error) {
// seconds
if secs, err := strconv.ParseFloat(raw, 64); err == nil {
Expand All @@ -32,7 +45,7 @@ func seekAndMux(
segments []*Segment,
start time.Time,
duration time.Duration,
w io.Writer,
w muxer,
) error {
if recordFormat == conf.RecordFormatFMP4 {
minTime := start.Sub(segments[0].Start)
Expand All @@ -47,12 +60,14 @@ func seekAndMux(
}
defer f.Close()

init, err = fmp4ReadInit(f)
init, err = segmentFMP4ReadInit(f)
if err != nil {
return err
}

maxElapsed, err = fmp4SeekAndMuxParts(f, init, minTime, maxTime, w)
w.writeInit(init)

maxElapsed, err = segmentFMP4SeekAndMuxParts(f, minTime, maxTime, w)
if err != nil {
return err
}
Expand All @@ -76,16 +91,16 @@ func seekAndMux(
}
defer f.Close()

init, err := fmp4ReadInit(f)
init, err := segmentFMP4ReadInit(f)
if err != nil {
return err
}

if !fmp4CanBeConcatenated(prevInit, prevEnd, init, seg.Start) {
if !segmentFMP4CanBeConcatenated(prevInit, prevEnd, init, seg.Start) {
return errStopIteration
}

maxElapsed, err = fmp4MuxParts(f, overallElapsed, duration, w)
maxElapsed, err = segmentFMP4WriteParts(f, overallElapsed, duration, w)
if err != nil {
return err
}
Expand Down Expand Up @@ -153,8 +168,9 @@ func (p *Server) onGet(ctx *gin.Context) {
}

ww := &writerWrapper{ctx: ctx}
sw := &muxerFMP4{w: ww}

err = seekAndMux(pathConf.RecordFormat, segments, start, duration, ww)
err = seekAndMux(pathConf.RecordFormat, segments, start, duration, sw)
if err != nil {
// user aborted the download
var neterr *net.OpError
Expand Down
4 changes: 2 additions & 2 deletions internal/playback/on_get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func writeSegment1(t *testing.T, fpath string) {
}},
},
{
SequenceNumber: 1,
SequenceNumber: 2,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Expand Down Expand Up @@ -293,7 +293,7 @@ func TestOnGet(t *testing.T) {
},
},
{
SequenceNumber: 0,
SequenceNumber: 1,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Expand Down
6 changes: 3 additions & 3 deletions internal/playback/on_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func computeDurationAndConcatenate(recordFormat conf.RecordFormat, segments []*S
}
defer f.Close()

init, err := fmp4ReadInit(f)
init, err := segmentFMP4ReadInit(f)
if err != nil {
return err
}
Expand All @@ -47,12 +47,12 @@ func computeDurationAndConcatenate(recordFormat conf.RecordFormat, segments []*S
return err
}

maxDuration, err := fmp4ReadMaxDuration(f)
maxDuration, err := segmentFMP4ReadMaxDuration(f)
if err != nil {
return err
}

if len(out) != 0 && fmp4CanBeConcatenated(
if len(out) != 0 && segmentFMP4CanBeConcatenated(
prevInit,
out[len(out)-1].Start.Add(time.Duration(out[len(out)-1].Duration)),
init,
Expand Down
Loading

0 comments on commit d25aef9

Please sign in to comment.