From ab1064dfc16f9d7bccc6be547f3854457d310787 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sat, 21 Sep 2024 23:53:04 +0200 Subject: [PATCH] muxer: support multiple audio tracks (bluenviron/mediamtx#2728) --- muxer.go | 158 ++++++++++++++++++++++++++++----------------- muxer_segmenter.go | 28 ++++---- muxer_stream.go | 39 ++++++++--- muxer_test.go | 109 ++++++++++++++++--------------- 4 files changed, 195 insertions(+), 139 deletions(-) diff --git a/muxer.go b/muxer.go index cd4fea1..91d89e1 100644 --- a/muxer.go +++ b/muxer.go @@ -29,6 +29,14 @@ func (w *switchableWriter) Write(p []byte) (int, error) { return w.w.Write(p) } +func isVideo(codec codecs.Codec) bool { + switch codec.(type) { + case *codecs.AV1, *codecs.VP9, *codecs.H265, *codecs.H264: + return true + } + return false +} + // a prefix is needed to prevent usage of cached segments // from previous muxing sessions. func generatePrefix() (string, error) { @@ -81,12 +89,10 @@ type fmp4AugmentedSample struct { // Muxer is a HLS muxer. type Muxer struct { // - // parameters (all optional except VideoTrack or AudioTrack). + // parameters (all optional except Tracks). // - // video track. - VideoTrack *Track - // audio track. - AudioTrack *Track + // tracks. + Tracks []*Track // Variant to use. // It defaults to MuxerVariantLowLatency Variant MuxerVariant @@ -152,21 +158,44 @@ func (m *Muxer) Start() error { m.SegmentMaxSize = 50 * 1024 * 1024 } - if m.VideoTrack == nil && m.AudioTrack == nil { - return fmt.Errorf("one between VideoTrack and AudioTrack is required") + if len(m.Tracks) == 0 { + return fmt.Errorf("at least one track must be provided") } + hasVideo := false + hasAudio := false + if m.Variant == MuxerVariantMPEGTS { - if m.VideoTrack != nil { - if _, ok := m.VideoTrack.Codec.(*codecs.H264); !ok { - return fmt.Errorf( - "the MPEG-TS variant of HLS supports H264 video only") + for _, track := range m.Tracks { + if isVideo(track.Codec) { + if hasVideo { + return fmt.Errorf("the MPEG-TS variant of HLS supports a single video track only") + } + if _, ok := track.Codec.(*codecs.H264); !ok { + return fmt.Errorf( + "the MPEG-TS variant of HLS supports H264 video only") + } + hasVideo = true + } else { + if hasAudio { + return fmt.Errorf("the MPEG-TS variant of HLS supports a single audio track only") + } + if _, ok := track.Codec.(*codecs.MPEG4Audio); !ok { + return fmt.Errorf( + "the MPEG-TS variant of HLS supports MPEG-4 Audio only") + } + hasAudio = true } } - if m.AudioTrack != nil { - if _, ok := m.AudioTrack.Codec.(*codecs.MPEG4Audio); !ok { - return fmt.Errorf( - "the MPEG-TS variant of HLS supports MPEG-4 Audio only") + } else { + for _, track := range m.Tracks { + if isVideo(track.Codec) { + if hasVideo { + return fmt.Errorf("only one video track is currently supported") + } + hasVideo = true + } else { + hasAudio = true } } } @@ -196,26 +225,15 @@ func (m *Muxer) Start() error { } m.server.initialize() - if m.VideoTrack != nil { - track := &muxerTrack{ - Track: m.VideoTrack, - variant: m.Variant, - isLeading: true, - } - track.initialize() - m.mtracks = append(m.mtracks, track) - m.mtracksByTrack[m.VideoTrack] = track - } - - if m.AudioTrack != nil { - track := &muxerTrack{ - Track: m.AudioTrack, + for i, track := range m.Tracks { + mtrack := &muxerTrack{ + Track: track, variant: m.Variant, - isLeading: m.VideoTrack == nil, + isLeading: isVideo(track.Codec) || (!hasVideo && i == 0), } - track.initialize() - m.mtracks = append(m.mtracks, track) - m.mtracksByTrack[m.AudioTrack] = track + mtrack.initialize() + m.mtracks = append(m.mtracks, mtrack) + m.mtracksByTrack[track] = mtrack } if m.Variant == MuxerVariantMPEGTS { @@ -230,33 +248,45 @@ func (m *Muxer) Start() error { switch { case m.Variant == MuxerVariantMPEGTS: stream := &muxerStream{ - muxer: m, - tracks: m.mtracks, - id: "main", + muxer: m, + tracks: m.mtracks, + id: "main", + isLeading: true, } stream.initialize() m.streams = append(m.streams, stream) default: - if m.VideoTrack != nil { - videoStream := &muxerStream{ - muxer: m, - tracks: []*muxerTrack{m.mtracksByTrack[m.VideoTrack]}, - id: "video", + defaultRenditionChosen := false + + for i, track := range m.mtracks { + var id string + if isVideo(track.Codec) { + id = "video" + strconv.FormatInt(int64(i+1), 10) + } else { + id = "audio" + strconv.FormatInt(int64(i+1), 10) } - videoStream.initialize() - m.streams = append(m.streams, videoStream) - } - if m.AudioTrack != nil { - audioStream := &muxerStream{ - muxer: m, - tracks: []*muxerTrack{m.mtracksByTrack[m.AudioTrack]}, - id: "audio", - isRendition: m.VideoTrack != nil, + isRendition := !track.isLeading || !isVideo(track.Codec) + + var isDefaultRendition bool + if isRendition && !defaultRenditionChosen { + isDefaultRendition = true + defaultRenditionChosen = true + } else { + isDefaultRendition = false } - audioStream.initialize() - m.streams = append(m.streams, audioStream) + + stream := &muxerStream{ + muxer: m, + tracks: []*muxerTrack{track}, + id: id, + isLeading: track.isLeading, + isRendition: isRendition, + isDefaultRendition: isDefaultRendition, + } + stream.initialize() + m.streams = append(m.streams, stream) } } @@ -290,52 +320,62 @@ func (m *Muxer) Close() { // WriteAV1 writes an AV1 temporal unit. func (m *Muxer) WriteAV1( + track *Track, ntp time.Time, pts time.Duration, tu [][]byte, ) error { - return m.segmenter.writeAV1(ntp, pts, tu) + return m.segmenter.writeAV1(m.mtracksByTrack[track], ntp, pts, tu) } // WriteVP9 writes a VP9 frame. func (m *Muxer) WriteVP9( + track *Track, ntp time.Time, pts time.Duration, frame []byte, ) error { - return m.segmenter.writeVP9(ntp, pts, frame) + return m.segmenter.writeVP9(m.mtracksByTrack[track], ntp, pts, frame) } // WriteH265 writes an H265 access unit. func (m *Muxer) WriteH265( + track *Track, ntp time.Time, pts time.Duration, au [][]byte, ) error { - return m.segmenter.writeH265(ntp, pts, au) + return m.segmenter.writeH265(m.mtracksByTrack[track], ntp, pts, au) } // WriteH264 writes an H264 access unit. func (m *Muxer) WriteH264( + track *Track, ntp time.Time, pts time.Duration, au [][]byte, ) error { - return m.segmenter.writeH264(ntp, pts, au) + return m.segmenter.writeH264(m.mtracksByTrack[track], ntp, pts, au) } // WriteOpus writes Opus packets. func (m *Muxer) WriteOpus( + track *Track, ntp time.Time, pts time.Duration, packets [][]byte, ) error { - return m.segmenter.writeOpus(ntp, pts, packets) + return m.segmenter.writeOpus(m.mtracksByTrack[track], ntp, pts, packets) } // WriteMPEG4Audio writes MPEG-4 Audio access units. -func (m *Muxer) WriteMPEG4Audio(ntp time.Time, pts time.Duration, aus [][]byte) error { - return m.segmenter.writeMPEG4Audio(ntp, pts, aus) +func (m *Muxer) WriteMPEG4Audio( + track *Track, + ntp time.Time, + pts time.Duration, + aus [][]byte, +) error { + return m.segmenter.writeMPEG4Audio(m.mtracksByTrack[track], ntp, pts, aus) } // Handle handles a HTTP request. diff --git a/muxer_segmenter.go b/muxer_segmenter.go index f5ff671..a5bbad6 100644 --- a/muxer_segmenter.go +++ b/muxer_segmenter.go @@ -67,12 +67,11 @@ func (s *muxerSegmenter) initialize() { } func (s *muxerSegmenter) writeAV1( + track *muxerTrack, ntp time.Time, pts time.Duration, tu [][]byte, ) error { - track := s.muxer.mtracksByTrack[s.muxer.VideoTrack] - codec := track.Codec.(*codecs.AV1) randomAccess := false @@ -118,12 +117,11 @@ func (s *muxerSegmenter) writeAV1( } func (s *muxerSegmenter) writeVP9( + track *muxerTrack, ntp time.Time, pts time.Duration, frame []byte, ) error { - track := s.muxer.mtracksByTrack[s.muxer.VideoTrack] - var h vp9.Header err := h.Unmarshal(frame) if err != nil { @@ -191,12 +189,11 @@ func (s *muxerSegmenter) writeVP9( } func (s *muxerSegmenter) writeH265( + track *muxerTrack, ntp time.Time, pts time.Duration, au [][]byte, ) error { - track := s.muxer.mtracksByTrack[s.muxer.VideoTrack] - randomAccess := false codec := track.Codec.(*codecs.H265) @@ -268,12 +265,11 @@ func (s *muxerSegmenter) writeH265( } func (s *muxerSegmenter) writeH264( + track *muxerTrack, ntp time.Time, pts time.Duration, au [][]byte, ) error { - track := s.muxer.mtracksByTrack[s.muxer.VideoTrack] - randomAccess := false codec := track.Codec.(*codecs.H264) nonIDRPresent := false @@ -379,12 +375,11 @@ func (s *muxerSegmenter) writeH264( } func (s *muxerSegmenter) writeOpus( + track *muxerTrack, ntp time.Time, pts time.Duration, packets [][]byte, ) error { - track := s.muxer.mtracksByTrack[s.muxer.AudioTrack] - for _, packet := range packets { err := s.fmp4WriteSample( track, @@ -410,11 +405,14 @@ func (s *muxerSegmenter) writeOpus( return nil } -func (s *muxerSegmenter) writeMPEG4Audio(ntp time.Time, pts time.Duration, aus [][]byte) error { - track := s.muxer.mtracksByTrack[s.muxer.AudioTrack] - +func (s *muxerSegmenter) writeMPEG4Audio( + track *muxerTrack, + ntp time.Time, + pts time.Duration, + aus [][]byte, +) error { if s.muxer.Variant == MuxerVariantMPEGTS { - if s.muxer.VideoTrack == nil { + if track.isLeading { if track.stream.nextSegment == nil { err := s.muxer.createFirstSegment(pts, ntp) if err != nil { @@ -441,7 +439,7 @@ func (s *muxerSegmenter) writeMPEG4Audio(ntp time.Time, pts time.Duration, aus [ return nil } else { - sampleRate := time.Duration(s.muxer.AudioTrack.Codec.(*codecs.MPEG4Audio).Config.SampleRate) + sampleRate := time.Duration(track.Codec.(*codecs.MPEG4Audio).Config.SampleRate) for i, au := range aus { auNTP := ntp.Add(time.Duration(i) * mpeg4audio.SamplesPerAccessUnit * diff --git a/muxer_stream.go b/muxer_stream.go index f7fe670..05e6987 100644 --- a/muxer_stream.go +++ b/muxer_stream.go @@ -33,16 +33,27 @@ func filterOutHLSParams(rawQuery string) string { return rawQuery } +func containsCodec(cs []string, c string) bool { + for _, c0 := range cs { + if c0 == c { + return true + } + } + return false +} + type generateMediaPlaylistFunc func( isDeltaUpdate bool, rawQuery string, ) ([]byte, error) type muxerStream struct { - muxer *Muxer // TODO: remove - tracks []*muxerTrack - id string - isRendition bool + muxer *Muxer // TODO: remove + tracks []*muxerTrack + id string + isLeading bool + isRendition bool + isDefaultRendition bool generateMediaPlaylist generateMediaPlaylistFunc @@ -97,7 +108,10 @@ func (s *muxerStream) populateMultivariantPlaylist( mv := pl.Variants[0] for _, track := range s.tracks { - mv.Codecs = append(mv.Codecs, codecparams.Marshal(track.Codec)) + codec := codecparams.Marshal(track.Codec) + if !containsCodec(mv.Codecs, codec) { + mv.Codecs = append(mv.Codecs, codec) + } switch codec := track.Codec.(type) { case *codecs.AV1: @@ -151,15 +165,20 @@ func (s *muxerStream) populateMultivariantPlaylist( uri += "?" + rawQuery } - if !s.isRendition { + if s.isLeading { mv.URI = uri - } else { + } + + if s.isRendition { mv.Audio = "audio" r := &playlist.MultivariantRendition{ - Type: playlist.MultivariantRenditionTypeAudio, - GroupID: "audio", - URI: &uri, + Type: playlist.MultivariantRenditionTypeAudio, + GroupID: "audio", + Name: s.id, + Default: s.isDefaultRendition, + Autoselect: true, + URI: &uri, } pl.Renditions = append(pl.Renditions, r) } diff --git a/muxer_test.go b/muxer_test.go index ed9b5c8..f398099 100644 --- a/muxer_test.go +++ b/muxer_test.go @@ -137,14 +137,14 @@ func TestMuxer(t *testing.T) { case "video+audio": // access unit without IDR d := 1 * time.Second - err = m.WriteH264(testTime.Add(d-1*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-1*time.Second), d, [][]byte{ {1}, // non-IDR }) require.NoError(t, err) // access unit with IDR d = 2 * time.Second - err = m.WriteH264(testTime.Add(d-1*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-1*time.Second), d, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR @@ -152,40 +152,40 @@ func TestMuxer(t *testing.T) { require.NoError(t, err) d = 3 * time.Second - err = m.WriteMPEG4Audio(testTime.Add(d-1*time.Second), d, [][]byte{{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime.Add(d-1*time.Second), d, [][]byte{{ 0x01, 0x02, 0x03, 0x04, }}) require.NoError(t, err) d = 3500 * time.Millisecond - err = m.WriteMPEG4Audio(testTime.Add(d-1*time.Second), d, [][]byte{{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime.Add(d-1*time.Second), d, [][]byte{{ 0x01, 0x02, 0x03, 0x04, }}) require.NoError(t, err) // access unit without IDR d = 4 * time.Second - err = m.WriteH264(testTime.Add(d-1*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-1*time.Second), d, [][]byte{ {1}, // non-IDR }) require.NoError(t, err) d = 4500 * time.Millisecond - err = m.WriteMPEG4Audio(testTime.Add(d-1*time.Second), d, [][]byte{{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime.Add(d-1*time.Second), d, [][]byte{{ 0x01, 0x02, 0x03, 0x04, }}) require.NoError(t, err) // access unit with IDR d = 6 * time.Second - err = m.WriteH264(testTime.Add(d-1*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-1*time.Second), d, [][]byte{ {5}, // IDR }) require.NoError(t, err) // access unit with IDR d = 7 * time.Second - err = m.WriteH264(testTime.Add(d-1*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-1*time.Second), d, [][]byte{ {5}, // IDR }) require.NoError(t, err) @@ -193,7 +193,7 @@ func TestMuxer(t *testing.T) { case "video-only": // access unit with IDR d := 2 * time.Second - err = m.WriteH264(testTime.Add(d-2*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-2*time.Second), d, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR @@ -202,14 +202,14 @@ func TestMuxer(t *testing.T) { // access unit with IDR d = 6 * time.Second - err = m.WriteH264(testTime.Add(d-2*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-2*time.Second), d, [][]byte{ {5}, // IDR }) require.NoError(t, err) // access unit with IDR d = 7 * time.Second - err = m.WriteH264(testTime.Add(d-2*time.Second), d, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime.Add(d-2*time.Second), d, [][]byte{ {5}, // IDR }) require.NoError(t, err) @@ -224,13 +224,13 @@ func TestMuxer(t *testing.T) { } d := 2 * time.Second - err = m.WriteMPEG4Audio(testTime.Add(d-1*time.Second), d, [][]byte{{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime.Add(d-1*time.Second), d, [][]byte{{ 0x01, 0x02, 0x03, 0x04, }}) require.NoError(t, err) d = 3 * time.Second - err = m.WriteMPEG4Audio(testTime.Add(d-1*time.Second), d, [][]byte{{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime.Add(d-1*time.Second), d, [][]byte{{ 0x01, 0x02, 0x03, 0x04, }}) require.NoError(t, err) @@ -597,9 +597,9 @@ func TestMuxerCloseBeforeData(t *testing.T) { Variant: MuxerVariantFMP4, SegmentCount: 3, SegmentMinDuration: 1 * time.Second, - VideoTrack: &Track{ + Tracks: []*Track{{ Codec: &codecs.AV1{}, - }, + }}, } err := m.Start() @@ -623,14 +623,14 @@ func TestMuxerMaxSegmentSize(t *testing.T) { SegmentCount: 3, SegmentMinDuration: 1 * time.Second, SegmentMaxSize: 1, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() require.NoError(t, err) defer m.Close() - err = m.WriteH264(testTime, 2*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 2*time.Second, [][]byte{ testSPS, {5}, // IDR }) @@ -642,21 +642,21 @@ func TestMuxerDoubleRead(t *testing.T) { Variant: MuxerVariantMPEGTS, SegmentCount: 3, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() require.NoError(t, err) defer m.Close() - err = m.WriteH264(testTime, 0, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 0, [][]byte{ testSPS, {5}, // IDR {1}, }) require.NoError(t, err) - err = m.WriteH264(testTime, 2*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 2*time.Second, [][]byte{ {5}, // IDR {2}, }) @@ -705,27 +705,27 @@ func TestMuxerSaveToDisk(t *testing.T) { Variant: v, SegmentCount: 3, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, Directory: dir, } err = m.Start() require.NoError(t, err) - err = m.WriteH264(testTime, 0, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 0, [][]byte{ testSPS, {5}, // IDR {1}, }) require.NoError(t, err) - err = m.WriteH264(testTime, 2*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 2*time.Second, [][]byte{ {5}, // IDR {2}, }) require.NoError(t, err) - err = m.WriteH264(testTime, 3*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 3*time.Second, [][]byte{ {5}, // IDR {2}, }) @@ -796,27 +796,27 @@ func TestMuxerDynamicParams(t *testing.T) { Variant: MuxerVariantFMP4, SegmentCount: 3, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() require.NoError(t, err) defer m.Close() - err = m.WriteH264(testTime, 0, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 0, [][]byte{ testSPS, {5}, // IDR {1}, }) require.NoError(t, err) - err = m.WriteH264(testTime, 1*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 1*time.Second, [][]byte{ {5}, // IDR {2}, }) require.NoError(t, err) - err = m.WriteH264(testTime, 2*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 2*time.Second, [][]byte{ {5}, // IDR {2}, }) @@ -866,14 +866,14 @@ func TestMuxerDynamicParams(t *testing.T) { 0xcb, } - err = m.WriteH264(testTime, 3*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 3*time.Second, [][]byte{ testSPS2, {0x65, 0x88, 0x84, 0x00, 0x33, 0xff}, // IDR {2}, }) require.NoError(t, err) - err = m.WriteH264(testTime, 5*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 5*time.Second, [][]byte{ {0x65, 0x88, 0x84, 0x00, 0x33, 0xff}, // IDR }) require.NoError(t, err) @@ -920,21 +920,21 @@ func TestMuxerFMP4ZeroDuration(t *testing.T) { Variant: MuxerVariantFMP4, SegmentCount: 3, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() require.NoError(t, err) defer m.Close() - err = m.WriteH264(time.Now(), 0, [][]byte{ + err = m.WriteH264(testVideoTrack, time.Now(), 0, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR }) require.NoError(t, err) - err = m.WriteH264(time.Now(), 1*time.Nanosecond, [][]byte{ + err = m.WriteH264(testVideoTrack, time.Now(), 1*time.Nanosecond, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR @@ -947,65 +947,64 @@ func TestMuxerFMP4NegativeTimestamp(t *testing.T) { Variant: MuxerVariantFMP4, SegmentCount: 3, SegmentMinDuration: 2 * time.Second, - VideoTrack: testVideoTrack, - AudioTrack: testAudioTrack, + Tracks: []*Track{testVideoTrack, testAudioTrack}, } err := m.Start() require.NoError(t, err) defer m.Close() - err = m.WriteMPEG4Audio(testTime, -9*time.Second, [][]byte{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime, -9*time.Second, [][]byte{ {1, 2, 3, 4}, }) require.NoError(t, err) // this is skipped - err = m.WriteH264(testTime, -11*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, -11*time.Second, [][]byte{ testSPS, {5}, // IDR {1}, }) require.NoError(t, err) - err = m.WriteH264(testTime, -9*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, -9*time.Second, [][]byte{ testSPS, {5}, // IDR {1}, }) require.NoError(t, err) - err = m.WriteH264(testTime, -8*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, -8*time.Second, [][]byte{ {5}, // IDR {2}, }) require.NoError(t, err) // this is skipped - err = m.WriteMPEG4Audio(testTime, -11*time.Second, [][]byte{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime, -11*time.Second, [][]byte{ {1, 2, 3, 4}, }) require.NoError(t, err) - err = m.WriteMPEG4Audio(testTime, -8*time.Second, [][]byte{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime, -8*time.Second, [][]byte{ {5, 6, 7, 8}, }) require.NoError(t, err) - err = m.WriteMPEG4Audio(testTime, -7*time.Second, [][]byte{ + err = m.WriteMPEG4Audio(testAudioTrack, testTime, -7*time.Second, [][]byte{ {9, 10, 11, 12}, }) require.NoError(t, err) // switch segment - err = m.WriteH264(testTime, -7*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, -7*time.Second, [][]byte{ {5}, // IDR {3}, }) require.NoError(t, err) // switch segment - err = m.WriteH264(testTime, -5*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, -5*time.Second, [][]byte{ {5}, // IDR {3}, }) @@ -1125,14 +1124,14 @@ func TestMuxerFMP4SequenceNumber(t *testing.T) { Variant: MuxerVariantLowLatency, SegmentCount: 7, SegmentMinDuration: 2 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() require.NoError(t, err) defer m.Close() - err = m.WriteH264(testTime, 0, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 0, [][]byte{ testSPS, {5}, // IDR {1}, @@ -1140,13 +1139,13 @@ func TestMuxerFMP4SequenceNumber(t *testing.T) { require.NoError(t, err) for i := 0; i < 3; i++ { - err = m.WriteH264(testTime, (1+time.Duration(i))*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, (1+time.Duration(i))*time.Second, [][]byte{ {1}, // non IDR }) require.NoError(t, err) } - err = m.WriteH264(testTime, 4*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 4*time.Second, [][]byte{ {5}, // IDR }) require.NoError(t, err) @@ -1227,7 +1226,7 @@ func TestMuxerInvalidFolder(t *testing.T) { Variant: v, SegmentCount: 7, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, Directory: "/nonexisting", } @@ -1236,7 +1235,7 @@ func TestMuxerInvalidFolder(t *testing.T) { defer m.Close() for i := 0; i < 2; i++ { - err := m.WriteH264(testTime, time.Duration(i)*time.Second, [][]byte{ + err := m.WriteH264(testVideoTrack, testTime, time.Duration(i)*time.Second, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR @@ -1257,7 +1256,7 @@ func TestMuxerExpiredSegment(t *testing.T) { Variant: MuxerVariantLowLatency, SegmentCount: 7, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() @@ -1265,7 +1264,7 @@ func TestMuxerExpiredSegment(t *testing.T) { defer m.Close() for i := 0; i < 2; i++ { - err := m.WriteH264(testTime, time.Duration(i)*time.Second, [][]byte{ + err := m.WriteH264(testVideoTrack, testTime, time.Duration(i)*time.Second, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR @@ -1307,7 +1306,7 @@ func TestMuxerPreloadHint(t *testing.T) { Variant: MuxerVariantLowLatency, SegmentCount: 7, SegmentMinDuration: 1 * time.Second, - VideoTrack: testVideoTrack, + Tracks: []*Track{testVideoTrack}, } err := m.Start() @@ -1315,7 +1314,7 @@ func TestMuxerPreloadHint(t *testing.T) { defer m.Close() for i := 0; i < 2; i++ { - err := m.WriteH264(testTime, time.Duration(i)*time.Second, [][]byte{ + err := m.WriteH264(testVideoTrack, testTime, time.Duration(i)*time.Second, [][]byte{ testSPS, // SPS {8}, // PPS {5}, // IDR @@ -1383,7 +1382,7 @@ func TestMuxerPreloadHint(t *testing.T) { case <-time.After(500 * time.Millisecond): } - err = m.WriteH264(testTime, 3*time.Second, [][]byte{ + err = m.WriteH264(testVideoTrack, testTime, 3*time.Second, [][]byte{ {5}, // IDR }) require.NoError(t, err)