Browse Source

update dependencies (#2113)

pull/2117/head
Alessandro Ros 2 years ago committed by GitHub
parent
commit
e4bd1b35a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      README.md
  2. 6
      go.mod
  3. 12
      go.sum
  4. 24
      internal/core/hls_muxer.go
  5. 20
      internal/core/hls_source.go
  6. 43
      internal/core/mpegts_buffered_reader.go
  7. 364
      internal/core/udp_source.go
  8. 22
      internal/formatprocessor/opus.go

12
README.md

@ -639,15 +639,23 @@ videotestsrc ! video/x-raw,width=1280,height=720 ! x264enc speed-preset=ultrafas
audiotestsrc ! audioconvert ! avenc_aac ! mux. audiotestsrc ! audioconvert ! avenc_aac ! mux.
``` ```
or FFmpeg:
```
ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \
-pix_fmt yuv420p -c:v libx264 -preset ultrafast -b:v 600k \
-f mpegts udp://238.0.0.1:1234?pkt_size=1316
```
Edit `mediamtx.yml` and replace everything inside section `paths` with the following content: Edit `mediamtx.yml` and replace everything inside section `paths` with the following content:
```yml ```yml
paths: paths:
udp: mypath:
source: udp://238.0.0.1:1234 source: udp://238.0.0.1:1234
``` ```
The resulting stream will be available in path `/udp`. The resulting stream will be available in path `/mypath`.
## Read from the server ## Read from the server

6
go.mod

@ -7,9 +7,9 @@ require (
github.com/abema/go-mp4 v0.11.0 github.com/abema/go-mp4 v0.11.0
github.com/alecthomas/kong v0.8.0 github.com/alecthomas/kong v0.8.0
github.com/asticode/go-astits v1.11.0 github.com/asticode/go-astits v1.11.0
github.com/bluenviron/gohlslib v0.2.7 github.com/bluenviron/gohlslib v0.3.0
github.com/bluenviron/gortsplib/v3 v3.8.2 github.com/bluenviron/gortsplib/v3 v3.9.0
github.com/bluenviron/mediacommon v0.6.0 github.com/bluenviron/mediacommon v0.7.0
github.com/fsnotify/fsnotify v1.6.0 github.com/fsnotify/fsnotify v1.6.0
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0

12
go.sum

@ -10,12 +10,12 @@ github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflx
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astits v1.11.0 h1:GTHUXht0ZXAJXsVbsLIcyfHr1Bchi4QQwMARw2ZWAng= github.com/asticode/go-astits v1.11.0 h1:GTHUXht0ZXAJXsVbsLIcyfHr1Bchi4QQwMARw2ZWAng=
github.com/asticode/go-astits v1.11.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= github.com/asticode/go-astits v1.11.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
github.com/bluenviron/gohlslib v0.2.7 h1:wV4ky3PbozwWORWd9pURktMnPIT7j/mBK3ivVljgVkk= github.com/bluenviron/gohlslib v0.3.0 h1:ze8cCKszGC2LAWp0B+qIXZIlCZocB7a3BKeBo9E8Sr0=
github.com/bluenviron/gohlslib v0.2.7/go.mod h1:x5XU60uvx91bQkRI8lrKNEltUtudR0WzaONBOhH+nzQ= github.com/bluenviron/gohlslib v0.3.0/go.mod h1:aO69Vu0mMUxWrLmgS6g/S3Y3sfAhyg2SXaMEL7yNlWc=
github.com/bluenviron/gortsplib/v3 v3.8.2 h1:KSZNOeWkOgotISiJIb38MRIAZGA3gNNN/fqVEwlLg/U= github.com/bluenviron/gortsplib/v3 v3.9.0 h1:aAHV6MhsDtgBF6yKaNBBCdvtSpLB8ne4kyUfLQlN7nM=
github.com/bluenviron/gortsplib/v3 v3.8.2/go.mod h1:hGrlEGPJlMbXonsnHtvSYJBxyYHboxjAULEEr/5Z//Y= github.com/bluenviron/gortsplib/v3 v3.9.0/go.mod h1:5h3Zu7jkzwDknYrf+89q2saab//oioKgM9mgvBEX3pg=
github.com/bluenviron/mediacommon v0.6.0 h1:suWFWHL9WL+sfBQPmleCd5jCY0iEtuKgvPRUaBGoq+g= github.com/bluenviron/mediacommon v0.7.0 h1:dJWLLL9oDbAqfK8KuNfnDUQwNbeMAtGeRjZc9Vo95js=
github.com/bluenviron/mediacommon v0.6.0/go.mod h1:wuLJdxcITiSPgY1MvQqrX+qPlKmNfeV9wNvXth5M98I= github.com/bluenviron/mediacommon v0.7.0/go.mod h1:wuLJdxcITiSPgY1MvQqrX+qPlKmNfeV9wNvXth5M98I=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=

24
internal/core/hls_muxer.go

@ -16,7 +16,6 @@ import (
"github.com/bluenviron/gortsplib/v3/pkg/formats" "github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media" "github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/ringbuffer" "github.com/bluenviron/gortsplib/v3/pkg/ringbuffer"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/conf"
@ -442,15 +441,12 @@ func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Tra
} }
pts := tunit.PTS - audioStartPTS pts := tunit.PTS - audioStartPTS
for i, au := range tunit.AUs { err := m.muxer.WriteMPEG4Audio(
err := m.muxer.WriteAudio( tunit.NTP,
tunit.NTP, pts,
pts+time.Duration(i)*mpeg4audio.SamplesPerAccessUnit* tunit.AUs)
time.Second/time.Duration(audioFormatMPEG4AudioGeneric.ClockRate()), if err != nil {
au) return fmt.Errorf("muxer error: %v", err)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
}
} }
return nil return nil
@ -488,10 +484,10 @@ func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Tra
} }
pts := tunit.PTS - audioStartPTS pts := tunit.PTS - audioStartPTS
err := m.muxer.WriteAudio( err := m.muxer.WriteMPEG4Audio(
tunit.NTP, tunit.NTP,
pts, pts,
tunit.AU) [][]byte{tunit.AU})
if err != nil { if err != nil {
return fmt.Errorf("muxer error: %v", err) return fmt.Errorf("muxer error: %v", err)
} }
@ -524,10 +520,10 @@ func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Tra
} }
pts := tunit.PTS - audioStartPTS pts := tunit.PTS - audioStartPTS
err := m.muxer.WriteAudio( err := m.muxer.WriteOpus(
tunit.NTP, tunit.NTP,
pts, pts,
tunit.Frame) tunit.Packets)
if err != nil { if err != nil {
return fmt.Errorf("muxer error: %v", err) return fmt.Errorf("muxer error: %v", err)
} }

