mirror of
https://github.com/bluenviron/mediamtx.git
synced 2026-01-26 21:39:16 -08:00
this allows to apply features that were previously implemented for single codecs (like RTP packet resizing), to any codec, and simplifies future development.
382 lines
10 KiB
Go
382 lines
10 KiB
Go
package stream
|
|
|
|
import (
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpac3"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpav1"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpfragmented"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtph264"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtph265"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpklv"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtplpcm"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmjpeg"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg1audio"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg1video"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg4audio"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpsimpleaudio"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpvp8"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpvp9"
|
|
mcopus "github.com/bluenviron/mediacommon/v2/pkg/codecs/opus"
|
|
"github.com/bluenviron/mediamtx/internal/unit"
|
|
"github.com/pion/rtp"
|
|
)
|
|
|
|
func ptrOf[T any](v T) *T {
|
|
return &v
|
|
}
|
|
|
|
type rtpEncoder interface {
|
|
encode(unit.Payload) ([]*rtp.Packet, error)
|
|
}
|
|
|
|
type rtpEncoderH265 rtph265.Encoder
|
|
|
|
func (e *rtpEncoderH265) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtph265.Encoder)(e).Encode(payload.(unit.PayloadH265))
|
|
}
|
|
|
|
type rtpEncoderH264 rtph264.Encoder
|
|
|
|
func (e *rtpEncoderH264) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtph264.Encoder)(e).Encode(payload.(unit.PayloadH264))
|
|
}
|
|
|
|
type rtpEncoderAV1 rtpav1.Encoder
|
|
|
|
func (e *rtpEncoderAV1) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpav1.Encoder)(e).Encode(payload.(unit.PayloadAV1))
|
|
}
|
|
|
|
type rtpEncoderVP9 rtpvp9.Encoder
|
|
|
|
func (e *rtpEncoderVP9) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpvp9.Encoder)(e).Encode(payload.(unit.PayloadVP9))
|
|
}
|
|
|
|
type rtpEncoderVP8 rtpvp8.Encoder
|
|
|
|
func (e *rtpEncoderVP8) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpvp8.Encoder)(e).Encode(payload.(unit.PayloadVP8))
|
|
}
|
|
|
|
type rtpEncoderMPEG4Video rtpfragmented.Encoder
|
|
|
|
func (e *rtpEncoderMPEG4Video) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpfragmented.Encoder)(e).Encode(payload.(unit.PayloadMPEG4Video))
|
|
}
|
|
|
|
type rtpEncoderMPEG1Video rtpmpeg1video.Encoder
|
|
|
|
func (e *rtpEncoderMPEG1Video) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpmpeg1video.Encoder)(e).Encode(payload.(unit.PayloadMPEG1Video))
|
|
}
|
|
|
|
type rtpEncoderMJPEG rtpmjpeg.Encoder
|
|
|
|
func (e *rtpEncoderMJPEG) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpmjpeg.Encoder)(e).Encode(payload.(unit.PayloadMJPEG))
|
|
}
|
|
|
|
type rtpEncoderOpus rtpsimpleaudio.Encoder
|
|
|
|
func (e *rtpEncoderOpus) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
pts := int64(0)
|
|
packets := make([]*rtp.Packet, len(payload.(unit.PayloadOpus)))
|
|
|
|
for i, packet := range payload.(unit.PayloadOpus) {
|
|
pkt, err := (*rtpsimpleaudio.Encoder)(e).Encode(packet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pkt.Timestamp += uint32(pts)
|
|
pts += mcopus.PacketDuration2(packet)
|
|
packets[i] = pkt
|
|
}
|
|
|
|
return packets, nil
|
|
}
|
|
|
|
type rtpEncoderMPEG4Audio rtpmpeg4audio.Encoder
|
|
|
|
func (e *rtpEncoderMPEG4Audio) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpmpeg4audio.Encoder)(e).Encode(payload.(unit.PayloadMPEG4Audio))
|
|
}
|
|
|
|
type rtpEncoderMPEG4AudioLATM rtpfragmented.Encoder
|
|
|
|
func (e *rtpEncoderMPEG4AudioLATM) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpfragmented.Encoder)(e).Encode(payload.(unit.PayloadMPEG4AudioLATM))
|
|
}
|
|
|
|
type rtpEncoderMPEG1Audio rtpmpeg1audio.Encoder
|
|
|
|
func (e *rtpEncoderMPEG1Audio) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpmpeg1audio.Encoder)(e).Encode(payload.(unit.PayloadMPEG1Audio))
|
|
}
|
|
|
|
type rtpEncoderAC3 rtpac3.Encoder
|
|
|
|
func (e *rtpEncoderAC3) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpac3.Encoder)(e).Encode(payload.(unit.PayloadAC3))
|
|
}
|
|
|
|
type rtpEncoderG711 rtplpcm.Encoder
|
|
|
|
func (e *rtpEncoderG711) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtplpcm.Encoder)(e).Encode(payload.(unit.PayloadG711))
|
|
}
|
|
|
|
type rtpEncoderLPCM rtplpcm.Encoder
|
|
|
|
func (e *rtpEncoderLPCM) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtplpcm.Encoder)(e).Encode(payload.(unit.PayloadLPCM))
|
|
}
|
|
|
|
type rtpEncoderKLV rtpklv.Encoder
|
|
|
|
func (e *rtpEncoderKLV) encode(payload unit.Payload) ([]*rtp.Packet, error) {
|
|
return (*rtpklv.Encoder)(e).Encode(payload.(unit.PayloadKLV))
|
|
}
|
|
|
|
func newRTPEncoder(
|
|
forma format.Format,
|
|
rtpMaxPayloadSize int,
|
|
ssrc *uint32,
|
|
initialSequenceNumber *uint16,
|
|
) (rtpEncoder, error) {
|
|
switch forma := forma.(type) {
|
|
case *format.H265:
|
|
wrapped := &rtph265.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
MaxDONDiff: forma.MaxDONDiff,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderH265)(wrapped), nil
|
|
|
|
case *format.H264:
|
|
wrapped := &rtph264.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
PacketizationMode: forma.PacketizationMode,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderH264)(wrapped), nil
|
|
|
|
case *format.AV1:
|
|
wrapped := &rtpav1.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderAV1)(wrapped), nil
|
|
|
|
case *format.VP9:
|
|
wrapped := &rtpvp9.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
InitialPictureID: ptrOf(uint16(0x35af)),
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderVP9)(wrapped), nil
|
|
|
|
case *format.VP8:
|
|
wrapped := &rtpvp8.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderVP8)(wrapped), nil
|
|
|
|
case *format.MPEG4Video:
|
|
wrapped := &rtpfragmented.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderMPEG4Video)(wrapped), nil
|
|
|
|
case *format.MPEG1Video:
|
|
wrapped := &rtpmpeg1video.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderMPEG1Video)(wrapped), nil
|
|
|
|
case *format.MJPEG:
|
|
wrapped := &rtpmjpeg.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderMJPEG)(wrapped), nil
|
|
|
|
case *format.Opus:
|
|
wrapped := &rtpsimpleaudio.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderOpus)(wrapped), nil
|
|
|
|
case *format.MPEG4Audio:
|
|
wrapped := &rtpmpeg4audio.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
SizeLength: forma.SizeLength,
|
|
IndexLength: forma.IndexLength,
|
|
IndexDeltaLength: forma.IndexDeltaLength,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderMPEG4Audio)(wrapped), nil
|
|
|
|
case *format.MPEG4AudioLATM:
|
|
wrapped := &rtpfragmented.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderMPEG4AudioLATM)(wrapped), nil
|
|
|
|
case *format.MPEG1Audio:
|
|
wrapped := &rtpmpeg1audio.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderMPEG1Audio)(wrapped), nil
|
|
|
|
case *format.AC3:
|
|
wrapped := &rtpac3.Encoder{
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderAC3)(wrapped), nil
|
|
|
|
case *format.G711:
|
|
wrapped := &rtplpcm.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadType(),
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
BitDepth: 8,
|
|
ChannelCount: forma.ChannelCount,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderG711)(wrapped), nil
|
|
|
|
case *format.LPCM:
|
|
wrapped := &rtplpcm.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
BitDepth: forma.BitDepth,
|
|
ChannelCount: forma.ChannelCount,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderLPCM)(wrapped), nil
|
|
|
|
case *format.KLV:
|
|
wrapped := &rtpklv.Encoder{
|
|
PayloadMaxSize: rtpMaxPayloadSize,
|
|
PayloadType: forma.PayloadTyp,
|
|
SSRC: ssrc,
|
|
InitialSequenceNumber: initialSequenceNumber,
|
|
}
|
|
err := wrapped.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return (*rtpEncoderKLV)(wrapped), nil
|
|
|
|
default:
|
|
return nil, nil
|
|
}
|
|
}
|