Browse Source

move mp4 writer into dedicated folder

pull/1003/head
aler9 4 years ago
parent
commit
750743d1ed
  1. 93
      internal/hls/mp4writer.go
  2. 186
      internal/hls/muxer_variant_fmp4_init.go
  3. 74
      internal/hls/muxer_variant_fmp4_part.go
  4. 100
      internal/mp4/writer.go

93
internal/hls/mp4writer.go

@ -1,93 +0,0 @@
package hls
import (
"io"
"github.com/abema/go-mp4"
"github.com/orcaman/writerseeker"
)
type mp4Writer struct {
buf *writerseeker.WriterSeeker
w *mp4.Writer
}
func newMP4Writer() *mp4Writer {
w := &mp4Writer{
buf: &writerseeker.WriterSeeker{},
}
w.w = mp4.NewWriter(w.buf)
return 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
}
func (w *mp4Writer) bytes() []byte {
return w.buf.Bytes()
}

186
internal/hls/muxer_variant_fmp4_init.go

@ -1,26 +1,28 @@
package hls package hls
import ( import (
"github.com/abema/go-mp4" gomp4 "github.com/abema/go-mp4"
"github.com/aler9/gortsplib" "github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/aac" "github.com/aler9/gortsplib/pkg/aac"
"github.com/aler9/gortsplib/pkg/h264" "github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/rtsp-simple-server/internal/mp4"
) )
type myEsds struct { type myEsds struct {
mp4.FullBox `mp4:"0,extend"` gomp4.FullBox `mp4:"0,extend"`
Data []byte `mp4:"1,size=8"` Data []byte `mp4:"1,size=8"`
} }
func (*myEsds) GetType() mp4.BoxType { func (*myEsds) GetType() gomp4.BoxType {
return mp4.StrToBoxType("esds") return gomp4.StrToBoxType("esds")
} }
func init() { //nolint:gochecknoinits func init() { //nolint:gochecknoinits
mp4.AddBoxDef(&myEsds{}, 0) gomp4.AddBoxDef(&myEsds{}, 0)
} }
func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.TrackH264) error { func mp4InitGenerateVideoTrack(w *mp4.Writer, trackID int, videoTrack *gortsplib.TrackH264) error {
/* /*
trak trak
- tkhd - tkhd
@ -44,7 +46,7 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
- stco - stco
*/ */
_, err := w.writeBoxStart(&mp4.Trak{}) // <trak> _, err := w.WriteBoxStart(&gomp4.Trak{}) // <trak>
if err != nil { if err != nil {
return err return err
} }
@ -61,8 +63,8 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
width := spsp.Width() width := spsp.Width()
height := spsp.Height() height := spsp.Height()
_, err = w.writeBox(&mp4.Tkhd{ // <tkhd/> _, err = w.WriteBox(&gomp4.Tkhd{ // <tkhd/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 3}, Flags: [3]byte{0, 0, 3},
}, },
TrackID: uint32(trackID), TrackID: uint32(trackID),
@ -74,12 +76,12 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
_, err = w.writeBoxStart(&mp4.Mdia{}) // <mdia> _, err = w.WriteBoxStart(&gomp4.Mdia{}) // <mdia>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Mdhd{ // <mdhd/> _, err = w.WriteBox(&gomp4.Mdhd{ // <mdhd/>
Timescale: fmp4VideoTimescale, // the number of time units that pass per second Timescale: fmp4VideoTimescale, // the number of time units that pass per second
Language: [3]byte{'u', 'n', 'd'}, Language: [3]byte{'u', 'n', 'd'},
}) })
@ -87,7 +89,7 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
_, err = w.writeBox(&mp4.Hdlr{ // <hdlr/> _, err = w.WriteBox(&gomp4.Hdlr{ // <hdlr/>
HandlerType: [4]byte{'v', 'i', 'd', 'e'}, HandlerType: [4]byte{'v', 'i', 'd', 'e'},
Name: "VideoHandler", Name: "VideoHandler",
}) })
@ -95,13 +97,13 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
_, err = w.writeBoxStart(&mp4.Minf{}) // <minf> _, err = w.WriteBoxStart(&gomp4.Minf{}) // <minf>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Vmhd{ // <vmhd/> _, err = w.WriteBox(&gomp4.Vmhd{ // <vmhd/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 1}, Flags: [3]byte{0, 0, 1},
}, },
}) })
@ -109,20 +111,20 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
_, err = w.writeBoxStart(&mp4.Dinf{}) // <dinf> _, err = w.WriteBoxStart(&gomp4.Dinf{}) // <dinf>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Dref{ // <dref> _, err = w.WriteBoxStart(&gomp4.Dref{ // <dref>
EntryCount: 1, EntryCount: 1,
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Url{ // <url/> _, err = w.WriteBox(&gomp4.Url{ // <url/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 1}, Flags: [3]byte{0, 0, 1},
}, },
}) })
@ -130,32 +132,32 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
err = w.writeBoxEnd() // </dref> err = w.WriteBoxEnd() // </dref>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </dinf> err = w.WriteBoxEnd() // </dinf>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Stbl{}) // <stbl> _, err = w.WriteBoxStart(&gomp4.Stbl{}) // <stbl>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Stsd{ // <stsd> _, err = w.WriteBoxStart(&gomp4.Stsd{ // <stsd>
EntryCount: 1, EntryCount: 1,
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <avc1> _, err = w.WriteBoxStart(&gomp4.VisualSampleEntry{ // <avc1>
SampleEntry: mp4.SampleEntry{ SampleEntry: gomp4.SampleEntry{
AnyTypeBox: mp4.AnyTypeBox{ AnyTypeBox: gomp4.AnyTypeBox{
Type: mp4.BoxTypeAvc1(), Type: gomp4.BoxTypeAvc1(),
}, },
DataReferenceIndex: 1, DataReferenceIndex: 1,
}, },
@ -171,9 +173,9 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
_, err = w.writeBox(&mp4.AVCDecoderConfiguration{ // <avcc/> _, err = w.WriteBox(&gomp4.AVCDecoderConfiguration{ // <avcc/>
AnyTypeBox: mp4.AnyTypeBox{ AnyTypeBox: gomp4.AnyTypeBox{
Type: mp4.BoxTypeAvcC(), Type: gomp4.BoxTypeAvcC(),
}, },
ConfigurationVersion: 1, ConfigurationVersion: 1,
Profile: spsp.ProfileIdc, Profile: spsp.ProfileIdc,
@ -181,14 +183,14 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
Level: spsp.LevelIdc, Level: spsp.LevelIdc,
LengthSizeMinusOne: 3, LengthSizeMinusOne: 3,
NumOfSequenceParameterSets: 1, NumOfSequenceParameterSets: 1,
SequenceParameterSets: []mp4.AVCParameterSet{ SequenceParameterSets: []gomp4.AVCParameterSet{
{ {
Length: uint16(len(sps)), Length: uint16(len(sps)),
NALUnit: sps, NALUnit: sps,
}, },
}, },
NumOfPictureParameterSets: 1, NumOfPictureParameterSets: 1,
PictureParameterSets: []mp4.AVCParameterSet{ PictureParameterSets: []gomp4.AVCParameterSet{
{ {
Length: uint16(len(pps)), Length: uint16(len(pps)),
NALUnit: pps, NALUnit: pps,
@ -199,7 +201,7 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
_, err = w.writeBox(&mp4.Btrt{ // <btrt/> _, err = w.WriteBox(&gomp4.Btrt{ // <btrt/>
MaxBitrate: 1000000, MaxBitrate: 1000000,
AvgBitrate: 1000000, AvgBitrate: 1000000,
}) })
@ -207,56 +209,56 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return err return err
} }
err = w.writeBoxEnd() // </avc1> err = w.WriteBoxEnd() // </avc1>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </stsd> err = w.WriteBoxEnd() // </stsd>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stts{ // <stts> _, err = w.WriteBox(&gomp4.Stts{ // <stts>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stsc{ // <stsc> _, err = w.WriteBox(&gomp4.Stsc{ // <stsc>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stsz{ // <stsz> _, err = w.WriteBox(&gomp4.Stsz{ // <stsz>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stco{ // <stco> _, err = w.WriteBox(&gomp4.Stco{ // <stco>
}) })
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </stbl> err = w.WriteBoxEnd() // </stbl>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </minf> err = w.WriteBoxEnd() // </minf>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </mdia> err = w.WriteBoxEnd() // </mdia>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </trak> err = w.WriteBoxEnd() // </trak>
if err != nil { if err != nil {
return err return err
} }
@ -264,7 +266,7 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib.
return nil return nil
} }
func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.TrackAAC) error { func mp4InitGenerateAudioTrack(w *mp4.Writer, trackID int, audioTrack *gortsplib.TrackAAC) error {
/* /*
trak trak
- tkhd - tkhd
@ -287,13 +289,13 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
- stco - stco
*/ */
_, err := w.writeBoxStart(&mp4.Trak{}) // <trak> _, err := w.WriteBoxStart(&gomp4.Trak{}) // <trak>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Tkhd{ // <tkhd/> _, err = w.WriteBox(&gomp4.Tkhd{ // <tkhd/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 3}, Flags: [3]byte{0, 0, 3},
}, },
TrackID: uint32(trackID), TrackID: uint32(trackID),
@ -305,12 +307,12 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
return err return err
} }
_, err = w.writeBoxStart(&mp4.Mdia{}) // <mdia> _, err = w.WriteBoxStart(&gomp4.Mdia{}) // <mdia>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Mdhd{ // <mdhd/> _, err = w.WriteBox(&gomp4.Mdhd{ // <mdhd/>
Timescale: uint32(audioTrack.ClockRate()), Timescale: uint32(audioTrack.ClockRate()),
Language: [3]byte{'u', 'n', 'd'}, Language: [3]byte{'u', 'n', 'd'},
}) })
@ -318,7 +320,7 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
return err return err
} }
_, err = w.writeBox(&mp4.Hdlr{ // <hdlr/> _, err = w.WriteBox(&gomp4.Hdlr{ // <hdlr/>
HandlerType: [4]byte{'s', 'o', 'u', 'n'}, HandlerType: [4]byte{'s', 'o', 'u', 'n'},
Name: "SoundHandler", Name: "SoundHandler",
}) })
@ -326,31 +328,31 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
return err return err
} }
_, err = w.writeBoxStart(&mp4.Minf{}) // <minf> _, err = w.WriteBoxStart(&gomp4.Minf{}) // <minf>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Smhd{ // <smhd/> _, err = w.WriteBox(&gomp4.Smhd{ // <smhd/>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Dinf{}) // <dinf> _, err = w.WriteBoxStart(&gomp4.Dinf{}) // <dinf>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Dref{ // <dref> _, err = w.WriteBoxStart(&gomp4.Dref{ // <dref>
EntryCount: 1, EntryCount: 1,
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Url{ // <url/> _, err = w.WriteBox(&gomp4.Url{ // <url/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 1}, Flags: [3]byte{0, 0, 1},
}, },
}) })
@ -358,32 +360,32 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
return err return err
} }
err = w.writeBoxEnd() // </dref> err = w.WriteBoxEnd() // </dref>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </dinf> err = w.WriteBoxEnd() // </dinf>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Stbl{}) // <stbl> _, err = w.WriteBoxStart(&gomp4.Stbl{}) // <stbl>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.Stsd{ // <stsd> _, err = w.WriteBoxStart(&gomp4.Stsd{ // <stsd>
EntryCount: 1, EntryCount: 1,
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // <mp4a> _, err = w.WriteBoxStart(&gomp4.AudioSampleEntry{ // <mp4a>
SampleEntry: mp4.SampleEntry{ SampleEntry: gomp4.SampleEntry{
AnyTypeBox: mp4.AnyTypeBox{ AnyTypeBox: gomp4.AnyTypeBox{
Type: mp4.BoxTypeMp4a(), Type: gomp4.BoxTypeMp4a(),
}, },
DataReferenceIndex: 1, DataReferenceIndex: 1,
}, },
@ -406,14 +408,14 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
decSpecificInfoTagSize := uint8(len(conf)) decSpecificInfoTagSize := uint8(len(conf))
decSpecificInfoTag := append( decSpecificInfoTag := append(
[]byte{ []byte{
mp4.DecSpecificInfoTag, gomp4.DecSpecificInfoTag,
0x80, 0x80, 0x80, decSpecificInfoTagSize, // size 0x80, 0x80, 0x80, decSpecificInfoTagSize, // size
}, },
conf..., conf...,
) )
esDescrTag := []byte{ esDescrTag := []byte{
mp4.ESDescrTag, gomp4.ESDescrTag,
0x80, 0x80, 0x80, 32 + decSpecificInfoTagSize, // size 0x80, 0x80, 0x80, 32 + decSpecificInfoTagSize, // size
0x00, 0x00,
byte(trackID), // ES_ID byte(trackID), // ES_ID
@ -421,7 +423,7 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
} }
decoderConfigDescrTag := []byte{ decoderConfigDescrTag := []byte{
mp4.DecoderConfigDescrTag, gomp4.DecoderConfigDescrTag,
0x80, 0x80, 0x80, 18 + decSpecificInfoTagSize, // size 0x80, 0x80, 0x80, 18 + decSpecificInfoTagSize, // size
0x40, // object type indicator (MPEG-4 Audio) 0x40, // object type indicator (MPEG-4 Audio)
0x15, 0x00, 0x15, 0x00,
@ -431,7 +433,7 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
} }
slConfigDescrTag := []byte{ slConfigDescrTag := []byte{
mp4.SLConfigDescrTag, gomp4.SLConfigDescrTag,
0x80, 0x80, 0x80, 0x01, // size (1) 0x80, 0x80, 0x80, 0x01, // size (1)
0x02, 0x02,
} }
@ -444,14 +446,14 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
pos += copy(data[pos:], decSpecificInfoTag) pos += copy(data[pos:], decSpecificInfoTag)
copy(data[pos:], slConfigDescrTag) copy(data[pos:], slConfigDescrTag)
_, err = w.writeBox(&myEsds{ // <esds/> _, err = w.WriteBox(&myEsds{ // <esds/>
Data: data, Data: data,
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Btrt{ // <btrt/> _, err = w.WriteBox(&gomp4.Btrt{ // <btrt/>
MaxBitrate: 128825, MaxBitrate: 128825,
AvgBitrate: 128825, AvgBitrate: 128825,
}) })
@ -459,56 +461,56 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib.
return err return err
} }
err = w.writeBoxEnd() // </mp4a> err = w.WriteBoxEnd() // </mp4a>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </stsd> err = w.WriteBoxEnd() // </stsd>
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stts{ // <stts> _, err = w.WriteBox(&gomp4.Stts{ // <stts>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stsc{ // <stsc> _, err = w.WriteBox(&gomp4.Stsc{ // <stsc>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stsz{ // <stsz> _, err = w.WriteBox(&gomp4.Stsz{ // <stsz>
}) })
if err != nil { if err != nil {
return err return err
} }
_, err = w.writeBox(&mp4.Stco{ // <stco> _, err = w.WriteBox(&gomp4.Stco{ // <stco>
}) })
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </stbl> err = w.WriteBoxEnd() // </stbl>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </minf> err = w.WriteBoxEnd() // </minf>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </mdia> err = w.WriteBoxEnd() // </mdia>
if err != nil { if err != nil {
return err return err
} }
err = w.writeBoxEnd() // </trak> err = w.WriteBoxEnd() // </trak>
if err != nil { if err != nil {
return err return err
} }
@ -528,12 +530,12 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac
- trex (audio) - trex (audio)
*/ */
w := newMP4Writer() w := mp4.NewWriter()
_, err := w.writeBox(&mp4.Ftyp{ // <ftyp/> _, err := w.WriteBox(&gomp4.Ftyp{ // <ftyp/>
MajorBrand: [4]byte{'m', 'p', '4', '2'}, MajorBrand: [4]byte{'m', 'p', '4', '2'},
MinorVersion: 1, MinorVersion: 1,
CompatibleBrands: []mp4.CompatibleBrandElem{ CompatibleBrands: []gomp4.CompatibleBrandElem{
{CompatibleBrand: [4]byte{'m', 'p', '4', '1'}}, {CompatibleBrand: [4]byte{'m', 'p', '4', '1'}},
{CompatibleBrand: [4]byte{'m', 'p', '4', '2'}}, {CompatibleBrand: [4]byte{'m', 'p', '4', '2'}},
{CompatibleBrand: [4]byte{'i', 's', 'o', 'm'}}, {CompatibleBrand: [4]byte{'i', 's', 'o', 'm'}},
@ -544,12 +546,12 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac
return nil, err return nil, err
} }
_, err = w.writeBoxStart(&mp4.Moov{}) // <moov> _, err = w.WriteBoxStart(&gomp4.Moov{}) // <moov>
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = w.writeBox(&mp4.Mvhd{ // <mvhd/> _, err = w.WriteBox(&gomp4.Mvhd{ // <mvhd/>
Timescale: 1000, Timescale: 1000,
Rate: 65536, Rate: 65536,
Volume: 256, Volume: 256,
@ -578,7 +580,7 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac
} }
} }
_, err = w.writeBoxStart(&mp4.Mvex{}) // <mvex> _, err = w.WriteBoxStart(&gomp4.Mvex{}) // <mvex>
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -586,7 +588,7 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac
trackID = 1 trackID = 1
if videoTrack != nil { if videoTrack != nil {
_, err = w.writeBox(&mp4.Trex{ // <trex/> _, err = w.WriteBox(&gomp4.Trex{ // <trex/>
TrackID: uint32(trackID), TrackID: uint32(trackID),
DefaultSampleDescriptionIndex: 1, DefaultSampleDescriptionIndex: 1,
}) })
@ -598,7 +600,7 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac
} }
if audioTrack != nil { if audioTrack != nil {
_, err = w.writeBox(&mp4.Trex{ // <trex/> _, err = w.WriteBox(&gomp4.Trex{ // <trex/>
TrackID: uint32(trackID), TrackID: uint32(trackID),
DefaultSampleDescriptionIndex: 1, DefaultSampleDescriptionIndex: 1,
}) })
@ -607,15 +609,15 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac
} }
} }
err = w.writeBoxEnd() // </mvex> err = w.WriteBoxEnd() // </mvex>
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = w.writeBoxEnd() // </moov> err = w.WriteBoxEnd() // </moov>
if err != nil { if err != nil {
return nil, err return nil, err
} }
return w.bytes(), nil return w.Bytes(), nil
} }

