mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-05 16:26:50 +08:00
162 lines
3.5 KiB
Go
162 lines
3.5 KiB
Go
package mpegts
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/AlexxIT/go2rtc/pkg/aac"
|
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
|
"github.com/AlexxIT/go2rtc/pkg/h264"
|
|
"github.com/AlexxIT/go2rtc/pkg/h265"
|
|
"github.com/pion/rtp"
|
|
)
|
|
|
|
type Producer struct {
|
|
core.SuperProducer
|
|
rd *core.ReadBuffer
|
|
}
|
|
|
|
func Open(rd io.Reader) (*Producer, error) {
|
|
prod := &Producer{rd: core.NewReadBuffer(rd)}
|
|
if err := prod.probe(); err != nil {
|
|
return nil, err
|
|
}
|
|
return prod, nil
|
|
}
|
|
|
|
func (c *Producer) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, error) {
|
|
receiver, _ := c.SuperProducer.GetTrack(media, codec)
|
|
receiver.ID = StreamType(codec)
|
|
return receiver, nil
|
|
}
|
|
|
|
func (c *Producer) Start() error {
|
|
rd := NewDemuxer()
|
|
|
|
for {
|
|
pkt, err := rd.ReadPacket(c.rd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//log.Printf("[mpegts] size: %6d, muxer: %10d, pt: %2d", len(pkt.Payload), pkt.Timestamp, pkt.PayloadType)
|
|
|
|
for _, receiver := range c.Receivers {
|
|
if receiver.ID == pkt.PayloadType {
|
|
TimestampToRTP(pkt, receiver.Codec)
|
|
receiver.WriteRTP(pkt)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Producer) Stop() error {
|
|
_ = c.SuperProducer.Close()
|
|
return c.rd.Close()
|
|
}
|
|
|
|
func (c *Producer) probe() error {
|
|
c.rd.BufferSize = core.ProbeSize
|
|
defer c.rd.Reset()
|
|
|
|
rd := NewDemuxer()
|
|
|
|
// Strategy:
|
|
// 1. Wait packet with metadata, init other packets for wait
|
|
// 2. Wait other packets
|
|
// 3. Stop after timeout
|
|
waitType := []byte{StreamTypeMetadata}
|
|
timeout := time.Now().Add(core.ProbeTimeout)
|
|
|
|
for len(waitType) != 0 && time.Now().Before(timeout) {
|
|
pkt, err := rd.ReadPacket(c.rd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check if we wait this type
|
|
if i := bytes.IndexByte(waitType, pkt.PayloadType); i < 0 {
|
|
continue
|
|
} else {
|
|
waitType = append(waitType[:i], waitType[i+1:]...)
|
|
}
|
|
|
|
switch pkt.PayloadType {
|
|
case StreamTypeMetadata:
|
|
for _, streamType := range pkt.Payload {
|
|
switch streamType {
|
|
case StreamTypeH264, StreamTypeH265, StreamTypeAAC, StreamTypePrivateOPUS:
|
|
waitType = append(waitType, streamType)
|
|
}
|
|
}
|
|
|
|
case StreamTypeH264:
|
|
codec := h264.AVCCToCodec(pkt.Payload)
|
|
media := &core.Media{
|
|
Kind: core.KindVideo,
|
|
Direction: core.DirectionRecvonly,
|
|
Codecs: []*core.Codec{codec},
|
|
}
|
|
c.Medias = append(c.Medias, media)
|
|
|
|
case StreamTypeH265:
|
|
codec := h265.AVCCToCodec(pkt.Payload)
|
|
media := &core.Media{
|
|
Kind: core.KindVideo,
|
|
Direction: core.DirectionRecvonly,
|
|
Codecs: []*core.Codec{codec},
|
|
}
|
|
c.Medias = append(c.Medias, media)
|
|
|
|
case StreamTypeAAC:
|
|
codec := aac.RTPToCodec(pkt.Payload)
|
|
media := &core.Media{
|
|
Kind: core.KindAudio,
|
|
Direction: core.DirectionRecvonly,
|
|
Codecs: []*core.Codec{codec},
|
|
}
|
|
c.Medias = append(c.Medias, media)
|
|
|
|
case StreamTypePrivateOPUS:
|
|
codec := &core.Codec{
|
|
Name: core.CodecOpus,
|
|
ClockRate: 48000,
|
|
Channels: 2,
|
|
}
|
|
media := &core.Media{
|
|
Kind: core.KindAudio,
|
|
Direction: core.DirectionRecvonly,
|
|
Codecs: []*core.Codec{codec},
|
|
}
|
|
c.Medias = append(c.Medias, media)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func StreamType(codec *core.Codec) uint8 {
|
|
switch codec.Name {
|
|
case core.CodecH264:
|
|
return StreamTypeH264
|
|
case core.CodecH265:
|
|
return StreamTypeH265
|
|
case core.CodecAAC:
|
|
return StreamTypeAAC
|
|
case core.CodecPCMA:
|
|
return StreamTypePCMATapo
|
|
case core.CodecOpus:
|
|
return StreamTypePrivateOPUS
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func TimestampToRTP(rtp *rtp.Packet, codec *core.Codec) {
|
|
if codec.ClockRate == ClockRate {
|
|
return
|
|
}
|
|
rtp.Timestamp = uint32(float64(rtp.Timestamp) * float64(codec.ClockRate) / ClockRate)
|
|
}
|