diff --git a/internal/hls/mp4writer.go b/internal/hls/mp4writer.go deleted file mode 100644 index f964739b..00000000 --- a/internal/hls/mp4writer.go +++ /dev/null @@ -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() -} diff --git a/internal/hls/muxer_variant_fmp4_init.go b/internal/hls/muxer_variant_fmp4_init.go index c276ad01..b2c67705 100644 --- a/internal/hls/muxer_variant_fmp4_init.go +++ b/internal/hls/muxer_variant_fmp4_init.go @@ -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{}) // + _, err := w.WriteBoxStart(&gomp4.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{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{}) // + _, err = w.WriteBoxStart(&gomp4.Mdia{}) // if err != nil { return err } - _, err = w.writeBox(&mp4.Mdhd{ // + _, err = w.WriteBox(&gomp4.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{ // + _, err = w.WriteBox(&gomp4.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{}) // + _, err = w.WriteBoxStart(&gomp4.Minf{}) // if err != nil { return err } - _, err = w.writeBox(&mp4.Vmhd{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{}) // + _, err = w.WriteBoxStart(&gomp4.Dinf{}) // if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Dref{ // + _, err = w.WriteBoxStart(&gomp4.Dref{ // EntryCount: 1, }) if err != nil { return err } - _, err = w.writeBox(&mp4.Url{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Stbl{}) // + _, err = w.WriteBoxStart(&gomp4.Stbl{}) // if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Stsd{ // + _, err = w.WriteBoxStart(&gomp4.Stsd{ // EntryCount: 1, }) if err != nil { return err } - _, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // - SampleEntry: mp4.SampleEntry{ - AnyTypeBox: mp4.AnyTypeBox{ - Type: mp4.BoxTypeAvc1(), + _, err = w.WriteBoxStart(&gomp4.VisualSampleEntry{ // + 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{ // - AnyTypeBox: mp4.AnyTypeBox{ - Type: mp4.BoxTypeAvcC(), + _, err = w.WriteBox(&gomp4.AVCDecoderConfiguration{ // + 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{ // + _, err = w.WriteBox(&gomp4.Btrt{ // MaxBitrate: 1000000, AvgBitrate: 1000000, }) @@ -207,56 +209,56 @@ func mp4InitGenerateVideoTrack(w *mp4Writer, trackID int, videoTrack *gortsplib. return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - _, err = w.writeBox(&mp4.Stts{ // + _, err = w.WriteBox(&gomp4.Stts{ // }) if err != nil { return err } - _, err = w.writeBox(&mp4.Stsc{ // + _, err = w.WriteBox(&gomp4.Stsc{ // }) if err != nil { return err } - _, err = w.writeBox(&mp4.Stsz{ // + _, err = w.WriteBox(&gomp4.Stsz{ // }) if err != nil { return err } - _, err = w.writeBox(&mp4.Stco{ // + _, err = w.WriteBox(&gomp4.Stco{ // }) if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // 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{}) // + _, err := w.WriteBoxStart(&gomp4.Trak{}) // if err != nil { return err } - _, err = w.writeBox(&mp4.Tkhd{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{}) // + _, err = w.WriteBoxStart(&gomp4.Mdia{}) // if err != nil { return err } - _, err = w.writeBox(&mp4.Mdhd{ // + _, err = w.WriteBox(&gomp4.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{ // + _, err = w.WriteBox(&gomp4.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{}) // + _, err = w.WriteBoxStart(&gomp4.Minf{}) // if err != nil { return err } - _, err = w.writeBox(&mp4.Smhd{ // + _, err = w.WriteBox(&gomp4.Smhd{ // }) if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Dinf{}) // + _, err = w.WriteBoxStart(&gomp4.Dinf{}) // if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Dref{ // + _, err = w.WriteBoxStart(&gomp4.Dref{ // EntryCount: 1, }) if err != nil { return err } - _, err = w.writeBox(&mp4.Url{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Stbl{}) // + _, err = w.WriteBoxStart(&gomp4.Stbl{}) // if err != nil { return err } - _, err = w.writeBoxStart(&mp4.Stsd{ // + _, err = w.WriteBoxStart(&gomp4.Stsd{ // EntryCount: 1, }) if err != nil { return err } - _, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // - SampleEntry: mp4.SampleEntry{ - AnyTypeBox: mp4.AnyTypeBox{ - Type: mp4.BoxTypeMp4a(), + _, err = w.WriteBoxStart(&gomp4.AudioSampleEntry{ // + 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{ // + _, err = w.WriteBox(&myEsds{ // Data: data, }) if err != nil { return err } - _, err = w.writeBox(&mp4.Btrt{ // + _, err = w.WriteBox(&gomp4.Btrt{ // MaxBitrate: 128825, AvgBitrate: 128825, }) @@ -459,56 +461,56 @@ func mp4InitGenerateAudioTrack(w *mp4Writer, trackID int, audioTrack *gortsplib. return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - _, err = w.writeBox(&mp4.Stts{ // + _, err = w.WriteBox(&gomp4.Stts{ // }) if err != nil { return err } - _, err = w.writeBox(&mp4.Stsc{ // + _, err = w.WriteBox(&gomp4.Stsc{ // }) if err != nil { return err } - _, err = w.writeBox(&mp4.Stsz{ // + _, err = w.WriteBox(&gomp4.Stsz{ // }) if err != nil { return err } - _, err = w.writeBox(&mp4.Stco{ // + _, err = w.WriteBox(&gomp4.Stco{ // }) if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // 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{ // + _, err := w.WriteBox(&gomp4.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{}) // + _, err = w.WriteBoxStart(&gomp4.Moov{}) // if err != nil { return nil, err } - _, err = w.writeBox(&mp4.Mvhd{ // + _, err = w.WriteBox(&gomp4.Mvhd{ // Timescale: 1000, Rate: 65536, Volume: 256, @@ -578,7 +580,7 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac } } - _, err = w.writeBoxStart(&mp4.Mvex{}) // + _, err = w.WriteBoxStart(&gomp4.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{ // + _, err = w.WriteBox(&gomp4.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{ // + _, err = w.WriteBox(&gomp4.Trex{ // TrackID: uint32(trackID), DefaultSampleDescriptionIndex: 1, }) @@ -607,15 +609,15 @@ func mp4InitGenerate(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.Trac } } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return nil, err } - err = w.writeBoxEnd() // + err = w.WriteBoxEnd() // if err != nil { return nil, err } - return w.bytes(), nil + return w.Bytes(), nil } diff --git a/internal/hls/muxer_variant_fmp4_part.go b/internal/hls/muxer_variant_fmp4_part.go index 950c807d..d5c25cee 100644 --- a/internal/hls/muxer_variant_fmp4_part.go +++ b/internal/hls/muxer_variant_fmp4_part.go @@ -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{}) // + _, err := w.WriteBoxStart(&gomp4.Traf{}) // if err != nil { return nil, 0, err } flags := 0 - _, err = w.writeBox(&mp4.Tfhd{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{ // - FullBox: mp4.FullBox{ + trun := &gomp4.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() // + err = w.WriteBoxEnd() // 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{}) // + _, err := w.WriteBoxStart(&gomp4.Traf{}) // if err != nil { return nil, 0, err } flags := 0 - _, err = w.writeBox(&mp4.Tfhd{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{ // - FullBox: mp4.FullBox{ + _, err = w.WriteBox(&gomp4.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{ // - FullBox: mp4.FullBox{ + trun := &gomp4.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() // + err = w.WriteBoxEnd() // 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{}) // + moofOffset, err := w.WriteBoxStart(&gomp4.Moof{}) // if err != nil { return nil, err } - _, err = w.writeBox(&mp4.Mfhd{ // + _, err = w.WriteBox(&gomp4.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() // + err = w.WriteBoxEnd() // if err != nil { return nil, err } - mdat := &mp4.Mdat{} // + mdat := &gomp4.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 { diff --git a/internal/mp4/writer.go b/internal/mp4/writer.go new file mode 100644 index 00000000..906d78d1 --- /dev/null +++ b/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() +}