1
0
Fork 0
forked from External/mediamtx
mediamtx/internal/rtmputils/metadata.go

132 lines
2.7 KiB
Go

package rtmputils
import (
"fmt"
"time"
"github.com/aler9/gortsplib"
"github.com/notedit/rtmp/av"
"github.com/notedit/rtmp/codec/h264"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/notedit/rtmp/format/rtmp"
)
const (
codecH264 = 7
codecAAC = 10
)
func readMetadata(rconn *rtmp.Conn) (flvio.AMFMap, error) {
pkt, err := rconn.ReadPacket()
if err != nil {
return nil, err
}
if pkt.Type != av.Metadata {
return nil, fmt.Errorf("first packet must be metadata")
}
arr, err := flvio.ParseAMFVals(pkt.Data, false)
if err != nil {
return nil, err
}
if len(arr) != 1 {
return nil, fmt.Errorf("invalid metadata")
}
ma, ok := arr[0].(flvio.AMFMap)
if !ok {
return nil, fmt.Errorf("invalid metadata")
}
return ma, nil
}
// Metadata extracts track informations from a RTMP connection that is publishing.
func Metadata(conn ConnPair, readTimeout time.Duration) (
*gortsplib.Track, *gortsplib.Track, error) {
var videoTrack *gortsplib.Track
var audioTrack *gortsplib.Track
// configuration must be completed within readTimeout
conn.NConn.SetReadDeadline(time.Now().Add(readTimeout))
md, err := readMetadata(conn.RConn)
if err != nil {
return nil, nil, err
}
hasVideo := false
if v, ok := md.GetFloat64("videocodecid"); ok {
switch v {
case codecH264:
hasVideo = true
case 0:
default:
return nil, nil, fmt.Errorf("unsupported video codec %v", v)
}
}
hasAudio := false
if v, ok := md.GetFloat64("audiocodecid"); ok {
switch v {
case codecAAC:
hasAudio = true
case 0:
default:
return nil, nil, fmt.Errorf("unsupported audio codec %v", v)
}
}
if !hasVideo && !hasAudio {
return nil, nil, fmt.Errorf("stream has no tracks")
}
for {
var pkt av.Packet
pkt, err = conn.RConn.ReadPacket()
if err != nil {
return nil, nil, err
}
switch pkt.Type {
case av.H264DecoderConfig:
if !hasVideo {
return nil, nil, fmt.Errorf("unexpected video packet")
}
if videoTrack != nil {
return nil, nil, fmt.Errorf("video track setupped twice")
}
codec, err := h264.FromDecoderConfig(pkt.Data)
if err != nil {
return nil, nil, err
}
videoTrack, err = gortsplib.NewTrackH264(96, codec.SPS[0], codec.PPS[0])
if err != nil {
return nil, nil, err
}
case av.AACDecoderConfig:
if !hasAudio {
return nil, nil, fmt.Errorf("unexpected audio packet")
}
if audioTrack != nil {
return nil, nil, fmt.Errorf("audio track setupped twice")
}
audioTrack, err = gortsplib.NewTrackAAC(96, pkt.Data)
if err != nil {
return nil, nil, err
}
}
if (!hasVideo || videoTrack != nil) &&
(!hasAudio || audioTrack != nil) {
return videoTrack, audioTrack, nil
}
}
}