mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
188 lines
4.8 KiB
Go
188 lines
4.8 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/pion/rtcp"
|
|
"github.com/pion/rtp"
|
|
. "github.com/pion/webrtc/v3"
|
|
"m7s.live/pro"
|
|
"m7s.live/pro/pkg/task"
|
|
"m7s.live/pro/pkg/util"
|
|
mrtp "m7s.live/pro/plugin/rtp/pkg"
|
|
)
|
|
|
|
type Connection struct {
|
|
task.Job
|
|
*PeerConnection
|
|
SDP string
|
|
// LocalSDP *sdp.SessionDescription
|
|
Publisher *m7s.Publisher
|
|
PLI time.Duration
|
|
}
|
|
|
|
func (IO *Connection) GetOffer() (*SessionDescription, error) {
|
|
offer, err := IO.CreateOffer(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// IO.LocalSDP, err = offer.Unmarshal()
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
gatherComplete := GatheringCompletePromise(IO.PeerConnection)
|
|
if err = IO.SetLocalDescription(offer); err != nil {
|
|
return nil, err
|
|
}
|
|
<-gatherComplete
|
|
return IO.LocalDescription(), nil
|
|
}
|
|
|
|
func (IO *Connection) GetAnswer() (*SessionDescription, error) {
|
|
// Sets the LocalDescription, and starts our UDP listeners
|
|
answer, err := IO.CreateAnswer(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// IO.LocalSDP, err = answer.Unmarshal()
|
|
// if err != nil {
|
|
// return "", err
|
|
// }
|
|
gatherComplete := GatheringCompletePromise(IO.PeerConnection)
|
|
if err = IO.SetLocalDescription(answer); err != nil {
|
|
return nil, err
|
|
}
|
|
<-gatherComplete
|
|
return IO.LocalDescription(), nil
|
|
}
|
|
|
|
func (IO *Connection) Receive() {
|
|
IO.AddTask(IO.Publisher)
|
|
IO.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) {
|
|
IO.Info("OnTrack", "kind", track.Kind().String(), "payloadType", uint8(track.Codec().PayloadType))
|
|
var n int
|
|
var err error
|
|
if codecP := track.Codec(); track.Kind() == RTPCodecTypeAudio {
|
|
if !IO.Publisher.PubAudio {
|
|
return
|
|
}
|
|
mem := util.NewScalableMemoryAllocator(1 << 12)
|
|
defer mem.Recycle()
|
|
frame := &mrtp.Audio{}
|
|
frame.RTPCodecParameters = &codecP
|
|
frame.SetAllocator(mem)
|
|
for {
|
|
var packet rtp.Packet
|
|
buf := mem.Malloc(mrtp.MTUSize)
|
|
if n, _, err = track.Read(buf); err == nil {
|
|
mem.FreeRest(&buf, n)
|
|
err = packet.Unmarshal(buf)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
if len(packet.Payload) == 0 {
|
|
mem.Free(buf)
|
|
continue
|
|
}
|
|
if len(frame.Packets) == 0 || packet.Timestamp == frame.Packets[0].Timestamp {
|
|
frame.AddRecycleBytes(buf)
|
|
frame.Packets = append(frame.Packets, &packet)
|
|
} else {
|
|
err = IO.Publisher.WriteAudio(frame)
|
|
frame = &mrtp.Audio{}
|
|
frame.AddRecycleBytes(buf)
|
|
frame.Packets = []*rtp.Packet{&packet}
|
|
frame.RTPCodecParameters = &codecP
|
|
frame.SetAllocator(mem)
|
|
}
|
|
}
|
|
} else {
|
|
if !IO.Publisher.PubVideo {
|
|
return
|
|
}
|
|
var lastPLISent time.Time
|
|
mem := util.NewScalableMemoryAllocator(1 << 12)
|
|
defer mem.Recycle()
|
|
frame := &mrtp.Video{}
|
|
frame.RTPCodecParameters = &codecP
|
|
frame.SetAllocator(mem)
|
|
for {
|
|
if time.Since(lastPLISent) > IO.PLI {
|
|
if rtcpErr := IO.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); rtcpErr != nil {
|
|
IO.Publisher.Error("writeRTCP", "error", rtcpErr)
|
|
return
|
|
}
|
|
lastPLISent = time.Now()
|
|
}
|
|
var packet rtp.Packet
|
|
buf := mem.Malloc(mrtp.MTUSize)
|
|
if n, _, err = track.Read(buf); err == nil {
|
|
mem.FreeRest(&buf, n)
|
|
err = packet.Unmarshal(buf)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
if len(packet.Payload) == 0 {
|
|
mem.Free(buf)
|
|
continue
|
|
}
|
|
if len(frame.Packets) == 0 || packet.Timestamp == frame.Packets[0].Timestamp {
|
|
frame.AddRecycleBytes(buf)
|
|
frame.Packets = append(frame.Packets, &packet)
|
|
} else {
|
|
// t := time.Now()
|
|
err = IO.Publisher.WriteVideo(frame)
|
|
// fmt.Println("write video", time.Since(t))
|
|
frame = &mrtp.Video{}
|
|
frame.AddRecycleBytes(buf)
|
|
frame.Packets = []*rtp.Packet{&packet}
|
|
frame.RTPCodecParameters = &codecP
|
|
frame.SetAllocator(mem)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
IO.OnICECandidate(func(ice *ICECandidate) {
|
|
if ice != nil {
|
|
IO.Info(ice.ToJSON().Candidate)
|
|
}
|
|
})
|
|
IO.OnDataChannel(func(d *DataChannel) {
|
|
IO.Info("OnDataChannel", "label", d.Label())
|
|
d.OnMessage(func(msg DataChannelMessage) {
|
|
IO.SDP = string(msg.Data[1:])
|
|
IO.Debug("dc message", "sdp", IO.SDP)
|
|
if err := IO.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: IO.SDP}); err != nil {
|
|
return
|
|
}
|
|
if answer, err := IO.GetAnswer(); err == nil {
|
|
d.SendText(answer.SDP)
|
|
} else {
|
|
return
|
|
}
|
|
switch msg.Data[0] {
|
|
case '0':
|
|
IO.Stop(errors.New("stop by remote"))
|
|
case '1':
|
|
|
|
}
|
|
})
|
|
})
|
|
IO.OnConnectionStateChange(func(state PeerConnectionState) {
|
|
IO.Info("Connection State has changed:" + state.String())
|
|
switch state {
|
|
case PeerConnectionStateConnected:
|
|
|
|
case PeerConnectionStateDisconnected, PeerConnectionStateFailed, PeerConnectionStateClosed:
|
|
IO.Stop(errors.New("connection state:" + state.String()))
|
|
}
|
|
})
|
|
}
|
|
|
|
func (IO *Connection) Dispose() {
|
|
IO.PeerConnection.Close()
|
|
}
|