20
internal/core/hls_source.go

@ -77,10 +77,10 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
}}, }},
} }
c.OnData(track, func(pts time.Duration, unit interface{}) { c.OnDataH26x(track, func(pts time.Duration, dts time.Duration, au [][]byte) {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{ stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{
PTS: pts, PTS: pts,
AU: unit.([][]byte), AU: au,
NTP: time.Now(), NTP: time.Now(),
}) })
}) })
@ -96,10 +96,10 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
}}, }},
} }
c.OnData(track, func(pts time.Duration, unit interface{}) { c.OnDataH26x(track, func(pts time.Duration, dts time.Duration, au [][]byte) {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH265{ stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH265{
PTS: pts, PTS: pts,
AU: unit.([][]byte), AU: au,
NTP: time.Now(), NTP: time.Now(),
}) })
}) })
@ -116,10 +116,10 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
}}, }},
} }
c.OnData(track, func(pts time.Duration, unit interface{}) { c.OnDataMPEG4Audio(track, func(pts time.Duration, dts time.Duration, aus [][]byte) {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitMPEG4AudioGeneric{ stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitMPEG4AudioGeneric{
PTS: pts, PTS: pts,
AUs: [][]byte{unit.([]byte)}, AUs: aus,
NTP: time.Now(), NTP: time.Now(),
}) })
}) })
@ -133,11 +133,11 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
}}, }},
} }
c.OnData(track, func(pts time.Duration, unit interface{}) { c.OnDataOpus(track, func(pts time.Duration, dts time.Duration, packets [][]byte) {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitOpus{ stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitOpus{
PTS: pts, PTS: pts,
Frame: unit.([]byte), Packets: packets,
NTP: time.Now(), NTP: time.Now(),
}) })
}) })
} }

