|
|
@ -55,6 +55,7 @@ var api *API |
|
|
|
var SSRC uint32 |
|
|
|
var SSRCMap = make(map[string]uint32) |
|
|
|
var ssrcLock sync.Mutex |
|
|
|
var playWaitList sync.Map |
|
|
|
|
|
|
|
func init() { |
|
|
|
m.RegisterCodec(NewRTPH264Codec(DefaultPayloadTypeH264, 90000)) |
|
|
@ -72,45 +73,18 @@ type WebRTC struct { |
|
|
|
RTP |
|
|
|
*PeerConnection |
|
|
|
RemoteAddr string |
|
|
|
videoTrack *Track |
|
|
|
} |
|
|
|
|
|
|
|
func (rtc *WebRTC) Play(streamPath string) bool { |
|
|
|
peerConnection, err := api.NewPeerConnection(Configuration{ |
|
|
|
ICEServers: []ICEServer{ |
|
|
|
{ |
|
|
|
URLs: config.ICEServers, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}) |
|
|
|
if _, err = peerConnection.AddTransceiverFromKind(RTPCodecTypeVideo); err != nil { |
|
|
|
if err != nil { |
|
|
|
Println(err) |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
if err != nil { |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
rtc.PeerConnection = peerConnection |
|
|
|
// Create a video track, using the same SSRC as the incoming RTP Packet
|
|
|
|
ssrcLock.Lock() |
|
|
|
ssrc, ok := SSRCMap[streamPath] |
|
|
|
if !ok { |
|
|
|
SSRC++ |
|
|
|
ssrc = SSRC |
|
|
|
SSRCMap[streamPath] = SSRC |
|
|
|
} |
|
|
|
ssrcLock.Unlock() |
|
|
|
videoTrack, err := peerConnection.NewTrack(DefaultPayloadTypeH264, ssrc, "video", "monibuca") |
|
|
|
if err != nil { |
|
|
|
Println(err) |
|
|
|
return false |
|
|
|
} |
|
|
|
if _, err = peerConnection.AddTrack(videoTrack); err != nil { |
|
|
|
Println(err) |
|
|
|
return false |
|
|
|
rtc.OnICEConnectionStateChange(func(connectionState ICEConnectionState) { |
|
|
|
Printf("%s Connection State has changed %s ", streamPath, connectionState.String()) |
|
|
|
switch connectionState { |
|
|
|
case ICEConnectionStateDisconnected: |
|
|
|
if rtc.Stream != nil { |
|
|
|
rtc.Stream.Close() |
|
|
|
} |
|
|
|
case ICEConnectionStateConnected: |
|
|
|
var sequence uint16 |
|
|
|
var sub Subscriber |
|
|
|
var sps []byte |
|
|
@ -121,7 +95,7 @@ func (rtc *WebRTC) Play(streamPath string) bool { |
|
|
|
sequence++ |
|
|
|
return rtp.Header{ |
|
|
|
Version: 2, |
|
|
|
SSRC: ssrc, |
|
|
|
SSRC: SSRC, |
|
|
|
PayloadType: DefaultPayloadTypeH264, |
|
|
|
SequenceNumber: sequence, |
|
|
|
Timestamp: ts, |
|
|
@ -154,8 +128,8 @@ func (rtc *WebRTC) Play(streamPath string) bool { |
|
|
|
pps = payload[2:ppsLen] |
|
|
|
} else { |
|
|
|
if packet.IsKeyFrame { |
|
|
|
if err := videoTrack.WriteRTP(&rtp.Packet{ |
|
|
|
Header: nextHeader(0, true), |
|
|
|
if err := rtc.videoTrack.WriteRTP(&rtp.Packet{ |
|
|
|
Header: nextHeader(packet.Timestamp*90, true), |
|
|
|
Payload: stapA(sps, pps), |
|
|
|
}); err != nil { |
|
|
|
return err |
|
|
@ -167,14 +141,13 @@ func (rtc *WebRTC) Play(streamPath string) bool { |
|
|
|
payload = payload[4:] |
|
|
|
_payload := payload[:naulLen] |
|
|
|
if naulLen > 1000 { |
|
|
|
part := _payload[:1000] |
|
|
|
indicator := ((part[0] >> 5) << 5) | 28 |
|
|
|
nalutype := part[0] & 31 |
|
|
|
indicator := (_payload[0] & 224) | 28 |
|
|
|
nalutype := _payload[0] & 31 |
|
|
|
header := 128 | nalutype |
|
|
|
part = part[1:] |
|
|
|
part := _payload[1:1000] |
|
|
|
marker := false |
|
|
|
for { |
|
|
|
if err := videoTrack.WriteRTP(&rtp.Packet{ |
|
|
|
if err := rtc.videoTrack.WriteRTP(&rtp.Packet{ |
|
|
|
Header: nextHeader(packet.Timestamp*90, marker), |
|
|
|
Payload: append([]byte{indicator, header}, part...), |
|
|
|
}); err != nil { |
|
|
@ -183,19 +156,19 @@ func (rtc *WebRTC) Play(streamPath string) bool { |
|
|
|
if _payload == nil { |
|
|
|
break |
|
|
|
} |
|
|
|
if len(_payload[1000:]) <= 1000 { |
|
|
|
_payload = _payload[1000:] |
|
|
|
if len(_payload) <= 1000 { |
|
|
|
header = 64 | nalutype |
|
|
|
part = _payload[1000:] |
|
|
|
part = _payload |
|
|
|
_payload = nil |
|
|
|
marker = true |
|
|
|
} else { |
|
|
|
header = nalutype |
|
|
|
part = _payload[1000:] |
|
|
|
_payload = part |
|
|
|
part = _payload[:1000] |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
if err := videoTrack.WriteRTP(&rtp.Packet{ |
|
|
|
if err := rtc.videoTrack.WriteRTP(&rtp.Packet{ |
|
|
|
Header: nextHeader(packet.Timestamp*90, true), |
|
|
|
Payload: _payload, |
|
|
|
}); err != nil { |
|
|
@ -217,17 +190,8 @@ func (rtc *WebRTC) Play(streamPath string) bool { |
|
|
|
return nil |
|
|
|
} |
|
|
|
go sub.Subscribe(streamPath) |
|
|
|
// peerConnection.OnICEConnectionStateChange(func(connectionState ICEConnectionState) {
|
|
|
|
// Printf("%s Connection State has changed %s ", streamPath, connectionState.String())
|
|
|
|
// switch connectionState {
|
|
|
|
// case ICEConnectionStateDisconnected:
|
|
|
|
// if rtc.Stream != nil {
|
|
|
|
// rtc.Stream.Close()
|
|
|
|
// }
|
|
|
|
// case ICEConnectionStateConnected:
|
|
|
|
|
|
|
|
// }
|
|
|
|
// })
|
|
|
|
} |
|
|
|
}) |
|
|
|
return true |
|
|
|
} |
|
|
|
func (rtc *WebRTC) Publish(streamPath string) bool { |
|
|
@ -310,16 +274,68 @@ func (rtc *WebRTC) GetAnswer(localSdp SessionDescription) ([]byte, error) { |
|
|
|
func run() { |
|
|
|
http.HandleFunc("/webrtc/play", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
streamPath := r.URL.Query().Get("streamPath") |
|
|
|
// offer := SessionDescription{}
|
|
|
|
// bytes, err := ioutil.ReadAll(r.Body)
|
|
|
|
// err = json.Unmarshal(bytes, &offer)
|
|
|
|
// if err != nil {
|
|
|
|
// Println(err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
offer := SessionDescription{} |
|
|
|
bytes, err := ioutil.ReadAll(r.Body) |
|
|
|
err = json.Unmarshal(bytes, &offer) |
|
|
|
if err != nil { |
|
|
|
Println(err) |
|
|
|
return |
|
|
|
} |
|
|
|
if value, ok := playWaitList.Load(streamPath); ok { |
|
|
|
rtc := value.(*WebRTC) |
|
|
|
if err := rtc.SetRemoteDescription(offer); err != nil { |
|
|
|
Println(err) |
|
|
|
return |
|
|
|
} |
|
|
|
if rtc.Play(streamPath) { |
|
|
|
w.Write([]byte(`success`)) |
|
|
|
} else { |
|
|
|
w.Write([]byte(`{"errmsg":"bad name"}`)) |
|
|
|
} |
|
|
|
} else { |
|
|
|
w.Write([]byte(`{"errmsg":"bad name"}`)) |
|
|
|
} |
|
|
|
}) |
|
|
|
http.HandleFunc("/webrtc/preparePlay", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
streamPath := r.URL.Query().Get("streamPath") |
|
|
|
rtc := new(WebRTC) |
|
|
|
peerConnection, err := api.NewPeerConnection(Configuration{ |
|
|
|
ICEServers: []ICEServer{ |
|
|
|
{ |
|
|
|
URLs: config.ICEServers, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}) |
|
|
|
if _, err = peerConnection.AddTransceiverFromKind(RTPCodecTypeVideo); err != nil { |
|
|
|
if err != nil { |
|
|
|
Println(err) |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
rtc.PeerConnection = peerConnection |
|
|
|
// Create a video track, using the same SSRC as the incoming RTP Packet
|
|
|
|
ssrcLock.Lock() |
|
|
|
if _, ok := SSRCMap[streamPath]; !ok { |
|
|
|
SSRC++ |
|
|
|
SSRCMap[streamPath] = SSRC |
|
|
|
} |
|
|
|
ssrcLock.Unlock() |
|
|
|
videoTrack, err := rtc.NewTrack(DefaultPayloadTypeH264, SSRC, "video", "monibuca") |
|
|
|
if err != nil { |
|
|
|
Println(err) |
|
|
|
return |
|
|
|
} |
|
|
|
if _, err = rtc.AddTrack(videoTrack); err != nil { |
|
|
|
Println(err) |
|
|
|
return |
|
|
|
} |
|
|
|
rtc.videoTrack = videoTrack |
|
|
|
playWaitList.Store(streamPath, rtc) |
|
|
|
rtc.RemoteAddr = r.RemoteAddr |
|
|
|
if rtc.Play(streamPath) { |
|
|
|
offer, err := rtc.CreateOffer(nil) |
|
|
|
if err != nil { |
|
|
|
Println(err) |
|
|
@ -332,9 +348,6 @@ func run() { |
|
|
|
w.Write([]byte(err.Error())) |
|
|
|
return |
|
|
|
} |
|
|
|
} else { |
|
|
|
w.Write([]byte(`{"errmsg":"bad name"}`)) |
|
|
|
} |
|
|
|
}) |
|
|
|
http.HandleFunc("/webrtc/publish", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
streamPath := r.URL.Query().Get("streamPath") |
|
|
@ -364,6 +377,7 @@ func run() { |
|
|
|
w.Write([]byte(err.Error())) |
|
|
|
return |
|
|
|
} |
|
|
|
w.Write([]byte(`success`)) |
|
|
|
} else { |
|
|
|
w.Write([]byte(`{"errmsg":"bad name"}`)) |
|
|
|
} |
|
|
|