Browse Source

move stream in a dedicated package (#2121)

needed by #2068
pull/2122/head
Alessandro Ros 3 years ago committed by GitHub
parent
commit
db3862cf0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      internal/core/hls_muxer.go
  2. 11
      internal/core/hls_source.go
  3. 17
      internal/core/path.go
  4. 5
      internal/core/rpicamera_source.go
  5. 41
      internal/core/rtmp_conn.go
  6. 2
      internal/core/rtsp_conn.go
  7. 7
      internal/core/rtsp_session.go
  8. 2
      internal/core/rtsp_source.go
  9. 11
      internal/core/udp_source.go
  10. 5
      internal/core/webrtc_incoming_track.go
  11. 5
      internal/core/webrtc_outgoing_track.go
  12. 4
      internal/core/webrtc_session.go
  13. 44
      internal/stream/stream.go
  14. 18
      internal/stream/stream_format.go
  15. 6
      internal/stream/stream_media.go

27
internal/core/hls_muxer.go

@ -21,6 +21,7 @@ import (
"github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/formatprocessor" "github.com/bluenviron/mediamtx/internal/formatprocessor"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
) )
const ( const (
@ -267,7 +268,7 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
medias = append(medias, audioMedia) medias = append(medias, audioMedia)
} }
defer res.stream.readerRemove(m) defer res.stream.RemoveReader(m)
if medias == nil { if medias == nil {
return fmt.Errorf( return fmt.Errorf(
@ -334,15 +335,15 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
} }
} }
func (m *hlsMuxer) createVideoTrack(stream *stream) (*media.Media, *gohlslib.Track) { func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohlslib.Track) {
var videoFormatH265 *formats.H265 var videoFormatH265 *formats.H265
videoMedia := stream.medias().FindFormat(&videoFormatH265) videoMedia := stream.Medias().FindFormat(&videoFormatH265)
if videoFormatH265 != nil { if videoFormatH265 != nil {
videoStartPTSFilled := false videoStartPTSFilled := false
var videoStartPTS time.Duration var videoStartPTS time.Duration
stream.readerAdd(m, videoMedia, videoFormatH265, func(unit formatprocessor.Unit) { stream.AddReader(m, videoMedia, videoFormatH265, func(unit formatprocessor.Unit) {
m.ringBuffer.Push(func() error { m.ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitH265) tunit := unit.(*formatprocessor.UnitH265)
@ -377,13 +378,13 @@ func (m *hlsMuxer) createVideoTrack(stream *stream) (*media.Media, *gohlslib.Tra
} }
var videoFormatH264 *formats.H264 var videoFormatH264 *formats.H264
videoMedia = stream.medias().FindFormat(&videoFormatH264) videoMedia = stream.Medias().FindFormat(&videoFormatH264)
if videoFormatH264 != nil { if videoFormatH264 != nil {
videoStartPTSFilled := false videoStartPTSFilled := false
var videoStartPTS time.Duration var videoStartPTS time.Duration
stream.readerAdd(m, videoMedia, videoFormatH264, func(unit formatprocessor.Unit) { stream.AddReader(m, videoMedia, videoFormatH264, func(unit formatprocessor.Unit) {
m.ringBuffer.Push(func() error { m.ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitH264) tunit := unit.(*formatprocessor.UnitH264)
@ -419,15 +420,15 @@ func (m *hlsMuxer) createVideoTrack(stream *stream) (*media.Media, *gohlslib.Tra
return nil, nil return nil, nil
} }
func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Track) { func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*media.Media, *gohlslib.Track) {
var audioFormatMPEG4AudioGeneric *formats.MPEG4AudioGeneric var audioFormatMPEG4AudioGeneric *formats.MPEG4AudioGeneric
audioMedia := stream.medias().FindFormat(&audioFormatMPEG4AudioGeneric) audioMedia := stream.Medias().FindFormat(&audioFormatMPEG4AudioGeneric)
if audioMedia != nil { if audioMedia != nil {
audioStartPTSFilled := false audioStartPTSFilled := false
var audioStartPTS time.Duration var audioStartPTS time.Duration
stream.readerAdd(m, audioMedia, audioFormatMPEG4AudioGeneric, func(unit formatprocessor.Unit) { stream.AddReader(m, audioMedia, audioFormatMPEG4AudioGeneric, func(unit formatprocessor.Unit) {
m.ringBuffer.Push(func() error { m.ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitMPEG4AudioGeneric) tunit := unit.(*formatprocessor.UnitMPEG4AudioGeneric)
@ -461,7 +462,7 @@ func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Tra
} }
var audioFormatMPEG4AudioLATM *formats.MPEG4AudioLATM var audioFormatMPEG4AudioLATM *formats.MPEG4AudioLATM
audioMedia = stream.medias().FindFormat(&audioFormatMPEG4AudioLATM) audioMedia = stream.Medias().FindFormat(&audioFormatMPEG4AudioLATM)
if audioMedia != nil && if audioMedia != nil &&
audioFormatMPEG4AudioLATM.Config != nil && audioFormatMPEG4AudioLATM.Config != nil &&
@ -470,7 +471,7 @@ func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Tra
audioStartPTSFilled := false audioStartPTSFilled := false
var audioStartPTS time.Duration var audioStartPTS time.Duration
stream.readerAdd(m, audioMedia, audioFormatMPEG4AudioLATM, func(unit formatprocessor.Unit) { stream.AddReader(m, audioMedia, audioFormatMPEG4AudioLATM, func(unit formatprocessor.Unit) {
m.ringBuffer.Push(func() error { m.ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitMPEG4AudioLATM) tunit := unit.(*formatprocessor.UnitMPEG4AudioLATM)
@ -504,13 +505,13 @@ func (m *hlsMuxer) createAudioTrack(stream *stream) (*media.Media, *gohlslib.Tra
} }
var audioFormatOpus *formats.Opus var audioFormatOpus *formats.Opus
audioMedia = stream.medias().FindFormat(&audioFormatOpus) audioMedia = stream.Medias().FindFormat(&audioFormatOpus)
if audioMedia != nil { if audioMedia != nil {
audioStartPTSFilled := false audioStartPTSFilled := false
var audioStartPTS time.Duration var audioStartPTS time.Duration
stream.readerAdd(m, audioMedia, audioFormatOpus, func(unit formatprocessor.Unit) { stream.AddReader(m, audioMedia, audioFormatOpus, func(unit formatprocessor.Unit) {
m.ringBuffer.Push(func() error { m.ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitOpus) tunit := unit.(*formatprocessor.UnitOpus)

11
internal/core/hls_source.go

@ -13,6 +13,7 @@ import (
"github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/formatprocessor" "github.com/bluenviron/mediamtx/internal/formatprocessor"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
) )
type hlsSourceParent interface { type hlsSourceParent interface {
@ -39,7 +40,7 @@ func (s *hlsSource) Log(level logger.Level, format string, args ...interface{})
// run implements sourceStaticImpl. // run implements sourceStaticImpl.
func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan *conf.PathConf) error { func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan *conf.PathConf) error {
var stream *stream var stream *stream.Stream
defer func() { defer func() {
if stream != nil { if stream != nil {
@ -78,7 +79,7 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
} }
c.OnDataH26x(track, func(pts time.Duration, dts time.Duration, au [][]byte) { 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: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -97,7 +98,7 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
} }
c.OnDataH26x(track, func(pts time.Duration, dts time.Duration, au [][]byte) { 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: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -117,7 +118,7 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
} }
c.OnDataMPEG4Audio(track, func(pts time.Duration, dts time.Duration, aus [][]byte) { 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: aus, AUs: aus,
NTP: time.Now(), NTP: time.Now(),
@ -134,7 +135,7 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
} }
c.OnDataOpus(track, func(pts time.Duration, dts time.Duration, packets [][]byte) { 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,
Packets: packets, Packets: packets,
NTP: time.Now(), NTP: time.Now(),

17
internal/core/path.go

@ -16,6 +16,7 @@ import (
"github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/externalcmd" "github.com/bluenviron/mediamtx/internal/externalcmd"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
) )
func newEmptyTimer() *time.Timer { func newEmptyTimer() *time.Timer {
@ -50,7 +51,7 @@ const (
) )
type pathSourceStaticSetReadyRes struct { type pathSourceStaticSetReadyRes struct {
stream *stream stream *stream.Stream
err error err error
} }
@ -88,7 +89,7 @@ type pathGetConfForPathReq struct {
type pathDescribeRes struct { type pathDescribeRes struct {
path *path path *path
stream *stream stream *stream.Stream
redirect string redirect string
err error err error
} }
@ -102,7 +103,7 @@ type pathDescribeReq struct {
type pathReaderSetupPlayRes struct { type pathReaderSetupPlayRes struct {
path *path path *path
stream *stream stream *stream.Stream
err error err error
} }
@ -128,7 +129,7 @@ type pathPublisherAddReq struct {
} }
type pathPublisherRecordRes struct { type pathPublisherRecordRes struct {
stream *stream stream *stream.Stream
err error err error
} }
@ -187,7 +188,7 @@ type path struct {
ctxCancel func() ctxCancel func()
confMutex sync.RWMutex confMutex sync.RWMutex
source source source source
stream *stream stream *stream.Stream
readyTime time.Time readyTime time.Time
bytesReceived *uint64 bytesReceived *uint64
readers map[reader]struct{} readers map[reader]struct{}
@ -619,7 +620,7 @@ func (pa *path) onDemandPublisherStop() {
} }
func (pa *path) setReady(medias media.Medias, allocateEncoder bool) error { func (pa *path) setReady(medias media.Medias, allocateEncoder bool) error {
stream, err := newStream( stream, err := stream.New(
pa.udpMaxPayloadSize, pa.udpMaxPayloadSize,
medias, medias,
allocateEncoder, allocateEncoder,
@ -665,7 +666,7 @@ func (pa *path) setNotReady() {
} }
if pa.stream != nil { if pa.stream != nil {
pa.stream.close() pa.stream.Close()
pa.stream = nil pa.stream = nil
} }
} }
@ -897,7 +898,7 @@ func (pa *path) handleAPIPathsGet(req pathAPIPathsGetReq) {
if pa.stream == nil { if pa.stream == nil {
return []string{} return []string{}
} }
return mediasDescription(pa.stream.medias()) return mediasDescription(pa.stream.Medias())
}(), }(),
BytesReceived: atomic.LoadUint64(pa.bytesReceived), BytesReceived: atomic.LoadUint64(pa.bytesReceived),
Readers: func() []interface{} { Readers: func() []interface{} {

5
internal/core/rpicamera_source.go

@ -11,6 +11,7 @@ import (
"github.com/bluenviron/mediamtx/internal/formatprocessor" "github.com/bluenviron/mediamtx/internal/formatprocessor"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/rpicamera" "github.com/bluenviron/mediamtx/internal/rpicamera"
"github.com/bluenviron/mediamtx/internal/stream"
) )
func paramsFromConf(cnf *conf.PathConf) rpicamera.Params { func paramsFromConf(cnf *conf.PathConf) rpicamera.Params {
@ -82,7 +83,7 @@ func (s *rpiCameraSource) run(ctx context.Context, cnf *conf.PathConf, reloadCon
}}, }},
} }
medias := media.Medias{medi} medias := media.Medias{medi}
var stream *stream var stream *stream.Stream
onData := func(dts time.Duration, au [][]byte) { onData := func(dts time.Duration, au [][]byte) {
if stream == nil { if stream == nil {
@ -98,7 +99,7 @@ func (s *rpiCameraSource) run(ctx context.Context, cnf *conf.PathConf, reloadCon
stream = res.stream stream = res.stream
} }
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{ stream.WriteUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{
PTS: dts, PTS: dts,
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),

41
internal/core/rtmp_conn.go

@ -27,6 +27,7 @@ import (
"github.com/bluenviron/mediamtx/internal/rtmp" "github.com/bluenviron/mediamtx/internal/rtmp"
"github.com/bluenviron/mediamtx/internal/rtmp/h264conf" "github.com/bluenviron/mediamtx/internal/rtmp/h264conf"
"github.com/bluenviron/mediamtx/internal/rtmp/message" "github.com/bluenviron/mediamtx/internal/rtmp/message"
"github.com/bluenviron/mediamtx/internal/stream"
) )
const ( const (
@ -43,7 +44,7 @@ func pathNameAndQuery(inURL *url.URL) (string, url.Values, string) {
type rtmpWriteFunc func(msg interface{}) error type rtmpWriteFunc func(msg interface{}) error
func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream) rtmpWriteFunc { func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream.Stream) rtmpWriteFunc {
switch format.(type) { switch format.(type) {
case *formats.H264: case *formats.H264:
return func(msg interface{}) error { return func(msg interface{}) error {
@ -62,7 +63,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
conf.PPS, conf.PPS,
} }
stream.writeUnit(medi, format, &formatprocessor.UnitH264{ stream.WriteUnit(medi, format, &formatprocessor.UnitH264{
PTS: tmsg.DTS + tmsg.PTSDelta, PTS: tmsg.DTS + tmsg.PTSDelta,
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -74,7 +75,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
return fmt.Errorf("unable to decode AVCC: %v", err) return fmt.Errorf("unable to decode AVCC: %v", err)
} }
stream.writeUnit(medi, format, &formatprocessor.UnitH264{ stream.WriteUnit(medi, format, &formatprocessor.UnitH264{
PTS: tmsg.DTS + tmsg.PTSDelta, PTS: tmsg.DTS + tmsg.PTSDelta,
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -93,7 +94,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
return fmt.Errorf("unable to decode AVCC: %v", err) return fmt.Errorf("unable to decode AVCC: %v", err)
} }
stream.writeUnit(medi, format, &formatprocessor.UnitH265{ stream.WriteUnit(medi, format, &formatprocessor.UnitH265{
PTS: tmsg.DTS + tmsg.PTSDelta, PTS: tmsg.DTS + tmsg.PTSDelta,
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -105,7 +106,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
return fmt.Errorf("unable to decode AVCC: %v", err) return fmt.Errorf("unable to decode AVCC: %v", err)
} }
stream.writeUnit(medi, format, &formatprocessor.UnitH265{ stream.WriteUnit(medi, format, &formatprocessor.UnitH265{
PTS: tmsg.DTS, PTS: tmsg.DTS,
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -117,7 +118,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
return fmt.Errorf("unable to decode AVCC: %v", err) return fmt.Errorf("unable to decode AVCC: %v", err)
} }
stream.writeUnit(medi, format, &formatprocessor.UnitH265{ stream.WriteUnit(medi, format, &formatprocessor.UnitH265{
PTS: tmsg.DTS + tmsg.PTSDelta, PTS: tmsg.DTS + tmsg.PTSDelta,
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -135,7 +136,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
return fmt.Errorf("unable to decode bitstream: %v", err) return fmt.Errorf("unable to decode bitstream: %v", err)
} }
stream.writeUnit(medi, format, &formatprocessor.UnitAV1{ stream.WriteUnit(medi, format, &formatprocessor.UnitAV1{
PTS: tmsg.DTS, PTS: tmsg.DTS,
OBUs: obus, OBUs: obus,
NTP: time.Now(), NTP: time.Now(),
@ -149,7 +150,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
return func(msg interface{}) error { return func(msg interface{}) error {
tmsg := msg.(*message.Audio) tmsg := msg.(*message.Audio)
stream.writeUnit(medi, format, &formatprocessor.UnitMPEG2Audio{ stream.WriteUnit(medi, format, &formatprocessor.UnitMPEG2Audio{
PTS: tmsg.DTS, PTS: tmsg.DTS,
Frames: [][]byte{tmsg.Payload}, Frames: [][]byte{tmsg.Payload},
NTP: time.Now(), NTP: time.Now(),
@ -163,7 +164,7 @@ func getRTMPWriteFunc(medi *media.Media, format formats.Format, stream *stream)
tmsg := msg.(*message.Audio) tmsg := msg.(*message.Audio)
if tmsg.AACType == message.AudioAACTypeAU { if tmsg.AACType == message.AudioAACTypeAU {
stream.writeUnit(medi, format, &formatprocessor.UnitMPEG4AudioGeneric{ stream.WriteUnit(medi, format, &formatprocessor.UnitMPEG4AudioGeneric{
PTS: tmsg.DTS, PTS: tmsg.DTS,
AUs: [][]byte{tmsg.Payload}, AUs: [][]byte{tmsg.Payload},
NTP: time.Now(), NTP: time.Now(),
@ -407,7 +408,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
"the stream doesn't contain any supported codec, which are currently H264, MPEG-4 Audio, MPEG-1/2 Audio") "the stream doesn't contain any supported codec, which are currently H264, MPEG-4 Audio, MPEG-1/2 Audio")
} }
defer res.stream.readerRemove(c) defer res.stream.RemoveReader(c)
c.Log(logger.Info, "is reading from path '%s', %s", c.Log(logger.Info, "is reading from path '%s', %s",
res.path.name, sourceMediaInfo(medias)) res.path.name, sourceMediaInfo(medias))
@ -451,18 +452,18 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
} }
} }
func (c *rtmpConn) findVideoFormat(stream *stream, ringBuffer *ringbuffer.RingBuffer, func (c *rtmpConn) findVideoFormat(stream *stream.Stream, ringBuffer *ringbuffer.RingBuffer,
videoFirstIDRFound *bool, videoStartDTS *time.Duration, videoFirstIDRFound *bool, videoStartDTS *time.Duration,
) (*media.Media, formats.Format) { ) (*media.Media, formats.Format) {
var videoFormatH264 *formats.H264 var videoFormatH264 *formats.H264
videoMedia := stream.medias().FindFormat(&videoFormatH264) videoMedia := stream.Medias().FindFormat(&videoFormatH264)
if videoFormatH264 != nil { if videoFormatH264 != nil {
videoStartPTSFilled := false videoStartPTSFilled := false
var videoStartPTS time.Duration var videoStartPTS time.Duration
var videoDTSExtractor *h264.DTSExtractor var videoDTSExtractor *h264.DTSExtractor
stream.readerAdd(c, videoMedia, videoFormatH264, func(unit formatprocessor.Unit) { stream.AddReader(c, videoMedia, videoFormatH264, func(unit formatprocessor.Unit) {
ringBuffer.Push(func() error { ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitH264) tunit := unit.(*formatprocessor.UnitH264)
@ -556,20 +557,20 @@ func (c *rtmpConn) findVideoFormat(stream *stream, ringBuffer *ringbuffer.RingBu
} }
func (c *rtmpConn) findAudioFormat( func (c *rtmpConn) findAudioFormat(
stream *stream, stream *stream.Stream,
ringBuffer *ringbuffer.RingBuffer, ringBuffer *ringbuffer.RingBuffer,
videoFormat formats.Format, videoFormat formats.Format,
videoFirstIDRFound *bool, videoFirstIDRFound *bool,
videoStartDTS *time.Duration, videoStartDTS *time.Duration,
) (*media.Media, formats.Format) { ) (*media.Media, formats.Format) {
var audioFormatMPEG4Generic *formats.MPEG4AudioGeneric var audioFormatMPEG4Generic *formats.MPEG4AudioGeneric
audioMedia := stream.medias().FindFormat(&audioFormatMPEG4Generic) audioMedia := stream.Medias().FindFormat(&audioFormatMPEG4Generic)
if audioMedia != nil { if audioMedia != nil {
audioStartPTSFilled := false audioStartPTSFilled := false
var audioStartPTS time.Duration var audioStartPTS time.Duration
stream.readerAdd(c, audioMedia, audioFormatMPEG4Generic, func(unit formatprocessor.Unit) { stream.AddReader(c, audioMedia, audioFormatMPEG4Generic, func(unit formatprocessor.Unit) {
ringBuffer.Push(func() error { ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitMPEG4AudioGeneric) tunit := unit.(*formatprocessor.UnitMPEG4AudioGeneric)
@ -621,7 +622,7 @@ func (c *rtmpConn) findAudioFormat(
} }
var audioFormatMPEG4AudioLATM *formats.MPEG4AudioLATM var audioFormatMPEG4AudioLATM *formats.MPEG4AudioLATM
audioMedia = stream.medias().FindFormat(&audioFormatMPEG4AudioLATM) audioMedia = stream.Medias().FindFormat(&audioFormatMPEG4AudioLATM)
if audioMedia != nil && if audioMedia != nil &&
audioFormatMPEG4AudioLATM.Config != nil && audioFormatMPEG4AudioLATM.Config != nil &&
@ -630,7 +631,7 @@ func (c *rtmpConn) findAudioFormat(
audioStartPTSFilled := false audioStartPTSFilled := false
var audioStartPTS time.Duration var audioStartPTS time.Duration
stream.readerAdd(c, audioMedia, audioFormatMPEG4AudioLATM, func(unit formatprocessor.Unit) { stream.AddReader(c, audioMedia, audioFormatMPEG4AudioLATM, func(unit formatprocessor.Unit) {
ringBuffer.Push(func() error { ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitMPEG4AudioLATM) tunit := unit.(*formatprocessor.UnitMPEG4AudioLATM)
@ -679,13 +680,13 @@ func (c *rtmpConn) findAudioFormat(
} }
var audioFormatMPEG2 *formats.MPEG2Audio var audioFormatMPEG2 *formats.MPEG2Audio
audioMedia = stream.medias().FindFormat(&audioFormatMPEG2) audioMedia = stream.Medias().FindFormat(&audioFormatMPEG2)
if audioMedia != nil { if audioMedia != nil {
audioStartPTSFilled := false audioStartPTSFilled := false
var audioStartPTS time.Duration var audioStartPTS time.Duration
stream.readerAdd(c, audioMedia, audioFormatMPEG2, func(unit formatprocessor.Unit) { stream.AddReader(c, audioMedia, audioFormatMPEG2, func(unit formatprocessor.Unit) {
ringBuffer.Push(func() error { ringBuffer.Push(func() error {
tunit := unit.(*formatprocessor.UnitMPEG2Audio) tunit := unit.(*formatprocessor.UnitMPEG2Audio)

2
internal/core/rtsp_conn.go

@ -182,7 +182,7 @@ func (c *rtspConn) onDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx,
return &base.Response{ return &base.Response{
StatusCode: base.StatusOK, StatusCode: base.StatusOK,
}, res.stream.rtspStream, nil }, res.stream.RTSPStream(), nil
} }
func (c *rtspConn) handleAuthError(authErr error) (*base.Response, error) { func (c *rtspConn) handleAuthError(authErr error) (*base.Response, error) {

7
internal/core/rtsp_session.go

@ -17,6 +17,7 @@ import (
"github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/externalcmd" "github.com/bluenviron/mediamtx/internal/externalcmd"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
) )
type rtspSessionPathManager interface { type rtspSessionPathManager interface {
@ -40,7 +41,7 @@ type rtspSession struct {
uuid uuid.UUID uuid uuid.UUID
created time.Time created time.Time
path *path path *path
stream *stream stream *stream.Stream
onReadCmd *externalcmd.Cmd // read onReadCmd *externalcmd.Cmd // read
mutex sync.Mutex mutex sync.Mutex
state gortsplib.ServerSessionState state gortsplib.ServerSessionState
@ -245,7 +246,7 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
return &base.Response{ return &base.Response{
StatusCode: base.StatusOK, StatusCode: base.StatusOK,
}, res.stream.rtspStream, nil }, res.stream.RTSPStream(), nil
default: // record default: // record
return &base.Response{ return &base.Response{
@ -315,7 +316,7 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
cforma := forma cforma := forma
ctx.Session.OnPacketRTP(cmedi, cforma, func(pkt *rtp.Packet) { ctx.Session.OnPacketRTP(cmedi, cforma, func(pkt *rtp.Packet) {
res.stream.writeRTPPacket(cmedi, cforma, pkt, time.Now()) res.stream.WriteRTPPacket(cmedi, cforma, pkt, time.Now())
}) })
} }
} }

2
internal/core/rtsp_source.go

@ -185,7 +185,7 @@ func (s *rtspSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf cha
cforma := forma cforma := forma
c.OnPacketRTP(cmedi, cforma, func(pkt *rtp.Packet) { c.OnPacketRTP(cmedi, cforma, func(pkt *rtp.Packet) {
res.stream.writeRTPPacket(cmedi, cforma, pkt, time.Now()) res.stream.WriteRTPPacket(cmedi, cforma, pkt, time.Now())
}) })
} }
} }

11
internal/core/udp_source.go

@ -14,6 +14,7 @@ import (
"github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/formatprocessor" "github.com/bluenviron/mediamtx/internal/formatprocessor"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/stream"
) )
const ( const (
@ -139,7 +140,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
} }
var medias media.Medias var medias media.Medias
var stream *stream var stream *stream.Stream
var td *mpegts.TimeDecoder var td *mpegts.TimeDecoder
decodeTime := func(t int64) time.Duration { decodeTime := func(t int64) time.Duration {
@ -163,7 +164,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
} }
r.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) error { r.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) error {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{ stream.WriteUnit(medi, medi.Formats[0], &formatprocessor.UnitH264{
PTS: decodeTime(pts), PTS: decodeTime(pts),
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -180,7 +181,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
} }
r.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) error { r.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) error {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitH265{ stream.WriteUnit(medi, medi.Formats[0], &formatprocessor.UnitH265{
PTS: decodeTime(pts), PTS: decodeTime(pts),
AU: au, AU: au,
NTP: time.Now(), NTP: time.Now(),
@ -201,7 +202,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
} }
r.OnDataMPEG4Audio(track, func(pts int64, _ int64, aus [][]byte) error { r.OnDataMPEG4Audio(track, func(pts int64, _ int64, aus [][]byte) error {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitMPEG4AudioGeneric{ stream.WriteUnit(medi, medi.Formats[0], &formatprocessor.UnitMPEG4AudioGeneric{
PTS: decodeTime(pts), PTS: decodeTime(pts),
AUs: aus, AUs: aus,
NTP: time.Now(), NTP: time.Now(),
@ -219,7 +220,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
} }
r.OnDataOpus(track, func(pts int64, _ int64, packets [][]byte) error { r.OnDataOpus(track, func(pts int64, _ int64, packets [][]byte) error {
stream.writeUnit(medi, medi.Formats[0], &formatprocessor.UnitOpus{ stream.WriteUnit(medi, medi.Formats[0], &formatprocessor.UnitOpus{
PTS: decodeTime(pts), PTS: decodeTime(pts),
Packets: packets, Packets: packets,
NTP: time.Now(), NTP: time.Now(),

5
internal/core/webrtc_incoming_track.go

@ -7,6 +7,7 @@ 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/mediamtx/internal/stream"
"github.com/pion/rtcp" "github.com/pion/rtcp"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
@ -96,7 +97,7 @@ func newWebRTCIncomingTrack(
return t, nil return t, nil
} }
func (t *webRTCIncomingTrack) start(stream *stream) { func (t *webRTCIncomingTrack) start(stream *stream.Stream) {
go func() { go func() {
for { for {
pkt, _, err := t.track.ReadRTP() pkt, _, err := t.track.ReadRTP()
@ -109,7 +110,7 @@ func (t *webRTCIncomingTrack) start(stream *stream) {
continue continue
} }
stream.writeRTPPacket(t.media, t.format, pkt, time.Now()) stream.WriteRTPPacket(t.media, t.format, pkt, time.Now())
} }
}() }()

5
internal/core/webrtc_outgoing_track.go

@ -16,6 +16,7 @@ import (
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"github.com/bluenviron/mediamtx/internal/formatprocessor" "github.com/bluenviron/mediamtx/internal/formatprocessor"
"github.com/bluenviron/mediamtx/internal/stream"
) )
// workaround until this gets tagged: // workaround until this gets tagged:
@ -346,7 +347,7 @@ func newWebRTCOutgoingTrackAudio(medias media.Medias) (*webRTCOutgoingTrack, err
func (t *webRTCOutgoingTrack) start( func (t *webRTCOutgoingTrack) start(
ctx context.Context, ctx context.Context,
r reader, r reader,
stream *stream, stream *stream.Stream,
ringBuffer *ringbuffer.RingBuffer, ringBuffer *ringbuffer.RingBuffer,
writeError chan error, writeError chan error,
) { ) {
@ -361,7 +362,7 @@ func (t *webRTCOutgoingTrack) start(
} }
}() }()
stream.readerAdd(r, t.media, t.format, func(unit formatprocessor.Unit) { stream.AddReader(r, t.media, t.format, func(unit formatprocessor.Unit) {
ringBuffer.Push(func() { ringBuffer.Push(func() {
err := t.cb(unit) err := t.cb(unit)
if err != nil { if err != nil {

4
internal/core/webrtc_session.go

@ -446,7 +446,7 @@ func (s *webRTCSession) runRead() (int, error) {
defer res.path.readerRemove(pathReaderRemoveReq{author: s}) defer res.path.readerRemove(pathReaderRemoveReq{author: s})
tracks, err := gatherOutgoingTracks(res.stream.medias()) tracks, err := gatherOutgoingTracks(res.stream.Medias())
if err != nil { if err != nil {
return http.StatusBadRequest, err return http.StatusBadRequest, err
} }
@ -522,7 +522,7 @@ func (s *webRTCSession) runRead() (int, error) {
track.start(s.ctx, s, res.stream, ringBuffer, writeError) track.start(s.ctx, s, res.stream, ringBuffer, writeError)
} }
defer res.stream.readerRemove(s) defer res.stream.RemoveReader(s)
s.Log(logger.Info, "is reading from path '%s', %s", s.Log(logger.Info, "is reading from path '%s', %s",
res.path.name, sourceMediaInfo(mediasOfOutgoingTracks(tracks))) res.path.name, sourceMediaInfo(mediasOfOutgoingTracks(tracks)))

44
internal/core/stream.go → internal/stream/stream.go

@ -1,4 +1,5 @@
package core // Package stream contains the Stream object.
package stream
import ( import (
"time" "time"
@ -9,23 +10,27 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/formatprocessor" "github.com/bluenviron/mediamtx/internal/formatprocessor"
"github.com/bluenviron/mediamtx/internal/logger"
) )
type stream struct { // Stream is a media stream.
// It stores tracks, readers and allow to write data to readers.
type Stream struct {
bytesReceived *uint64 bytesReceived *uint64
rtspStream *gortsplib.ServerStream rtspStream *gortsplib.ServerStream
smedias map[*media.Media]*streamMedia smedias map[*media.Media]*streamMedia
} }
func newStream( // New allocates a Stream.
func New(
udpMaxPayloadSize int, udpMaxPayloadSize int,
medias media.Medias, medias media.Medias,
generateRTPPackets bool, generateRTPPackets bool,
bytesReceived *uint64, bytesReceived *uint64,
source source, source logger.Writer,
) (*stream, error) { ) (*Stream, error) {
s := &stream{ s := &Stream{
bytesReceived: bytesReceived, bytesReceived: bytesReceived,
rtspStream: gortsplib.NewServerStream(medias), rtspStream: gortsplib.NewServerStream(medias),
} }
@ -43,35 +48,46 @@ func newStream(
return s, nil return s, nil
} }
func (s *stream) close() { // Close closes all resources of the stream.
func (s *Stream) Close() {
s.rtspStream.Close() s.rtspStream.Close()
} }
func (s *stream) medias() media.Medias { // Medias returns medias of the stream.
func (s *Stream) Medias() media.Medias {
return s.rtspStream.Medias() return s.rtspStream.Medias()
} }
func (s *stream) readerAdd(r reader, medi *media.Media, forma formats.Format, cb func(formatprocessor.Unit)) { // RTSPStream returns the RTSP stream.
func (s *Stream) RTSPStream() *gortsplib.ServerStream {
return s.rtspStream
}
// AddReader adds a reader.
func (s *Stream) AddReader(r interface{}, medi *media.Media, forma formats.Format, cb func(formatprocessor.Unit)) {
sm := s.smedias[medi] sm := s.smedias[medi]
sf := sm.formats[forma] sf := sm.formats[forma]
sf.readerAdd(r, cb) sf.addReader(r, cb)
} }
func (s *stream) readerRemove(r reader) { // RemoveReader removes a reader.
func (s *Stream) RemoveReader(r interface{}) {
for _, sm := range s.smedias { for _, sm := range s.smedias {
for _, sf := range sm.formats { for _, sf := range sm.formats {
sf.readerRemove(r) sf.removeReader(r)
} }
} }
} }
func (s *stream) writeUnit(medi *media.Media, forma formats.Format, data formatprocessor.Unit) { // WriteUnit writes a Unit.
func (s *Stream) WriteUnit(medi *media.Media, forma formats.Format, data formatprocessor.Unit) {
sm := s.smedias[medi] sm := s.smedias[medi]
sf := sm.formats[forma] sf := sm.formats[forma]
sf.writeUnit(s, medi, data) sf.writeUnit(s, medi, data)
} }
func (s *stream) writeRTPPacket(medi *media.Media, forma formats.Format, pkt *rtp.Packet, ntp time.Time) { // WriteRTPPacket writes a RTP packet.
func (s *Stream) WriteRTPPacket(medi *media.Media, forma formats.Format, pkt *rtp.Packet, ntp time.Time) {
sm := s.smedias[medi] sm := s.smedias[medi]
sf := sm.formats[forma] sf := sm.formats[forma]
sf.writeRTPPacket(s, medi, pkt, ntp) sf.writeRTPPacket(s, medi, pkt, ntp)

18
internal/core/stream_format.go → internal/stream/stream_format.go

@ -1,4 +1,4 @@
package core package stream
import ( import (
"sync" "sync"
@ -14,17 +14,17 @@ import (
) )
type streamFormat struct { type streamFormat struct {
source source source logger.Writer
proc formatprocessor.Processor proc formatprocessor.Processor
mutex sync.RWMutex mutex sync.RWMutex
nonRTSPReaders map[reader]func(formatprocessor.Unit) nonRTSPReaders map[interface{}]func(formatprocessor.Unit)
} }
func newStreamFormat( func newStreamFormat(
udpMaxPayloadSize int, udpMaxPayloadSize int,
forma formats.Format, forma formats.Format,
generateRTPPackets bool, generateRTPPackets bool,
source source, source logger.Writer,
) (*streamFormat, error) { ) (*streamFormat, error) {
proc, err := formatprocessor.New(udpMaxPayloadSize, forma, generateRTPPackets, source) proc, err := formatprocessor.New(udpMaxPayloadSize, forma, generateRTPPackets, source)
if err != nil { if err != nil {
@ -34,25 +34,25 @@ func newStreamFormat(
sf := &streamFormat{ sf := &streamFormat{
source: source, source: source,
proc: proc, proc: proc,
nonRTSPReaders: make(map[reader]func(formatprocessor.Unit)), nonRTSPReaders: make(map[interface{}]func(formatprocessor.Unit)),
} }
return sf, nil return sf, nil
} }
func (sf *streamFormat) readerAdd(r reader, cb func(formatprocessor.Unit)) { func (sf *streamFormat) addReader(r interface{}, cb func(formatprocessor.Unit)) {
sf.mutex.Lock() sf.mutex.Lock()
defer sf.mutex.Unlock() defer sf.mutex.Unlock()
sf.nonRTSPReaders[r] = cb sf.nonRTSPReaders[r] = cb
} }
func (sf *streamFormat) readerRemove(r reader) { func (sf *streamFormat) removeReader(r interface{}) {
sf.mutex.Lock() sf.mutex.Lock()
defer sf.mutex.Unlock() defer sf.mutex.Unlock()
delete(sf.nonRTSPReaders, r) delete(sf.nonRTSPReaders, r)
} }
func (sf *streamFormat) writeUnit(s *stream, medi *media.Media, data formatprocessor.Unit) { func (sf *streamFormat) writeUnit(s *Stream, medi *media.Media, data formatprocessor.Unit) {
sf.mutex.RLock() sf.mutex.RLock()
defer sf.mutex.RUnlock() defer sf.mutex.RUnlock()
@ -76,6 +76,6 @@ func (sf *streamFormat) writeUnit(s *stream, medi *media.Media, data formatproce
} }
} }
func (sf *streamFormat) writeRTPPacket(s *stream, medi *media.Media, pkt *rtp.Packet, ntp time.Time) { func (sf *streamFormat) writeRTPPacket(s *Stream, medi *media.Media, pkt *rtp.Packet, ntp time.Time) {
sf.writeUnit(s, medi, sf.proc.UnitForRTPPacket(pkt, ntp)) sf.writeUnit(s, medi, sf.proc.UnitForRTPPacket(pkt, ntp))
} }

6
internal/core/stream_media.go → internal/stream/stream_media.go

@ -1,8 +1,10 @@
package core package stream
import ( 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/mediamtx/internal/logger"
) )
type streamMedia struct { type streamMedia struct {
@ -12,7 +14,7 @@ type streamMedia struct {
func newStreamMedia(udpMaxPayloadSize int, func newStreamMedia(udpMaxPayloadSize int,
medi *media.Media, medi *media.Media,
generateRTPPackets bool, generateRTPPackets bool,
source source, source logger.Writer,
) (*streamMedia, error) { ) (*streamMedia, error) {
sm := &streamMedia{ sm := &streamMedia{
formats: make(map[formats.Format]*streamFormat), formats: make(map[formats.Format]*streamFormat),
Loading…
Cancel
Save