Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS media server and media proxy that allows to read, publish, proxy, record and playback video and audio streams.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

204 lines
4.7 KiB

package rtmp
import (
"time"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/bluenviron/mediamtx/internal/rtmp/h264conf"
"github.com/bluenviron/mediamtx/internal/rtmp/message"
)
func mpeg1AudioRate(sr int) uint8 {
switch sr {
case 5500:
return flvio.SOUND_5_5Khz
case 11025:
return flvio.SOUND_11Khz
case 22050:
return flvio.SOUND_22Khz
default:
return flvio.SOUND_44Khz
}
}
func mpeg1AudioChannels(m mpeg1audio.ChannelMode) uint8 {
if m == mpeg1audio.ChannelModeMono {
return flvio.SOUND_MONO
}
return flvio.SOUND_STEREO
}
// Writer is a wrapper around Conn that provides utilities to mux outgoing data.
type Writer struct {
conn *Conn
}
// NewWriter allocates a Writer.
func NewWriter(conn *Conn, videoTrack format.Format, audioTrack format.Format) (*Writer, error) {
w := &Writer{
conn: conn,
}
err := w.writeTracks(videoTrack, audioTrack)
if err != nil {
return nil, err
}
return w, nil
}
func (w *Writer) writeTracks(videoTrack format.Format, audioTrack format.Format) error {
err := w.conn.Write(&message.DataAMF0{
ChunkStreamID: 4,
MessageStreamID: 0x1000000,
Payload: []interface{}{
"@setDataFrame",
"onMetaData",
flvio.AMFMap{
{
K: "videodatarate",
V: float64(0),
},
{
K: "videocodecid",
V: func() float64 {
switch videoTrack.(type) {
case *format.H264:
return message.CodecH264
default:
return 0
}
}(),
},
{
K: "audiodatarate",
V: float64(0),
},
{
K: "audiocodecid",
V: func() float64 {
switch audioTrack.(type) {
case *format.MPEG1Audio:
return message.CodecMPEG1Audio
case *format.MPEG4Audio:
return message.CodecMPEG4Audio
default:
return 0
}
}(),
},
},
},
})
if err != nil {
return err
}
if videoTrack, ok := videoTrack.(*format.H264); ok {
// write decoder config only if SPS and PPS are available.
// if they're not available yet, they're sent later.
if sps, pps := videoTrack.SafeParams(); sps != nil && pps != nil {
buf, _ := h264conf.Conf{
SPS: sps,
PPS: pps,
}.Marshal()
err = w.conn.Write(&message.Video{
ChunkStreamID: message.VideoChunkStreamID,
MessageStreamID: 0x1000000,
Codec: message.CodecH264,
IsKeyFrame: true,
Type: message.VideoTypeConfig,
Payload: buf,
})
if err != nil {
return err
}
}
}
var audioConfig *mpeg4audio.AudioSpecificConfig
if track, ok := audioTrack.(*format.MPEG4Audio); ok {
audioConfig = track.GetConfig()
}
if audioConfig != nil {
enc, err := audioConfig.Marshal()
if err != nil {
return err
}
err = w.conn.Write(&message.Audio{
ChunkStreamID: message.AudioChunkStreamID,
MessageStreamID: 0x1000000,
Codec: message.CodecMPEG4Audio,
Rate: flvio.SOUND_44Khz,
Depth: flvio.SOUND_16BIT,
Channels: flvio.SOUND_STEREO,
AACType: message.AudioAACTypeConfig,
Payload: enc,
})
if err != nil {
return err
}
}
return nil
}
// WriteH264 writes H264 data.
func (w *Writer) WriteH264(pts time.Duration, dts time.Duration, idrPresent bool, au [][]byte) error {
avcc, err := h264.AVCCMarshal(au)
if err != nil {
return err
}
return w.conn.Write(&message.Video{
ChunkStreamID: message.VideoChunkStreamID,
MessageStreamID: 0x1000000,
Codec: message.CodecH264,
IsKeyFrame: idrPresent,
Type: message.VideoTypeAU,
Payload: avcc,
DTS: dts,
PTSDelta: pts - dts,
})
}
// WriteMPEG4Audio writes MPEG-4 Audio data.
func (w *Writer) WriteMPEG4Audio(pts time.Duration, au []byte) error {
return w.conn.Write(&message.Audio{
ChunkStreamID: message.AudioChunkStreamID,
MessageStreamID: 0x1000000,
Codec: message.CodecMPEG4Audio,
Rate: flvio.SOUND_44Khz,
Depth: flvio.SOUND_16BIT,
Channels: flvio.SOUND_STEREO,
AACType: message.AudioAACTypeAU,
Payload: au,
DTS: pts,
})
}
// WriteMPEG1Audio writes MPEG-1 Audio data.
func (w *Writer) WriteMPEG1Audio(pts time.Duration, h *mpeg1audio.FrameHeader, frame []byte) error {
return w.conn.Write(&message.Audio{
ChunkStreamID: message.AudioChunkStreamID,
MessageStreamID: 0x1000000,
Codec: message.CodecMPEG1Audio,
Rate: mpeg1AudioRate(h.SampleRate),
Depth: flvio.SOUND_16BIT,
Channels: mpeg1AudioChannels(h.ChannelMode),
Payload: frame,
DTS: pts,
})
}