1
0
Fork 0
forked from External/mediamtx

move pmp4 package into mediacommon (#3367)

This commit is contained in:
Alessandro Ros 2024-05-18 11:27:39 +02:00 committed by GitHub
parent af9792b44e
commit 425b7fa4fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 9 additions and 1617 deletions

2
go.mod
View file

@ -10,7 +10,7 @@ require (
github.com/alecthomas/kong v0.9.0
github.com/bluenviron/gohlslib v1.3.2
github.com/bluenviron/gortsplib/v4 v4.9.1-0.20240515082130-f283abc2e7cd
github.com/bluenviron/mediacommon v1.10.0
github.com/bluenviron/mediacommon v1.10.1-0.20240518092051-bab50c4ba9c5
github.com/datarhei/gosrt v0.6.0
github.com/fsnotify/fsnotify v1.7.0
github.com/gin-gonic/gin v1.10.0

4
go.sum
View file

@ -24,8 +24,8 @@ github.com/bluenviron/gohlslib v1.3.2 h1:xRiPfMIeYCkspL6jYa7Qrl4pIY+1w7IvFjx49Cs
github.com/bluenviron/gohlslib v1.3.2/go.mod h1:1/m7A2o5IWyBdZeauXe2bViu2l1mL2l8DMQl9302A2U=
github.com/bluenviron/gortsplib/v4 v4.9.1-0.20240515082130-f283abc2e7cd h1:w1Uml4bXdixu7cArQ3JyiZTpaKzZ31eP9+bWoPPkWcY=
github.com/bluenviron/gortsplib/v4 v4.9.1-0.20240515082130-f283abc2e7cd/go.mod h1:iLJ1tmwGMbaN04ZYh/KRlAHsCbz9Rycn7cPAvdR+Vkc=
github.com/bluenviron/mediacommon v1.10.0 h1:ffIWaS+1vYpPLV6QOt4VEvIlb/OKtodzagzsY6EDOnw=
github.com/bluenviron/mediacommon v1.10.0/go.mod h1:HDyW2CzjvhYJXtdxstdFPio3G0qSocPhqkhUt/qffec=
github.com/bluenviron/mediacommon v1.10.1-0.20240518092051-bab50c4ba9c5 h1:fZL+8Bz8wT0ljvt+ZyGGzirT1jQxH1wgqOiyRifAL60=
github.com/bluenviron/mediacommon v1.10.1-0.20240518092051-bab50c4ba9c5/go.mod h1:HDyW2CzjvhYJXtdxstdFPio3G0qSocPhqkhUt/qffec=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=

View file

@ -1,83 +0,0 @@
package mp4
import (
"io"
"github.com/abema/go-mp4"
)
type mp4Writer struct {
w *mp4.Writer
}
func newMP4Writer(w io.WriteSeeker) *mp4Writer {
return &mp4Writer{
w: mp4.NewWriter(w),
}
}
func (w *mp4Writer) writeBoxStart(box mp4.IImmutableBox) (int, error) {
bi := &mp4.BoxInfo{
Type: box.GetType(),
}
var err error
bi, err = w.w.StartBox(bi)
if err != nil {
return 0, err
}
_, err = mp4.Marshal(w.w, box, mp4.Context{})
if err != nil {
return 0, err
}
return int(bi.Offset), nil
}
func (w *mp4Writer) writeBoxEnd() error {
_, err := w.w.EndBox()
return err
}
func (w *mp4Writer) writeBox(box mp4.IImmutableBox) (int, error) {
off, err := w.writeBoxStart(box)
if err != nil {
return 0, err
}
err = w.writeBoxEnd()
if err != nil {
return 0, err
}
return off, nil
}
func (w *mp4Writer) rewriteBox(off int, box mp4.IImmutableBox) error {
prevOff, err := w.w.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
_, err = w.w.Seek(int64(off), io.SeekStart)
if err != nil {
return err
}
_, err = w.writeBoxStart(box)
if err != nil {
return err
}
err = w.writeBoxEnd()
if err != nil {
return err
}
_, err = w.w.Seek(prevOff, io.SeekStart)
if err != nil {
return err
}
return nil
}

View file

@ -1,209 +0,0 @@
// Package mp4 contains a MP4 muxer.
package mp4
import (
"io"
"time"
"github.com/abema/go-mp4"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4/seekablebuffer"
)
const (
globalTimescale = 1000
)
func durationMp4ToGo(v int64, timeScale uint32) time.Duration {
timeScale64 := int64(timeScale)
secs := v / timeScale64
dec := v % timeScale64
return time.Duration(secs)*time.Second + time.Duration(dec)*time.Second/time.Duration(timeScale64)
}
// Presentation is timed sequence of video/audio samples.
type Presentation struct {
Tracks []*Track
}
// Marshal encodes a Presentation.
func (p *Presentation) Marshal(w io.Writer) error {
/*
|ftyp|
|moov|
| |mvhd|
| |trak|
| |trak|
| |....|
|mdat|
*/
dataSize, sortedSamples := p.sortSamples()
err := p.marshalFtypAndMoov(w)
if err != nil {
return err
}
return p.marshalMdat(w, dataSize, sortedSamples)
}
func (p *Presentation) sortSamples() (uint32, []*Sample) {
sampleCount := 0
for _, track := range p.Tracks {
sampleCount += len(track.Samples)
}
processedSamples := make([]int, len(p.Tracks))
elapsed := make([]int64, len(p.Tracks))
offset := uint32(0)
sortedSamples := make([]*Sample, sampleCount)
pos := 0
for i, track := range p.Tracks {
elapsed[i] = int64(track.TimeOffset)
}
for {
bestTrack := -1
var bestElapsed time.Duration
for i, track := range p.Tracks {
if processedSamples[i] < len(track.Samples) {
elapsedGo := durationMp4ToGo(elapsed[i], track.TimeScale)
if bestTrack == -1 || elapsedGo < bestElapsed {
bestTrack = i
bestElapsed = elapsedGo
}
}
}
if bestTrack == -1 {
break
}
sample := p.Tracks[bestTrack].Samples[processedSamples[bestTrack]]
sample.offset = offset
processedSamples[bestTrack]++
elapsed[bestTrack] += int64(sample.Duration)
offset += sample.PayloadSize
sortedSamples[pos] = sample
pos++
}
return offset, sortedSamples
}
func (p *Presentation) marshalFtypAndMoov(w io.Writer) error {
var outBuf seekablebuffer.Buffer
mw := newMP4Writer(&outBuf)
_, err := mw.writeBox(&mp4.Ftyp{ // <ftyp/>
MajorBrand: [4]byte{'i', 's', 'o', 'm'},
MinorVersion: 1,
CompatibleBrands: []mp4.CompatibleBrandElem{
{CompatibleBrand: [4]byte{'i', 's', 'o', 'm'}},
{CompatibleBrand: [4]byte{'i', 's', 'o', '2'}},
{CompatibleBrand: [4]byte{'m', 'p', '4', '1'}},
{CompatibleBrand: [4]byte{'m', 'p', '4', '2'}},
},
})
if err != nil {
return err
}
_, err = mw.writeBoxStart(&mp4.Moov{}) // <moov>
if err != nil {
return err
}
mvhd := &mp4.Mvhd{ // <mvhd/>
Timescale: globalTimescale,
Rate: 65536,
Volume: 256,
Matrix: [9]int32{0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000},
NextTrackID: uint32(len(p.Tracks) + 1),
}
mvhdOffset, err := mw.writeBox(mvhd)
if err != nil {
return err
}
stcos := make([]*mp4.Stco, len(p.Tracks))
stcosOffsets := make([]int, len(p.Tracks))
for i, track := range p.Tracks {
var res *headerTrackMarshalResult
res, err = track.marshal(mw)
if err != nil {
return err
}
stcos[i] = res.stco
stcosOffsets[i] = res.stcoOffset
if res.presentationDuration > mvhd.DurationV0 {
mvhd.DurationV0 = res.presentationDuration
}
}
err = mw.rewriteBox(mvhdOffset, mvhd)
if err != nil {
return err
}
err = mw.writeBoxEnd() // </moov>
if err != nil {
return err
}
moovEndOffset, err := outBuf.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
dataOffset := moovEndOffset + 8
for i := range p.Tracks {
for j := range stcos[i].ChunkOffset {
stcos[i].ChunkOffset[j] += uint32(dataOffset)
}
err = mw.rewriteBox(stcosOffsets[i], stcos[i])
if err != nil {
return err
}
}
_, err = w.Write(outBuf.Bytes())
return err
}
func (p *Presentation) marshalMdat(w io.Writer, dataSize uint32, sortedSamples []*Sample) error {
mdatSize := uint32(8) + dataSize
_, err := w.Write([]byte{byte(mdatSize >> 24), byte(mdatSize >> 16), byte(mdatSize >> 8), byte(mdatSize)})
if err != nil {
return err
}
_, err = w.Write([]byte{'m', 'd', 'a', 't'})
if err != nil {
return err
}
for _, sa := range sortedSamples {
pl, err := sa.GetPayload()
if err != nil {
return err
}
_, err = w.Write(pl)
if err != nil {
return err
}
}
return nil
}

View file

@ -1,165 +0,0 @@
package mp4
import (
"bytes"
"testing"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4"
"github.com/bluenviron/mediamtx/internal/test"
"github.com/stretchr/testify/require"
)
var casesPresentation = []struct {
name string
dec Presentation
enc []byte
}{
{
"standard",
Presentation{
Tracks: []*Track{
{
ID: 1,
TimeScale: 90000,
TimeOffset: -90000,
Codec: &fmp4.CodecH264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
Samples: []*Sample{
{
Duration: 90000,
PTSOffset: -45000,
PayloadSize: 2,
GetPayload: func() ([]byte, error) {
return []byte{1, 2}, nil
},
},
{
Duration: 90000,
PayloadSize: 2,
GetPayload: func() ([]byte, error) {
return []byte{3, 4}, nil
},
},
{
Duration: 90000,
PTSOffset: -45000,
PayloadSize: 2,
GetPayload: func() ([]byte, error) {
return []byte{5, 6}, nil
},
},
},
},
},
},
[]byte{
0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70,
0x69, 0x73, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x01,
0x69, 0x73, 0x6f, 0x6d, 0x69, 0x73, 0x6f, 0x32,
0x6d, 0x70, 0x34, 0x31, 0x6d, 0x70, 0x34, 0x32,
0x00, 0x00, 0x02, 0xbf, 0x6d, 0x6f, 0x6f, 0x76,
0x00, 0x00, 0x00, 0x6c, 0x6d, 0x76, 0x68, 0x64,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
0x00, 0x00, 0x07, 0xd0, 0x00, 0x01, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x4b,
0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c,
0x74, 0x6b, 0x68, 0x64, 0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xd0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x07, 0x80, 0x00, 0x00, 0x04, 0x38, 0x00, 0x00,
0x00, 0x00, 0x00, 0x24, 0x65, 0x64, 0x74, 0x73,
0x00, 0x00, 0x00, 0x1c, 0x65, 0x6c, 0x73, 0x74,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x0f, 0xa0, 0x00, 0x01, 0x5f, 0x90,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc3,
0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20,
0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x5f, 0x90, 0x00, 0x02, 0xbf, 0x20,
0x55, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65,
0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
0x00, 0x00, 0x00, 0x01, 0x6e, 0x6d, 0x69, 0x6e,
0x66, 0x00, 0x00, 0x00, 0x14, 0x76, 0x6d, 0x68,
0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x24, 0x64, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00,
0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0c, 0x75, 0x72, 0x6c, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x01, 0x2e, 0x73, 0x74, 0x62,
0x6c, 0x00, 0x00, 0x00, 0x96, 0x73, 0x74, 0x73,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x86, 0x61, 0x76, 0x63,
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x07, 0x80, 0x04, 0x38, 0x00, 0x48, 0x00,
0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0x00,
0x00, 0x00, 0x30, 0x61, 0x76, 0x63, 0x43, 0x01,
0x42, 0xc0, 0x28, 0x03, 0x01, 0x00, 0x19, 0x67,
0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02, 0x27,
0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00,
0x00, 0x03, 0x00, 0xf0, 0x3c, 0x60, 0xc9, 0x20,
0x01, 0x00, 0x04, 0x08, 0x06, 0x07, 0x08, 0x00,
0x00, 0x00, 0x18, 0x73, 0x74, 0x74, 0x73, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x01, 0x5f, 0x90, 0x00,
0x00, 0x00, 0x28, 0x63, 0x74, 0x74, 0x73, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x01, 0xff, 0xff, 0x50, 0x38, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xff, 0xff, 0x50, 0x38, 0x00,
0x00, 0x00, 0x1c, 0x73, 0x74, 0x73, 0x63, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x73,
0x74, 0x73, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x73,
0x74, 0x63, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xe7, 0x00,
0x00, 0x00, 0x0e, 0x6d, 0x64, 0x61, 0x74, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06,
},
},
}
func TestPresentationMarshal(t *testing.T) {
for _, ca := range casesPresentation {
t.Run(ca.name, func(t *testing.T) {
var buf bytes.Buffer
err := ca.dec.Marshal(&buf)
require.NoError(t, err)
require.Equal(t, ca.enc, buf.Bytes())
})
}
}

View file

@ -1,12 +0,0 @@
package mp4
// Sample is a sample of a Track.
type Sample struct {
Duration uint32
PTSOffset int32
IsNonSyncSample bool
PayloadSize uint32
GetPayload func() ([]byte, error)
offset uint32 // filled by sortSamples
}

File diff suppressed because it is too large Load diff

View file

@ -4,11 +4,11 @@ import (
"io"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4"
"github.com/bluenviron/mediamtx/internal/playback/mp4"
"github.com/bluenviron/mediacommon/pkg/formats/pmp4"
)
type muxerMP4Track struct {
mp4.Track
pmp4.Track
lastDTS int64
}
@ -33,7 +33,7 @@ func (w *muxerMP4) writeInit(init *fmp4.Init) {
for i, track := range init.Tracks {
w.tracks[i] = &muxerMP4Track{
Track: mp4.Track{
Track: pmp4.Track{
ID: track.ID,
TimeScale: track.TimeScale,
Codec: track.Codec,
@ -73,7 +73,7 @@ func (w *muxerMP4) writeSample(
ptsOffset = 0
}
w.curTrack.Samples = append(w.curTrack.Samples, &mp4.Sample{
w.curTrack.Samples = append(w.curTrack.Samples, &pmp4.Sample{
PTSOffset: ptsOffset,
IsNonSyncSample: isNonSyncSample,
PayloadSize: payloadSize,
@ -93,8 +93,8 @@ func (w *muxerMP4) writeFinalDTS(dts int64) {
}
func (w *muxerMP4) flush() error {
h := mp4.Presentation{
Tracks: make([]*mp4.Track, len(w.tracks)),
h := pmp4.Presentation{
Tracks: make([]*pmp4.Track, len(w.tracks)),
}
for i, track := range w.tracks {