package stream import ( "errors" "fmt" "time" "github.com/bluenviron/gortsplib/v5/pkg/format" "github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/unit" ) type subStreamFormat struct { curFormat format.Format streamFormat *streamFormat useRTPPackets bool rtpDecoder rtpDecoder tempRTPEncoder rtpEncoder tempRTPTimeOffset uint32 } func (ssf *subStreamFormat) initialize() error { if ssf.useRTPPackets { var err error ssf.rtpDecoder, err = newRTPDecoder(ssf.curFormat) if err != nil { return err } } if ssf.streamFormat.rtpEncoder == nil && (!ssf.useRTPPackets || ssf.streamFormat.alwaysAvailable) { var err error ssf.tempRTPEncoder, err = newRTPEncoder(ssf.curFormat, ssf.streamFormat.rtpMaxPayloadSize, nil, nil) if err != nil { return err } ssf.tempRTPTimeOffset, err = randUint32() if err != nil { return err } } return nil } func (ssf *subStreamFormat) initialize2(firstTimeReceived bool, lastPTS time.Duration, lastSystemTime time.Time) { if ssf.tempRTPEncoder != nil { if ssf.streamFormat.rtpEncoder == nil { ssf.streamFormat.rtpEncoder = ssf.tempRTPEncoder ssf.streamFormat.rtpTimeOffset = ssf.tempRTPTimeOffset } ssf.tempRTPEncoder = nil ssf.tempRTPTimeOffset = 0 } if ssf.streamFormat.alwaysAvailable { if firstTimeReceived { ptsOffsetGo := lastPTS + time.Since(lastSystemTime) ssf.streamFormat.ptsOffset = multiplyAndDivide(int64(ptsOffsetGo), int64(ssf.streamFormat.format.ClockRate()), int64(time.Second)) } switch curFormat := ssf.curFormat.(type) { case *format.H265: vps, sps, pps := curFormat.SafeParams() if vps != nil && sps != nil && pps != nil { ssf.writeUnit(&unit.Unit{ PTS: 0, NTP: time.Time{}, RTPPackets: nil, Payload: unit.PayloadH265([][]byte{vps, sps, pps}), }) } case *format.H264: sps, pps := curFormat.SafeParams() if sps != nil && pps != nil { ssf.writeUnit(&unit.Unit{ PTS: 0, NTP: time.Time{}, RTPPackets: nil, Payload: unit.PayloadH264([][]byte{sps, pps}), }) } } } } func (ssf *subStreamFormat) writeUnit(u *unit.Unit) { err := ssf.writeUnitInner(u) if err != nil { ssf.streamFormat.inboundFramesInError.Add(err) return } } func (ssf *subStreamFormat) writeUnitInner(u *unit.Unit) error { if ssf.streamFormat.alwaysAvailable { u.PTS += ssf.streamFormat.ptsOffset ssf.streamFormat.updateLastTime( multiplyAndDivide2(time.Duration(u.PTS), time.Second, time.Duration(ssf.streamFormat.format.ClockRate()))) } if ssf.streamFormat.replaceNTP { u.NTP = ssf.streamFormat.ntpEstimator.Estimate(u.PTS) } if len(u.RTPPackets) != 0 { if ssf.rtpDecoder != nil { var err error u.Payload, err = ssf.rtpDecoder.decode(u.RTPPackets[0]) if err != nil { return err } } if ssf.streamFormat.rtpEncoder == nil { for _, pkt := range u.RTPPackets { if len(pkt.Payload) > ssf.streamFormat.rtpMaxPayloadSize { var err error ssf.streamFormat.rtpEncoder, err = newRTPEncoder(ssf.streamFormat.format, ssf.streamFormat.rtpMaxPayloadSize, ptrOf(pkt.SSRC), ptrOf(pkt.SequenceNumber)) if err != nil { var err2 rtpEncoderNotAvailableError if errors.As(err, &err2) { return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)", len(pkt.Payload), ssf.streamFormat.rtpMaxPayloadSize) } return err } ssf.streamFormat.rtpTimeOffset = pkt.Timestamp - uint32(u.PTS) ssf.streamFormat.parent.Log(logger.Info, "RTP packets are too big (%d > %d), remuxing them into smaller ones", len(pkt.Payload), ssf.streamFormat.rtpMaxPayloadSize) break } } } if ssf.streamFormat.rtpEncoder != nil { u.RTPPackets = nil } } if !u.NilPayload() { ssf.streamFormat.formatUpdater(ssf.streamFormat.format, u.Payload) u.Payload = ssf.streamFormat.unitRemuxer(ssf.streamFormat.format, u.Payload) if ssf.streamFormat.rtpEncoder != nil && !u.NilPayload() { var err error u.RTPPackets, err = ssf.streamFormat.rtpEncoder.encode(u.Payload) if err != nil { return err } for _, pkt := range u.RTPPackets { pkt.Timestamp += ssf.streamFormat.rtpTimeOffset + uint32(u.PTS) } } } size := unitSize(u) ssf.streamFormat.addInboundBytes(size) ssf.streamFormat.writeRTSP(ssf.streamFormat.media, u.RTPPackets, u.NTP) for sr, onData := range ssf.streamFormat.onDatas { csr := sr cOnData := onData sr.push(func() error { if !csr.SkipBytesSent { ssf.streamFormat.addOutboundBytes(size) } return cOnData(u) }) } return nil }