74
internal/hls/muxer_variant_fmp4_part.go

@ -7,9 +7,11 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/abema/go-mp4" gomp4 "github.com/abema/go-mp4"
"github.com/aler9/gortsplib" "github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/aac" "github.com/aler9/gortsplib/pkg/aac"
"github.com/aler9/rtsp-simple-server/internal/mp4"
) )
func durationGoToMp4(v time.Duration, timescale time.Duration) int64 { func durationGoToMp4(v time.Duration, timescale time.Duration) int64 {
@ -17,10 +19,10 @@ func durationGoToMp4(v time.Duration, timescale time.Duration) int64 {
} }
func mp4PartGenerateVideoTraf( func mp4PartGenerateVideoTraf(
w *mp4Writer, w *mp4.Writer,
trackID int, trackID int,
videoSamples []*fmp4VideoSample, videoSamples []*fmp4VideoSample,
) (*mp4.Trun, int, error) { ) (*gomp4.Trun, int, error) {
/* /*
traf traf
- tfhd - tfhd
@ -28,15 +30,15 @@ func mp4PartGenerateVideoTraf(
- trun - trun
*/ */
_, err := w.writeBoxStart(&mp4.Traf{}) // <traf> _, err := w.WriteBoxStart(&gomp4.Traf{}) // <traf>
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
flags := 0 flags := 0
_, err = w.writeBox(&mp4.Tfhd{ // <tfhd/> _, err = w.WriteBox(&gomp4.Tfhd{ // <tfhd/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{2, byte(flags >> 8), byte(flags)}, Flags: [3]byte{2, byte(flags >> 8), byte(flags)},
}, },
TrackID: uint32(trackID), TrackID: uint32(trackID),
@ -45,8 +47,8 @@ func mp4PartGenerateVideoTraf(
return nil, 0, err return nil, 0, err
} }
_, err = w.writeBox(&mp4.Tfdt{ // <tfdt/> _, err = w.WriteBox(&gomp4.Tfdt{ // <tfdt/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Version: 1, Version: 1,
}, },
// sum of decode durations of all earlier samples // sum of decode durations of all earlier samples
@ -63,8 +65,8 @@ func mp4PartGenerateVideoTraf(
flags |= 0x400 // sample flags present flags |= 0x400 // sample flags present
flags |= 0x800 // sample composition time offset present or v1 flags |= 0x800 // sample composition time offset present or v1
trun := &mp4.Trun{ // <trun/> trun := &gomp4.Trun{ // <trun/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Version: 1, Version: 1,
Flags: [3]byte{0, byte(flags >> 8), byte(flags)}, Flags: [3]byte{0, byte(flags >> 8), byte(flags)},
}, },
@ -79,7 +81,7 @@ func mp4PartGenerateVideoTraf(
flags |= 1 << 16 // sample_is_non_sync_sample flags |= 1 << 16 // sample_is_non_sync_sample
} }
trun.Entries = append(trun.Entries, mp4.TrunEntry{ trun.Entries = append(trun.Entries, gomp4.TrunEntry{
SampleDuration: uint32(durationGoToMp4(e.duration(), fmp4VideoTimescale)), SampleDuration: uint32(durationGoToMp4(e.duration(), fmp4VideoTimescale)),
SampleSize: uint32(len(e.avcc)), SampleSize: uint32(len(e.avcc)),
SampleFlags: flags, SampleFlags: flags,
@ -87,12 +89,12 @@ func mp4PartGenerateVideoTraf(
}) })
} }
trunOffset, err := w.writeBox(trun) trunOffset, err := w.WriteBox(trun)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
err = w.writeBoxEnd() // </traf> err = w.WriteBoxEnd() // </traf>
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -101,11 +103,11 @@ func mp4PartGenerateVideoTraf(
} }
func mp4PartGenerateAudioTraf( func mp4PartGenerateAudioTraf(
w *mp4Writer, w *mp4.Writer,
trackID int, trackID int,
audioTrack *gortsplib.TrackAAC, audioTrack *gortsplib.TrackAAC,
audioSamples []*fmp4AudioSample, audioSamples []*fmp4AudioSample,
) (*mp4.Trun, int, error) { ) (*gomp4.Trun, int, error) {
/* /*
traf traf
- tfhd - tfhd
@ -117,15 +119,15 @@ func mp4PartGenerateAudioTraf(
return nil, 0, nil return nil, 0, nil
} }
_, err := w.writeBoxStart(&mp4.Traf{}) // <traf> _, err := w.WriteBoxStart(&gomp4.Traf{}) // <traf>
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
flags := 0 flags := 0
_, err = w.writeBox(&mp4.Tfhd{ // <tfhd/> _, err = w.WriteBox(&gomp4.Tfhd{ // <tfhd/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Flags: [3]byte{2, byte(flags >> 8), byte(flags)}, Flags: [3]byte{2, byte(flags >> 8), byte(flags)},
}, },
TrackID: uint32(trackID), TrackID: uint32(trackID),
@ -134,8 +136,8 @@ func mp4PartGenerateAudioTraf(
return nil, 0, err return nil, 0, err
} }
_, err = w.writeBox(&mp4.Tfdt{ // <tfdt/> _, err = w.WriteBox(&gomp4.Tfdt{ // <tfdt/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Version: 1, Version: 1,
}, },
// sum of decode durations of all earlier samples // sum of decode durations of all earlier samples
@ -150,8 +152,8 @@ func mp4PartGenerateAudioTraf(
flags |= 0x100 // sample duration present flags |= 0x100 // sample duration present
flags |= 0x200 // sample size present flags |= 0x200 // sample size present
trun := &mp4.Trun{ // <trun/> trun := &gomp4.Trun{ // <trun/>
FullBox: mp4.FullBox{ FullBox: gomp4.FullBox{
Version: 0, Version: 0,
Flags: [3]byte{0, byte(flags >> 8), byte(flags)}, Flags: [3]byte{0, byte(flags >> 8), byte(flags)},
}, },
@ -159,18 +161,18 @@ func mp4PartGenerateAudioTraf(
} }
for _, e := range audioSamples { for _, e := range audioSamples {
trun.Entries = append(trun.Entries, mp4.TrunEntry{ trun.Entries = append(trun.Entries, gomp4.TrunEntry{
SampleDuration: uint32(durationGoToMp4(e.duration(), time.Duration(audioTrack.ClockRate()))), SampleDuration: uint32(durationGoToMp4(e.duration(), time.Duration(audioTrack.ClockRate()))),
SampleSize: uint32(len(e.au)), SampleSize: uint32(len(e.au)),
}) })
} }
trunOffset, err := w.writeBox(trun) trunOffset, err := w.WriteBox(trun)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
err = w.writeBoxEnd() // </traf> err = w.WriteBoxEnd() // </traf>
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -192,14 +194,14 @@ func mp4PartGenerate(
mdat mdat
*/ */
w := newMP4Writer() w := mp4.NewWriter()
moofOffset, err := w.writeBoxStart(&mp4.Moof{}) // <moof> moofOffset, err := w.WriteBoxStart(&gomp4.Moof{}) // <moof>
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = w.writeBox(&mp4.Mfhd{ // <mfhd/> _, err = w.WriteBox(&gomp4.Mfhd{ // <mfhd/>
SequenceNumber: 0, SequenceNumber: 0,
}) })
if err != nil { if err != nil {
@ -208,7 +210,7 @@ func mp4PartGenerate(
trackID := 1 trackID := 1
var videoTrun *mp4.Trun var videoTrun *gomp4.Trun
var videoTrunOffset int var videoTrunOffset int
if videoTrack != nil { if videoTrack != nil {
var err error var err error
@ -221,7 +223,7 @@ func mp4PartGenerate(
trackID++ trackID++
} }
var audioTrun *mp4.Trun var audioTrun *gomp4.Trun
var audioTrunOffset int var audioTrunOffset int
if audioTrack != nil { if audioTrack != nil {
var err error var err error
@ -231,12 +233,12 @@ func mp4PartGenerate(
} }
} }
err = w.writeBoxEnd() // </moof> err = w.WriteBoxEnd() // </moof>
if err != nil { if err != nil {
return nil, err return nil, err
} }
mdat := &mp4.Mdat{} // <mdat/> mdat := &gomp4.Mdat{} // <mdat/>
dataSize := 0 dataSize := 0
videoDataSize := 0 videoDataSize := 0
@ -269,14 +271,14 @@ func mp4PartGenerate(
} }
} }
mdatOffset, err := w.writeBox(mdat) mdatOffset, err := w.WriteBox(mdat)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if videoTrack != nil { if videoTrack != nil {
videoTrun.DataOffset = int32(mdatOffset - moofOffset + 8) videoTrun.DataOffset = int32(mdatOffset - moofOffset + 8)
err = w.rewriteBox(videoTrunOffset, videoTrun) err = w.RewriteBox(videoTrunOffset, videoTrun)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -284,13 +286,13 @@ func mp4PartGenerate(
if audioTrack != nil && audioTrun != nil { if audioTrack != nil && audioTrun != nil {
audioTrun.DataOffset = int32(videoDataSize + mdatOffset - moofOffset + 8) audioTrun.DataOffset = int32(videoDataSize + mdatOffset - moofOffset + 8)
err = w.rewriteBox(audioTrunOffset, audioTrun) err = w.RewriteBox(audioTrunOffset, audioTrun)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
return w.bytes(), nil return w.Bytes(), nil
} }
func fmp4PartName(id uint64) string { func fmp4PartName(id uint64) string {

100
internal/mp4/writer.go

@ -0,0 +1,100 @@
package mp4
import (
"io"
gomp4 "github.com/abema/go-mp4"
"github.com/orcaman/writerseeker"
)
// Writer is a MP4 writer.
type Writer struct {
buf *writerseeker.WriterSeeker
w *gomp4.Writer
}
// NewWriter allocates a Writer.
func NewWriter() *Writer {
w := &Writer{
buf: &writerseeker.WriterSeeker{},
}
w.w = gomp4.NewWriter(w.buf)
return w
}
// WriteBoxStart writes a box start.
func (w *Writer) WriteBoxStart(box gomp4.IImmutableBox) (int, error) {
bi := &gomp4.BoxInfo{
Type: box.GetType(),
}
var err error
bi, err = w.w.StartBox(bi)
if err != nil {
return 0, err
}
_, err = gomp4.Marshal(w.w, box, gomp4.Context{})
if err != nil {
return 0, err
}
return int(bi.Offset), nil
}
// WriteBoxEnd writes a box end.
func (w *Writer) WriteBoxEnd() error {
_, err := w.w.EndBox()
return err
}
// WriteBox writes a self-closing box.
func (w *Writer) WriteBox(box gomp4.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
}
// RewriteBox rewrites a box.
func (w *Writer) RewriteBox(off int, box gomp4.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
}
// Bytes returns the MP4 content.
func (w *Writer) Bytes() []byte {
return w.buf.Bytes()
}
Loading…
Cancel
Save