43
internal/core/mpegts_buffered_reader.go

@ -0,0 +1,43 @@
package core
import (
"fmt"
"io"
)
// mpegtsBufferedReader is a buffered reader optimized for MPEG-TS.
type mpegtsBufferedReader struct {
r io.Reader
midbuf []byte
midbufpos int
}
func newMPEGTSBufferedReader(r io.Reader) *mpegtsBufferedReader {
return &mpegtsBufferedReader{
r: r,
midbuf: make([]byte, 0, 1500),
}
}
// Read implements io.Reader.
func (r *mpegtsBufferedReader) Read(p []byte) (int, error) {
if r.midbufpos < len(r.midbuf) {
n := copy(p, r.midbuf[r.midbufpos:])
r.midbufpos += n
return n, nil
}
mn, err := r.r.Read(r.midbuf[:cap(r.midbuf)])
if err != nil {
return 0, err
}
if (mn % 188) != 0 {
return 0, fmt.Errorf("received packet with size %d not multiple of 188", mn)
}
r.midbuf = r.midbuf[:mn]
n := copy(p, r.midbuf)
r.midbufpos = n
return n, nil
}

364
internal/core/udp_source.go

@ -6,11 +6,8 @@ import (
"net" "net"
"time" "time"
"github.com/asticode/go-astits"
"github.com/bluenviron/gortsplib/v3/pkg/formats" "github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media" "github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts" "github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
@ -24,43 +21,6 @@ const (
udpMTU = 1472 udpMTU = 1472
) )
var opusDurations = [32]int{
480, 960, 1920, 2880, /* Silk NB */
480, 960, 1920, 2880, /* Silk MB */
480, 960, 1920, 2880, /* Silk WB */
480, 960, /* Hybrid SWB */
480, 960, /* Hybrid FB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
}
func opusGetPacketDuration(pkt []byte) time.Duration {
if len(pkt) == 0 {
return 0
}
frameDuration := opusDurations[pkt[0]>>3]
frameCount := 0
switch pkt[0] & 3 {
case 0:
frameCount = 1
case 1:
frameCount = 2
case 2:
frameCount = 2
case 3:
if len(pkt) < 2 {
return 0
}
frameCount = int(pkt[1] & 63)
}
return (time.Duration(frameDuration) * time.Duration(frameCount) * time.Millisecond) / 48
}
func joinMulticastGroupOnAtLeastOneInterface(p *ipv4.PacketConn, listenIP net.IP) error { func joinMulticastGroupOnAtLeastOneInterface(p *ipv4.PacketConn, listenIP net.IP) error {
intfs, err := net.Interfaces() intfs, err := net.Interfaces()
if err != nil { if err != nil {
@ -86,38 +46,18 @@ func joinMulticastGroupOnAtLeastOneInterface(p *ipv4.PacketConn, listenIP net.IP
} }
type packetConnReader struct { type packetConnReader struct {
pc net.PacketConn pc net.PacketConn
midbuf []byte
midbufpos int
} }
func newPacketConnReader(pc net.PacketConn) *packetConnReader { func newPacketConnReader(pc net.PacketConn) *packetConnReader {
return &packetConnReader{ return &packetConnReader{
pc: pc, pc: pc,
midbuf: make([]byte, 0, 1500),
} }
} }
func (r *packetConnReader) Read(p []byte) (int, error) { func (r *packetConnReader) Read(p []byte) (int, error) {
if r.midbufpos < len(r.midbuf) { n, _, err := r.pc.ReadFrom(p)
n := copy(p, r.midbuf[r.midbufpos:]) return n, err
r.midbufpos += n
return n, nil
}
mn, _, err := r.pc.ReadFrom(r.midbuf[:cap(r.midbuf)])
if err != nil {
return 0, err
}
if (mn % 188) != 0 {
return 0, fmt.Errorf("received packet with size %d not multiple of 188", mn)
}
r.midbuf = r.midbuf[:mn]
n := copy(p, r.midbuf)
r.midbufpos = n
return n, nil
} }
type udpSourceParent interface { type udpSourceParent interface {
@ -174,203 +114,143 @@ func (s *udpSource) run(ctx context.Context, cnf *conf.PathConf, _ chan *conf.Pa
} }
} }
dem := astits.NewDemuxer(
context.Background(),
newPacketConnReader(pc),
astits.DemuxerOptPacketSize(188))
readerErr := make(chan error) readerErr := make(chan error)
go func() { go func() {
readerErr <- func() error { readerErr <- s.runReader(pc)
pc.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout))) }()
tracks, err := mpegts.FindTracks(dem)
if err != nil { select {
return err case err := <-readerErr:
} return err
case <-ctx.Done():
pc.Close()
<-readerErr
return fmt.Errorf("terminated")
}
}
var medias media.Medias func (s *udpSource) runReader(pc net.PacketConn) error {
mediaCallbacks := make(map[uint16]func(time.Duration, []byte), len(tracks)) pc.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
var stream *stream r, err := mpegts.NewReader(newMPEGTSBufferedReader(newPacketConnReader(pc)))
if err != nil {
for _, track := range tracks { return err
var medi *media.Media }
switch tcodec := track.Codec.(type) { var medias media.Medias
case *mpegts.CodecH264: var stream *stream
medi = &media.Media{
Type: media.TypeVideo, var td *mpegts.TimeDecoder
Formats: []formats.Format{&formats.H264{ decodeTime := func(t int64) time.Duration {
PayloadTyp: 96, if td == nil {
PacketizationMode: 1, td = mpegts.NewTimeDecoder(t)
}}, }
} return td.Decode(t)
}
mediaCallbacks[track.ES.ElementaryPID] = func(pts time.Duration, data []byte) {
au, err := h264.AnnexBUnmarshal(data) for _, track := range r.Tracks() {
if err != nil { var medi *media.Media
s.Log(logger.Warn, "%v", err)
return switch tcodec := track.Codec.(type) {
} case *mpegts.CodecH264:
medi = &media.Media{
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{ Type: media.TypeVideo,
PTS: pts, Formats: []formats.Format{&formats.H264{
AU: au, PayloadTyp: 96,
NTP: time.Now(), PacketizationMode: 1,
}) }},
}
case *mpegts.CodecH265:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H265{
PayloadTyp: 96,
}},
}
mediaCallbacks[track.ES.ElementaryPID] = func(pts time.Duration, data []byte) {
au, err := h264.AnnexBUnmarshal(data)
if err != nil {
s.Log(logger.Warn, "%v", err)
return
}
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH265{
PTS: pts,
AU: au,
NTP: time.Now(),
})
}
case *mpegts.CodecMPEG4Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
PayloadTyp: 96,
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
Config: &tcodec.Config,
}},
}
mediaCallbacks[track.ES.ElementaryPID] = func(pts time.Duration, data []byte) {
var pkts mpeg4audio.ADTSPackets
err := pkts.Unmarshal(data)
if err != nil {
s.Log(logger.Warn, "%v", err)
return
}
aus := make([][]byte, len(pkts))
for i, pkt := range pkts {
aus[i] = pkt.AU
}
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitMPEG4AudioGeneric{
PTS: pts,
AUs: aus,
NTP: time.Now(),
})
}
case *mpegts.CodecOpus:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.Opus{
PayloadTyp: 96,
IsStereo: (tcodec.Channels == 2),
}},
}
mediaCallbacks[track.ES.ElementaryPID] = func(pts time.Duration, data []byte) {
pos := 0
for {
var au mpegts.OpusAccessUnit
n, err := au.Unmarshal(data[pos:])
if err != nil {
s.Log(logger.Warn, "%v", err)
return
}
pos += n
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitOpus{
PTS: pts,
Frame: au.Frame,
NTP: time.Now(),
})
if len(data[pos:]) == 0 {
break
}
pts += opusGetPacketDuration(au.Frame)
}
}
}
medias = append(medias, medi)
} }
res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{ r.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) error {
medias: medias, stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{
generateRTPPackets: true, PTS: decodeTime(pts),
AU: au,
NTP: time.Now(),
})
return nil
}) })
if res.err != nil {
return res.err case *mpegts.CodecH265:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H265{
PayloadTyp: 96,
}},
} }
defer s.parent.sourceStaticImplSetNotReady(pathSourceStaticSetNotReadyReq{}) r.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) error {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH265{
PTS: decodeTime(pts),
AU: au,
NTP: time.Now(),
})
return nil
})
s.Log(logger.Info, "ready: %s", sourceMediaInfo(medias)) case *mpegts.CodecMPEG4Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
PayloadTyp: 96,
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
Config: &tcodec.Config,
}},
}
stream = res.stream r.OnDataMPEG4Audio(track, func(pts int64, _ int64, aus [][]byte) error {
var timedec *mpegts.TimeDecoder stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitMPEG4AudioGeneric{
PTS: decodeTime(pts),
AUs: aus,
NTP: time.Now(),
})
return nil
})
for { case *mpegts.CodecOpus:
pc.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout))) medi = &media.Media{
data, err := dem.NextData() Type: media.TypeAudio,
if err != nil { Formats: []formats.Format{&formats.Opus{
return err PayloadTyp: 96,
} IsStereo: (tcodec.ChannelCount == 2),
}},
}
if data.PES == nil { r.OnDataOpus(track, func(pts int64, _ int64, packets [][]byte) error {
continue stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitOpus{
} PTS: decodeTime(pts),
Packets: packets,
NTP: time.Now(),
})
return nil
})
}
if data.PES.Header.OptionalHeader == nil || medias = append(medias, medi)
data.PES.Header.OptionalHeader.PTSDTSIndicator == astits.PTSDTSIndicatorNoPTSOrDTS || }
data.PES.Header.OptionalHeader.PTSDTSIndicator == astits.PTSDTSIndicatorIsForbidden {
return fmt.Errorf("PTS is missing")
}
var pts time.Duration res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{
if timedec == nil { medias: medias,
timedec = mpegts.NewTimeDecoder(data.PES.Header.OptionalHeader.PTS.Base) generateRTPPackets: true,
pts = 0 })
} else { if res.err != nil {
pts = timedec.Decode(data.PES.Header.OptionalHeader.PTS.Base) return res.err
} }
cb, ok := mediaCallbacks[data.PID] defer s.parent.sourceStaticImplSetNotReady(pathSourceStaticSetNotReadyReq{})
if !ok {
continue
}
cb(pts, data.PES.Data) s.Log(logger.Info, "ready: %s", sourceMediaInfo(medias))
}
}()
}()
select { stream = res.stream
case err := <-readerErr:
return err
case <-ctx.Done(): for {
pc.Close() pc.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
<-readerErr err := r.Read()
return fmt.Errorf("terminated") if err != nil {
return err
}
} }
} }

22
internal/formatprocessor/opus.go

@ -6,6 +6,7 @@ import (
"github.com/bluenviron/gortsplib/v3/pkg/formats" "github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpsimpleaudio" "github.com/bluenviron/gortsplib/v3/pkg/formats/rtpsimpleaudio"
"github.com/bluenviron/mediacommon/pkg/codecs/opus"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
@ -16,7 +17,7 @@ type UnitOpus struct {
RTPPackets []*rtp.Packet RTPPackets []*rtp.Packet
NTP time.Time NTP time.Time
PTS time.Duration PTS time.Duration
Frame []byte Packets [][]byte
} }
// GetRTPPackets implements Unit. // GetRTPPackets implements Unit.
@ -91,12 +92,12 @@ func (t *formatProcessorOpus) Process(unit Unit, hasNonRTSPReaders bool) error {
} }
} }
frame, pts, err := t.decoder.Decode(pkt) packet, pts, err := t.decoder.Decode(pkt)
if err != nil { if err != nil {
return err return err
} }
tunit.Frame = frame tunit.Packets = [][]byte{packet}
tunit.PTS = pts tunit.PTS = pts
} }
@ -105,11 +106,18 @@ func (t *formatProcessorOpus) Process(unit Unit, hasNonRTSPReaders bool) error {
} }
// encode into RTP // encode into RTP
pkt, err := t.encoder.Encode(tunit.Frame, tunit.PTS) var rtpPackets []*rtp.Packet //nolint:prealloc
if err != nil { pts := tunit.PTS
return err for _, packet := range tunit.Packets {
pkt, err := t.encoder.Encode(packet, pts)
if err != nil {
return err
}
rtpPackets = append(rtpPackets, pkt)
pts += opus.PacketDuration(packet)
} }
tunit.RTPPackets = []*rtp.Packet{pkt} tunit.RTPPackets = rtpPackets
return nil return nil
} }

Loading…
Cancel
Save