move mp4 writer into dedicated folder

This commit is contained in:
aler9 2022-06-04 16:51:57 +02:00
parent cd93b70612
commit 750743d1ed
4 changed files with 232 additions and 221 deletions

View File

@ -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()
}

View File

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

View File

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

100
internal/mp4/writer.go Normal file
View File

@ -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()
}