mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 07:06:51 +08:00

committed by
Michiel De Backker

parent
bf422e0c0a
commit
ddcef2d84f
@@ -6,8 +6,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
"github.com/pions/webrtc/pkg/datachannel"
|
"github.com/pions/webrtc/pkg/datachannel"
|
||||||
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -27,7 +28,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -52,18 +55,22 @@ func main() {
|
|||||||
|
|
||||||
cnt := *closeAfter
|
cnt := *closeAfter
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
message := util.RandSeq(15)
|
message := signal.RandSeq(15)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
err := d.Send(datachannel.PayloadString{Data: []byte(message)})
|
err := d.Send(datachannel.PayloadString{Data: []byte(message)})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
cnt--
|
cnt--
|
||||||
if cnt < 0 {
|
if cnt < 0 {
|
||||||
fmt.Printf("Sent %d times. Closing data channel '%s'-'%d'.\n", *closeAfter, d.Label, d.ID)
|
fmt.Printf("Sent %d times. Closing data channel '%s'-'%d'.\n", *closeAfter, d.Label, d.ID)
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
err = d.Close()
|
err = d.Close()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -83,22 +90,28 @@ func main() {
|
|||||||
|
|
||||||
// Wait for the offer to be pasted
|
// Wait for the offer to be pasted
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &offer)
|
signal.Decode(signal.MustReadStdin(), &offer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the answer in base64 so we can paste it in browser
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
|
@@ -5,8 +5,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
sugar "github.com/pions/webrtc/pkg/datachannel"
|
||||||
"github.com/pions/webrtc/pkg/datachannel"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -23,11 +24,15 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a datachannel with label 'data'
|
// Create a datachannel with label 'data'
|
||||||
dataChannel, err := peerConnection.CreateDataChannel("data", nil)
|
dataChannel, err := peerConnection.CreateDataChannel("data", nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -40,20 +45,22 @@ func main() {
|
|||||||
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", dataChannel.Label, dataChannel.ID)
|
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", dataChannel.Label, dataChannel.ID)
|
||||||
|
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(15)
|
message := signal.RandSeq(15)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
err := dataChannel.Send(datachannel.PayloadString{Data: []byte(message)})
|
err := dataChannel.Send(sugar.PayloadString{Data: []byte(message)})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register the OnMessage to handle incoming messages
|
// Register the OnMessage to handle incoming messages
|
||||||
dataChannel.OnMessage(func(payload datachannel.Payload) {
|
dataChannel.OnMessage(func(payload sugar.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *sugar.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), dataChannel.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), dataChannel.Label, string(p.Data))
|
||||||
case *datachannel.PayloadBinary:
|
case *sugar.PayloadBinary:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), dataChannel.Label, p.Data)
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), dataChannel.Label, p.Data)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), dataChannel.Label)
|
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), dataChannel.Label)
|
||||||
@@ -62,22 +69,28 @@ func main() {
|
|||||||
|
|
||||||
// Create an offer to send to the browser
|
// Create an offer to send to the browser
|
||||||
offer, err := peerConnection.CreateOffer(nil)
|
offer, err := peerConnection.CreateOffer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(offer)
|
err = peerConnection.SetLocalDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the offer in base64 so we can paste it in browser
|
// Output the offer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(offer))
|
fmt.Println(signal.Encode(offer))
|
||||||
|
|
||||||
// Wait for the answer to be pasted
|
// Wait for the answer to be pasted
|
||||||
answer := webrtc.SessionDescription{}
|
answer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &answer)
|
signal.Decode(signal.MustReadStdin(), &answer)
|
||||||
|
|
||||||
// Apply the answer as the remote description
|
// Apply the answer as the remote description
|
||||||
err = peerConnection.SetRemoteDescription(answer)
|
err = peerConnection.SetRemoteDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
|
@@ -6,7 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/pions/datachannel"
|
"github.com/pions/datachannel"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const messageSize = 15
|
const messageSize = 15
|
||||||
@@ -30,11 +31,15 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a datachannel with label 'data'
|
// Create a datachannel with label 'data'
|
||||||
dataChannel, err := peerConnection.CreateDataChannel("data", nil)
|
dataChannel, err := peerConnection.CreateDataChannel("data", nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -48,7 +53,9 @@ func main() {
|
|||||||
|
|
||||||
// Detach the data channel
|
// Detach the data channel
|
||||||
raw, dErr := dataChannel.Detach()
|
raw, dErr := dataChannel.Detach()
|
||||||
util.Check(dErr)
|
if dErr != nil {
|
||||||
|
panic(dErr)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle reading from the data channel
|
// Handle reading from the data channel
|
||||||
go ReadLoop(raw)
|
go ReadLoop(raw)
|
||||||
@@ -59,22 +66,28 @@ func main() {
|
|||||||
|
|
||||||
// Create an offer to send to the browser
|
// Create an offer to send to the browser
|
||||||
offer, err := peerConnection.CreateOffer(nil)
|
offer, err := peerConnection.CreateOffer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(offer)
|
err = peerConnection.SetLocalDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the offer in base64 so we can paste it in browser
|
// Output the offer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(offer))
|
fmt.Println(signal.Encode(offer))
|
||||||
|
|
||||||
// Wait for the answer to be pasted
|
// Wait for the answer to be pasted
|
||||||
answer := webrtc.SessionDescription{}
|
answer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), answer)
|
signal.Decode(signal.MustReadStdin(), answer)
|
||||||
|
|
||||||
// Apply the answer as the remote description
|
// Apply the answer as the remote description
|
||||||
err = peerConnection.SetRemoteDescription(answer)
|
err = peerConnection.SetRemoteDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
@@ -97,10 +110,12 @@ func ReadLoop(d *datachannel.DataChannel) {
|
|||||||
// WriteLoop shows how to write to the datachannel directly
|
// WriteLoop shows how to write to the datachannel directly
|
||||||
func WriteLoop(d *datachannel.DataChannel) {
|
func WriteLoop(d *datachannel.DataChannel) {
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(messageSize)
|
message := signal.RandSeq(messageSize)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
_, err := d.Write([]byte(message))
|
_, err := d.Write([]byte(message))
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/pions/datachannel"
|
"github.com/pions/datachannel"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const messageSize = 15
|
const messageSize = 15
|
||||||
@@ -30,7 +31,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -48,7 +51,9 @@ func main() {
|
|||||||
|
|
||||||
// Detach the data channel
|
// Detach the data channel
|
||||||
raw, dErr := d.Detach()
|
raw, dErr := d.Detach()
|
||||||
util.Check(dErr)
|
if dErr != nil {
|
||||||
|
panic(dErr)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle reading from the data channel
|
// Handle reading from the data channel
|
||||||
go ReadLoop(raw)
|
go ReadLoop(raw)
|
||||||
@@ -60,22 +65,28 @@ func main() {
|
|||||||
|
|
||||||
// Wait for the offer to be pasted
|
// Wait for the offer to be pasted
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), offer)
|
signal.Decode(signal.MustReadStdin(), offer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the answer in base64 so we can paste it in browser
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
@@ -98,10 +109,12 @@ func ReadLoop(d *datachannel.DataChannel) {
|
|||||||
// WriteLoop shows how to write to the datachannel directly
|
// WriteLoop shows how to write to the datachannel directly
|
||||||
func WriteLoop(d *datachannel.DataChannel) {
|
func WriteLoop(d *datachannel.DataChannel) {
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(messageSize)
|
message := signal.RandSeq(messageSize)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
_, err := d.Write([]byte(message))
|
_, err := d.Write([]byte(message))
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
sugar "github.com/pions/webrtc/pkg/datachannel"
|
||||||
"github.com/pions/webrtc/pkg/datachannel"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -23,7 +24,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -40,20 +43,22 @@ func main() {
|
|||||||
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label, d.ID)
|
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label, d.ID)
|
||||||
|
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(15)
|
message := signal.RandSeq(15)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
err := d.Send(datachannel.PayloadString{Data: []byte(message)})
|
err := d.Send(sugar.PayloadString{Data: []byte(message)})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register message handling
|
// Register message handling
|
||||||
d.OnMessage(func(payload datachannel.Payload) {
|
d.OnMessage(func(payload sugar.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *sugar.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
||||||
case *datachannel.PayloadBinary:
|
case *sugar.PayloadBinary:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), d.Label, p.Data)
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), d.Label, p.Data)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), d.Label)
|
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), d.Label)
|
||||||
@@ -63,22 +68,28 @@ func main() {
|
|||||||
|
|
||||||
// Wait for the offer to be pasted
|
// Wait for the offer to be pasted
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &offer)
|
signal.Decode(signal.MustReadStdin(), &offer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create an answer
|
// Create an answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the answer in base64 so we can paste it in browser
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
|
@@ -7,8 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/pions/rtcp"
|
"github.com/pions/rtcp"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
gst "github.com/pions/webrtc/examples/util/gstreamer-sink"
|
gst "github.com/pions/webrtc/examples/internal/gstreamer-sink"
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gstreamerReceiveMain is launched in a goroutine because the main thread is needed
|
// gstreamerReceiveMain is launched in a goroutine because the main thread is needed
|
||||||
@@ -31,7 +32,9 @@ func gstreamerReceiveMain() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set a handler for when a new remote track starts, this handler creates a gstreamer pipeline
|
// Set a handler for when a new remote track starts, this handler creates a gstreamer pipeline
|
||||||
// for the given codec
|
// for the given codec
|
||||||
@@ -66,22 +69,28 @@ func gstreamerReceiveMain() {
|
|||||||
|
|
||||||
// Wait for the offer to be pasted
|
// Wait for the offer to be pasted
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &offer)
|
signal.Decode(signal.MustReadStdin(), &offer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create an answer
|
// Create an answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the answer in base64 so we can paste it in browser
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
|
@@ -4,8 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
gst "github.com/pions/webrtc/examples/util/gstreamer-src"
|
gst "github.com/pions/webrtc/examples/internal/gstreamer-src"
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -26,7 +27,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -36,34 +39,48 @@ func main() {
|
|||||||
|
|
||||||
// Create a audio track
|
// Create a audio track
|
||||||
opusTrack, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
opusTrack, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
_, err = peerConnection.AddTrack(opusTrack)
|
_, err = peerConnection.AddTrack(opusTrack)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a video track
|
// Create a video track
|
||||||
vp8Track, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
vp8Track, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
_, err = peerConnection.AddTrack(vp8Track)
|
_, err = peerConnection.AddTrack(vp8Track)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create an offer to send to the browser
|
// Create an offer to send to the browser
|
||||||
offer, err := peerConnection.CreateOffer(nil)
|
offer, err := peerConnection.CreateOffer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(offer)
|
err = peerConnection.SetLocalDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the offer in base64 so we can paste it in browser
|
// Output the offer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(offer))
|
fmt.Println(signal.Encode(offer))
|
||||||
|
|
||||||
// Wait for the answer to be pasted
|
// Wait for the answer to be pasted
|
||||||
answer := webrtc.SessionDescription{}
|
answer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &answer)
|
signal.Decode(signal.MustReadStdin(), &answer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(answer)
|
err = peerConnection.SetRemoteDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start pushing buffers on these tracks
|
// Start pushing buffers on these tracks
|
||||||
gst.CreatePipeline(webrtc.Opus, opusTrack.Samples, "audiotestsrc").Start()
|
gst.CreatePipeline(webrtc.Opus, opusTrack.Samples, "audiotestsrc").Start()
|
||||||
|
@@ -5,8 +5,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
gst "github.com/pions/webrtc/examples/util/gstreamer-src"
|
gst "github.com/pions/webrtc/examples/internal/gstreamer-src"
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -31,7 +32,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -41,34 +44,48 @@ func main() {
|
|||||||
|
|
||||||
// Create a audio track
|
// Create a audio track
|
||||||
opusTrack, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
opusTrack, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
_, err = peerConnection.AddTrack(opusTrack)
|
_, err = peerConnection.AddTrack(opusTrack)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a video track
|
// Create a video track
|
||||||
vp8Track, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
vp8Track, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
_, err = peerConnection.AddTrack(vp8Track)
|
_, err = peerConnection.AddTrack(vp8Track)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the offer to be pasted
|
// Wait for the offer to be pasted
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &offer)
|
signal.Decode(signal.MustReadStdin(), &offer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create an answer
|
// Create an answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the answer in base64 so we can paste it in browser
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Start pushing buffers on these tracks
|
// Start pushing buffers on these tracks
|
||||||
gst.CreatePipeline(webrtc.Opus, opusTrack.Samples, *audioSrc).Start()
|
gst.CreatePipeline(webrtc.Opus, opusTrack.Samples, *audioSrc).Start()
|
||||||
|
17
examples/internal/signal/rand.go
Normal file
17
examples/internal/signal/rand.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package signal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandSeq generates a random string to serve as dummy data
|
||||||
|
func RandSeq(n int) string {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letters[r.Intn(len(letters))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
package util
|
// Package signal contains helpers to exchange the SDP session
|
||||||
|
// description between examples.
|
||||||
|
package signal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -9,23 +11,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Allows compressing offer/answer to bypass terminal input limits.
|
// Allows compressing offer/answer to bypass terminal input limits.
|
||||||
const compress = false
|
const compress = false
|
||||||
|
|
||||||
// Check is used to panic in an error occurs.
|
|
||||||
// Don't do this! We're only using it to make the examples shorter.
|
|
||||||
func Check(err error) {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustReadStdin blocks until input is received from stdin
|
// MustReadStdin blocks until input is received from stdin
|
||||||
func MustReadStdin() string {
|
func MustReadStdin() string {
|
||||||
r := bufio.NewReader(os.Stdin)
|
r := bufio.NewReader(os.Stdin)
|
||||||
@@ -35,7 +27,9 @@ func MustReadStdin() string {
|
|||||||
var err error
|
var err error
|
||||||
in, err = r.ReadString('\n')
|
in, err = r.ReadString('\n')
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
in = strings.TrimSpace(in)
|
in = strings.TrimSpace(in)
|
||||||
if len(in) > 0 {
|
if len(in) > 0 {
|
||||||
@@ -52,7 +46,9 @@ func MustReadStdin() string {
|
|||||||
// It can optionally zip the input before encoding
|
// It can optionally zip the input before encoding
|
||||||
func Encode(obj interface{}) string {
|
func Encode(obj interface{}) string {
|
||||||
b, err := json.Marshal(obj)
|
b, err := json.Marshal(obj)
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
if compress {
|
if compress {
|
||||||
b = zip(b)
|
b = zip(b)
|
||||||
@@ -65,46 +61,51 @@ func Encode(obj interface{}) string {
|
|||||||
// It can optionally unzip the input after decoding
|
// It can optionally unzip the input after decoding
|
||||||
func Decode(in string, obj interface{}) {
|
func Decode(in string, obj interface{}) {
|
||||||
b, err := base64.StdEncoding.DecodeString(in)
|
b, err := base64.StdEncoding.DecodeString(in)
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
if compress {
|
if compress {
|
||||||
b = unzip(b)
|
b = unzip(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(b, obj)
|
err = json.Unmarshal(b, obj)
|
||||||
Check(err)
|
if err != nil {
|
||||||
}
|
panic(err)
|
||||||
|
|
||||||
// RandSeq generates a random string to serve as dummy data
|
|
||||||
func RandSeq(n int) string {
|
|
||||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
||||||
b := make([]rune, n)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = letters[r.Intn(len(letters))]
|
|
||||||
}
|
}
|
||||||
return string(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func zip(in []byte) []byte {
|
func zip(in []byte) []byte {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
gz := gzip.NewWriter(&b)
|
gz := gzip.NewWriter(&b)
|
||||||
_, err := gz.Write(in)
|
_, err := gz.Write(in)
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
err = gz.Flush()
|
err = gz.Flush()
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
err = gz.Close()
|
err = gz.Close()
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func unzip(in []byte) []byte {
|
func unzip(in []byte) []byte {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
_, err := b.Write(in)
|
_, err := b.Write(in)
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
r, err := gzip.NewReader(&b)
|
r, err := gzip.NewReader(&b)
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
res, err := ioutil.ReadAll(r)
|
res, err := ioutil.ReadAll(r)
|
||||||
Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
janus "github.com/notedit/janus-go"
|
janus "github.com/notedit/janus-go"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
"github.com/pions/webrtc/pkg/media/ivfwriter"
|
"github.com/pions/webrtc/pkg/media/ivfwriter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,7 +48,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||||
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
||||||
@@ -62,24 +63,34 @@ func main() {
|
|||||||
|
|
||||||
fmt.Println("Got VP8 track, saving to disk as output.ivf")
|
fmt.Println("Got VP8 track, saving to disk as output.ivf")
|
||||||
i, err := ivfwriter.New("output.ivf")
|
i, err := ivfwriter.New("output.ivf")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
err = i.AddPacket(<-track.Packets)
|
err = i.AddPacket(<-track.Packets)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Janus
|
// Janus
|
||||||
gateway, err := janus.Connect("ws://localhost:8188/")
|
gateway, err := janus.Connect("ws://localhost:8188/")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create session
|
// Create session
|
||||||
session, err := gateway.Create()
|
session, err := gateway.Create()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create handle
|
// Create handle
|
||||||
handle, err := session.Attach("janus.plugin.streaming")
|
handle, err := session.Attach("janus.plugin.streaming")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
go watchHandle(handle)
|
go watchHandle(handle)
|
||||||
|
|
||||||
@@ -87,27 +98,37 @@ func main() {
|
|||||||
_, err = handle.Request(map[string]interface{}{
|
_, err = handle.Request(map[string]interface{}{
|
||||||
"request": "list",
|
"request": "list",
|
||||||
})
|
})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Watch the second stream
|
// Watch the second stream
|
||||||
msg, err := handle.Message(map[string]interface{}{
|
msg, err := handle.Message(map[string]interface{}{
|
||||||
"request": "watch",
|
"request": "watch",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
}, nil)
|
}, nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
if msg.Jsep != nil {
|
if msg.Jsep != nil {
|
||||||
err = peerConnection.SetRemoteDescription(webrtc.SessionDescription{
|
err = peerConnection.SetRemoteDescription(webrtc.SessionDescription{
|
||||||
Type: webrtc.SDPTypeOffer,
|
Type: webrtc.SDPTypeOffer,
|
||||||
SDP: msg.Jsep["sdp"].(string),
|
SDP: msg.Jsep["sdp"].(string),
|
||||||
})
|
})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// now we start
|
// now we start
|
||||||
_, err = handle.Message(map[string]interface{}{
|
_, err = handle.Message(map[string]interface{}{
|
||||||
@@ -117,11 +138,15 @@ func main() {
|
|||||||
"sdp": answer.SDP,
|
"sdp": answer.SDP,
|
||||||
"trickle": false,
|
"trickle": false,
|
||||||
})
|
})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
_, err = session.KeepAlive()
|
_, err = session.KeepAlive()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
janus "github.com/notedit/janus-go"
|
janus "github.com/notedit/janus-go"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
gst "github.com/pions/webrtc/examples/util/gstreamer-src"
|
gst "github.com/pions/webrtc/examples/internal/gstreamer-src"
|
||||||
)
|
)
|
||||||
|
|
||||||
func watchHandle(handle *janus.Handle) {
|
func watchHandle(handle *janus.Handle) {
|
||||||
@@ -49,7 +49,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||||
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
||||||
@@ -57,30 +59,48 @@ func main() {
|
|||||||
|
|
||||||
// Create a audio track
|
// Create a audio track
|
||||||
opusTrack, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
opusTrack, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
_, err = peerConnection.AddTrack(opusTrack)
|
_, err = peerConnection.AddTrack(opusTrack)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a video track
|
// Create a video track
|
||||||
vp8Track, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
vp8Track, err := peerConnection.NewSampleTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
_, err = peerConnection.AddTrack(vp8Track)
|
_, err = peerConnection.AddTrack(vp8Track)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
offer, err := peerConnection.CreateOffer(nil)
|
offer, err := peerConnection.CreateOffer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = peerConnection.SetLocalDescription(offer)
|
err = peerConnection.SetLocalDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
gateway, err := janus.Connect("ws://localhost:8188/janus")
|
gateway, err := janus.Connect("ws://localhost:8188/janus")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
session, err := gateway.Create()
|
session, err := gateway.Create()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
handle, err := session.Attach("janus.plugin.videoroom")
|
handle, err := session.Attach("janus.plugin.videoroom")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
go watchHandle(handle)
|
go watchHandle(handle)
|
||||||
|
|
||||||
@@ -90,7 +110,9 @@ func main() {
|
|||||||
"room": 1234,
|
"room": 1234,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
}, nil)
|
}, nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
msg, err := handle.Message(map[string]interface{}{
|
msg, err := handle.Message(map[string]interface{}{
|
||||||
"request": "publish",
|
"request": "publish",
|
||||||
@@ -102,14 +124,18 @@ func main() {
|
|||||||
"sdp": offer.SDP,
|
"sdp": offer.SDP,
|
||||||
"trickle": false,
|
"trickle": false,
|
||||||
})
|
})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
if msg.Jsep != nil {
|
if msg.Jsep != nil {
|
||||||
err = peerConnection.SetRemoteDescription(webrtc.SessionDescription{
|
err = peerConnection.SetRemoteDescription(webrtc.SessionDescription{
|
||||||
Type: webrtc.SDPTypeAnswer,
|
Type: webrtc.SDPTypeAnswer,
|
||||||
SDP: msg.Jsep["sdp"].(string),
|
SDP: msg.Jsep["sdp"].(string),
|
||||||
})
|
})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start pushing buffers on these tracks
|
// Start pushing buffers on these tracks
|
||||||
gst.CreatePipeline(webrtc.Opus, opusTrack.Samples, "audiotestsrc").Start()
|
gst.CreatePipeline(webrtc.Opus, opusTrack.Samples, "audiotestsrc").Start()
|
||||||
|
@@ -5,10 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/pkg/quic"
|
"github.com/pions/webrtc/pkg/quic"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const messageSize = 15
|
const messageSize = 15
|
||||||
@@ -33,14 +33,18 @@ func main() {
|
|||||||
|
|
||||||
// Create the ICE gatherer
|
// Create the ICE gatherer
|
||||||
gatherer, err := api.NewICEGatherer(iceOptions)
|
gatherer, err := api.NewICEGatherer(iceOptions)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the ICE transport
|
// Construct the ICE transport
|
||||||
ice := api.NewICETransport(gatherer)
|
ice := api.NewICETransport(gatherer)
|
||||||
|
|
||||||
// Construct the Quic transport
|
// Construct the Quic transport
|
||||||
qt, err := api.NewQUICTransport(ice, nil)
|
qt, err := api.NewQUICTransport(ice, nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle incoming streams
|
// Handle incoming streams
|
||||||
qt.OnBidirectionalStream(func(stream *quic.BidirectionalStream) {
|
qt.OnBidirectionalStream(func(stream *quic.BidirectionalStream) {
|
||||||
@@ -55,26 +59,32 @@ func main() {
|
|||||||
|
|
||||||
// Gather candidates
|
// Gather candidates
|
||||||
err = gatherer.Gather()
|
err = gatherer.Gather()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
iceCandidates, err := gatherer.GetLocalCandidates()
|
iceCandidates, err := gatherer.GetLocalCandidates()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
iceParams, err := gatherer.GetLocalParameters()
|
iceParams, err := gatherer.GetLocalParameters()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
quicParams := qt.GetLocalParameters()
|
quicParams := qt.GetLocalParameters()
|
||||||
|
|
||||||
signal := Signal{
|
s := Signal{
|
||||||
ICECandidates: iceCandidates,
|
ICECandidates: iceCandidates,
|
||||||
ICEParameters: iceParams,
|
ICEParameters: iceParams,
|
||||||
QuicParameters: quicParams,
|
QuicParameters: quicParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exchange the information
|
// Exchange the information
|
||||||
fmt.Println(util.Encode(signal))
|
fmt.Println(signal.Encode(s))
|
||||||
remoteSignal := Signal{}
|
remoteSignal := Signal{}
|
||||||
util.Decode(util.MustReadStdin(), &remoteSignal)
|
signal.Decode(signal.MustReadStdin(), &remoteSignal)
|
||||||
|
|
||||||
iceRole := webrtc.ICERoleControlled
|
iceRole := webrtc.ICERoleControlled
|
||||||
if *isOffer {
|
if *isOffer {
|
||||||
@@ -82,21 +92,29 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = ice.SetRemoteCandidates(remoteSignal.ICECandidates)
|
err = ice.SetRemoteCandidates(remoteSignal.ICECandidates)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the ICE transport
|
// Start the ICE transport
|
||||||
err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole)
|
err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the Quic transport
|
// Start the Quic transport
|
||||||
err = qt.Start(remoteSignal.QuicParameters)
|
err = qt.Start(remoteSignal.QuicParameters)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the stream as the offerer
|
// Construct the stream as the offerer
|
||||||
if *isOffer {
|
if *isOffer {
|
||||||
var stream *quic.BidirectionalStream
|
var stream *quic.BidirectionalStream
|
||||||
stream, err = qt.CreateBidirectionalStream()
|
stream, err = qt.CreateBidirectionalStream()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle reading from the stream
|
// Handle reading from the stream
|
||||||
go ReadLoop(stream)
|
go ReadLoop(stream)
|
||||||
@@ -122,7 +140,9 @@ func ReadLoop(s *quic.BidirectionalStream) {
|
|||||||
for {
|
for {
|
||||||
buffer := make([]byte, messageSize)
|
buffer := make([]byte, messageSize)
|
||||||
params, err := s.ReadInto(buffer)
|
params, err := s.ReadInto(buffer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("Message from stream '%d': %s\n", s.StreamID(), string(buffer[:params.Amount]))
|
fmt.Printf("Message from stream '%d': %s\n", s.StreamID(), string(buffer[:params.Amount]))
|
||||||
}
|
}
|
||||||
@@ -131,13 +151,15 @@ func ReadLoop(s *quic.BidirectionalStream) {
|
|||||||
// WriteLoop writes to the stream
|
// WriteLoop writes to the stream
|
||||||
func WriteLoop(s *quic.BidirectionalStream) {
|
func WriteLoop(s *quic.BidirectionalStream) {
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(messageSize)
|
message := signal.RandSeq(messageSize)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
data := quic.StreamWriteParameters{
|
data := quic.StreamWriteParameters{
|
||||||
Data: []byte(message),
|
Data: []byte(message),
|
||||||
}
|
}
|
||||||
err := s.Write(data)
|
err := s.Write(data)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
sugar "github.com/pions/webrtc/pkg/datachannel"
|
||||||
"github.com/pions/webrtc/pkg/datachannel"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -28,14 +29,18 @@ func main() {
|
|||||||
|
|
||||||
// Create the ICE gatherer
|
// Create the ICE gatherer
|
||||||
gatherer, err := api.NewICEGatherer(iceOptions)
|
gatherer, err := api.NewICEGatherer(iceOptions)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the ICE transport
|
// Construct the ICE transport
|
||||||
ice := api.NewICETransport(gatherer)
|
ice := api.NewICETransport(gatherer)
|
||||||
|
|
||||||
// Construct the DTLS transport
|
// Construct the DTLS transport
|
||||||
dtls, err := api.NewDTLSTransport(ice, nil)
|
dtls, err := api.NewDTLSTransport(ice, nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the SCTP transport
|
// Construct the SCTP transport
|
||||||
sctp := api.NewSCTPTransport(dtls)
|
sctp := api.NewSCTPTransport(dtls)
|
||||||
@@ -51,19 +56,25 @@ func main() {
|
|||||||
|
|
||||||
// Gather candidates
|
// Gather candidates
|
||||||
err = gatherer.Gather()
|
err = gatherer.Gather()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
iceCandidates, err := gatherer.GetLocalCandidates()
|
iceCandidates, err := gatherer.GetLocalCandidates()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
iceParams, err := gatherer.GetLocalParameters()
|
iceParams, err := gatherer.GetLocalParameters()
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
dtlsParams := dtls.GetLocalParameters()
|
dtlsParams := dtls.GetLocalParameters()
|
||||||
|
|
||||||
sctpCapabilities := sctp.GetCapabilities()
|
sctpCapabilities := sctp.GetCapabilities()
|
||||||
|
|
||||||
signal := Signal{
|
s := Signal{
|
||||||
ICECandidates: iceCandidates,
|
ICECandidates: iceCandidates,
|
||||||
ICEParameters: iceParams,
|
ICEParameters: iceParams,
|
||||||
DTLSParameters: dtlsParams,
|
DTLSParameters: dtlsParams,
|
||||||
@@ -71,9 +82,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exchange the information
|
// Exchange the information
|
||||||
fmt.Println(util.Encode(signal))
|
fmt.Println(signal.Encode(s))
|
||||||
remoteSignal := Signal{}
|
remoteSignal := Signal{}
|
||||||
util.Decode(util.MustReadStdin(), &remoteSignal)
|
signal.Decode(signal.MustReadStdin(), &remoteSignal)
|
||||||
|
|
||||||
iceRole := webrtc.ICERoleControlled
|
iceRole := webrtc.ICERoleControlled
|
||||||
if *isOffer {
|
if *isOffer {
|
||||||
@@ -81,19 +92,27 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = ice.SetRemoteCandidates(remoteSignal.ICECandidates)
|
err = ice.SetRemoteCandidates(remoteSignal.ICECandidates)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the ICE transport
|
// Start the ICE transport
|
||||||
err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole)
|
err = ice.Start(nil, remoteSignal.ICEParameters, &iceRole)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the DTLS transport
|
// Start the DTLS transport
|
||||||
err = dtls.Start(remoteSignal.DTLSParameters)
|
err = dtls.Start(remoteSignal.DTLSParameters)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the SCTP transport
|
// Start the SCTP transport
|
||||||
err = sctp.Start(remoteSignal.SCTPCapabilities)
|
err = sctp.Start(remoteSignal.SCTPCapabilities)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the data channel as the offerer
|
// Construct the data channel as the offerer
|
||||||
if *isOffer {
|
if *isOffer {
|
||||||
@@ -103,7 +122,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
var channel *webrtc.DataChannel
|
var channel *webrtc.DataChannel
|
||||||
channel, err = api.NewDataChannel(sctp, dcParams)
|
channel, err = api.NewDataChannel(sctp, dcParams)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Register the handlers
|
// Register the handlers
|
||||||
// channel.OnOpen(handleOnOpen(channel)) // TODO: OnOpen on handle ChannelAck
|
// channel.OnOpen(handleOnOpen(channel)) // TODO: OnOpen on handle ChannelAck
|
||||||
@@ -129,21 +150,23 @@ func handleOnOpen(channel *webrtc.DataChannel) func() {
|
|||||||
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", channel.Label, channel.ID)
|
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", channel.Label, channel.ID)
|
||||||
|
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(15)
|
message := signal.RandSeq(15)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
err := channel.Send(datachannel.PayloadString{Data: []byte(message)})
|
err := channel.Send(sugar.PayloadString{Data: []byte(message)})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMessage(channel *webrtc.DataChannel) func(datachannel.Payload) {
|
func handleMessage(channel *webrtc.DataChannel) func(sugar.Payload) {
|
||||||
return func(payload datachannel.Payload) {
|
return func(payload sugar.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *sugar.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), channel.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), channel.Label, string(p.Data))
|
||||||
case *datachannel.PayloadBinary:
|
case *sugar.PayloadBinary:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), channel.Label, p.Data)
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), channel.Label, p.Data)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), channel.Label)
|
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), channel.Label)
|
||||||
|
@@ -8,8 +8,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
sugar "github.com/pions/webrtc/pkg/datachannel"
|
||||||
"github.com/pions/webrtc/pkg/datachannel"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -29,7 +30,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -46,20 +49,22 @@ func main() {
|
|||||||
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label, d.ID)
|
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label, d.ID)
|
||||||
|
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(15)
|
message := signal.RandSeq(15)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
err := d.Send(datachannel.PayloadString{Data: []byte(message)})
|
err := d.Send(sugar.PayloadString{Data: []byte(message)})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register message handling
|
// Register message handling
|
||||||
d.OnMessage(func(payload datachannel.Payload) {
|
d.OnMessage(func(payload sugar.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *sugar.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
||||||
case *datachannel.PayloadBinary:
|
case *sugar.PayloadBinary:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), d.Label, p.Data)
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), d.Label, p.Data)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), d.Label)
|
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), d.Label)
|
||||||
@@ -74,15 +79,21 @@ func main() {
|
|||||||
offer := <-offerChan
|
offer := <-offerChan
|
||||||
|
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Send the answer
|
// Send the answer
|
||||||
answerChan <- answer
|
answerChan <- answer
|
||||||
@@ -99,13 +110,17 @@ func mustSignalViaHTTP(address string) (offerOut chan webrtc.SessionDescription,
|
|||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
var offer webrtc.SessionDescription
|
var offer webrtc.SessionDescription
|
||||||
err := json.NewDecoder(r.Body).Decode(&offer)
|
err := json.NewDecoder(r.Body).Decode(&offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
offerOut <- offer
|
offerOut <- offer
|
||||||
answer := <-answerIn
|
answer := <-answerIn
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(answer)
|
err = json.NewEncoder(w).Encode(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -9,8 +9,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
sugar "github.com/pions/webrtc/pkg/datachannel"
|
||||||
"github.com/pions/webrtc/pkg/datachannel"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -30,11 +31,15 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a datachannel with label 'data'
|
// Create a datachannel with label 'data'
|
||||||
dataChannel, err := peerConnection.CreateDataChannel("data", nil)
|
dataChannel, err := peerConnection.CreateDataChannel("data", nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
@@ -47,20 +52,22 @@ func main() {
|
|||||||
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", dataChannel.Label, dataChannel.ID)
|
fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", dataChannel.Label, dataChannel.ID)
|
||||||
|
|
||||||
for range time.NewTicker(5 * time.Second).C {
|
for range time.NewTicker(5 * time.Second).C {
|
||||||
message := util.RandSeq(15)
|
message := signal.RandSeq(15)
|
||||||
fmt.Printf("Sending %s \n", message)
|
fmt.Printf("Sending %s \n", message)
|
||||||
|
|
||||||
err := dataChannel.Send(datachannel.PayloadString{Data: []byte(message)})
|
err := dataChannel.Send(sugar.PayloadString{Data: []byte(message)})
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register the OnMessage to handle incoming messages
|
// Register the OnMessage to handle incoming messages
|
||||||
dataChannel.OnMessage(func(payload datachannel.Payload) {
|
dataChannel.OnMessage(func(payload sugar.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *sugar.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), dataChannel.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), dataChannel.Label, string(p.Data))
|
||||||
case *datachannel.PayloadBinary:
|
case *sugar.PayloadBinary:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), dataChannel.Label, p.Data)
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '% 02x'\n", p.PayloadType().String(), dataChannel.Label, p.Data)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), dataChannel.Label)
|
fmt.Printf("Message '%s' from DataChannel '%s' no payload \n", p.PayloadType().String(), dataChannel.Label)
|
||||||
@@ -69,18 +76,24 @@ func main() {
|
|||||||
|
|
||||||
// Create an offer to send to the browser
|
// Create an offer to send to the browser
|
||||||
offer, err := peerConnection.CreateOffer(nil)
|
offer, err := peerConnection.CreateOffer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(offer)
|
err = peerConnection.SetLocalDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Exchange the offer for the answer
|
// Exchange the offer for the answer
|
||||||
answer := mustSignalViaHTTP(offer, *addr)
|
answer := mustSignalViaHTTP(offer, *addr)
|
||||||
|
|
||||||
// Apply the answer as the remote description
|
// Apply the answer as the remote description
|
||||||
err = peerConnection.SetRemoteDescription(answer)
|
err = peerConnection.SetRemoteDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
@@ -90,15 +103,21 @@ func main() {
|
|||||||
func mustSignalViaHTTP(offer webrtc.SessionDescription, address string) webrtc.SessionDescription {
|
func mustSignalViaHTTP(offer webrtc.SessionDescription, address string) webrtc.SessionDescription {
|
||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
err := json.NewEncoder(b).Encode(offer)
|
err := json.NewEncoder(b).Encode(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := http.Post("http://"+address, "application/json; charset=utf-8", b)
|
resp, err := http.Post("http://"+address, "application/json; charset=utf-8", b)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var answer webrtc.SessionDescription
|
var answer webrtc.SessionDescription
|
||||||
err = json.NewDecoder(resp.Body).Decode(&answer)
|
err = json.NewDecoder(resp.Body).Decode(&answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return answer
|
return answer
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/pions/rtcp"
|
"github.com/pions/rtcp"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
"github.com/pions/webrtc/pkg/media/ivfwriter"
|
"github.com/pions/webrtc/pkg/media/ivfwriter"
|
||||||
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -29,7 +30,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(config)
|
peerConnection, err := webrtc.NewPeerConnection(config)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set a handler for when a new remote track starts, this handler saves buffers to disk as
|
// Set a handler for when a new remote track starts, this handler saves buffers to disk as
|
||||||
// an ivf file, since we could have multiple video tracks we provide a counter.
|
// an ivf file, since we could have multiple video tracks we provide a counter.
|
||||||
@@ -50,10 +53,14 @@ func main() {
|
|||||||
if track.Codec.Name == webrtc.VP8 {
|
if track.Codec.Name == webrtc.VP8 {
|
||||||
fmt.Println("Got VP8 track, saving to disk as output.ivf")
|
fmt.Println("Got VP8 track, saving to disk as output.ivf")
|
||||||
i, err := ivfwriter.New("output.ivf")
|
i, err := ivfwriter.New("output.ivf")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
err = i.AddPacket(<-track.Packets)
|
err = i.AddPacket(<-track.Packets)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -66,22 +73,28 @@ func main() {
|
|||||||
|
|
||||||
// Wait for the offer to be pasted
|
// Wait for the offer to be pasted
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(util.MustReadStdin(), &offer)
|
signal.Decode(signal.MustReadStdin(), &offer)
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(offer)
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Output the answer in base64 so we can paste it in browser
|
// Output the answer in base64 so we can paste it in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
// Block forever
|
// Block forever
|
||||||
select {}
|
select {}
|
||||||
|
@@ -13,7 +13,8 @@ import (
|
|||||||
"github.com/pions/rtcp"
|
"github.com/pions/rtcp"
|
||||||
"github.com/pions/rtp"
|
"github.com/pions/rtp"
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
"github.com/pions/webrtc/examples/util"
|
|
||||||
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var peerConnectionConfig = webrtc.Configuration{
|
var peerConnectionConfig = webrtc.Configuration{
|
||||||
@@ -26,7 +27,9 @@ var peerConnectionConfig = webrtc.Configuration{
|
|||||||
|
|
||||||
func mustReadStdin(reader *bufio.Reader) string {
|
func mustReadStdin(reader *bufio.Reader) string {
|
||||||
rawSd, err := reader.ReadString('\n')
|
rawSd, err := reader.ReadString('\n')
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
|
|
||||||
return rawSd
|
return rawSd
|
||||||
@@ -54,11 +57,13 @@ func main() {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := http.ListenAndServe(":"+strconv.Itoa(*port), nil)
|
err := http.ListenAndServe(":"+strconv.Itoa(*port), nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
offer := webrtc.SessionDescription{}
|
offer := webrtc.SessionDescription{}
|
||||||
util.Decode(mustReadHTTP(sdp), &offer)
|
signal.Decode(mustReadHTTP(sdp), &offer)
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
|
|
||||||
/* Everything below is the pion-WebRTC API, thanks for using it! */
|
/* Everything below is the pion-WebRTC API, thanks for using it! */
|
||||||
@@ -68,7 +73,9 @@ func main() {
|
|||||||
|
|
||||||
// Create a new RTCPeerConnection
|
// Create a new RTCPeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig)
|
peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
inboundSSRC := make(chan uint32)
|
inboundSSRC := make(chan uint32)
|
||||||
inboundPayloadType := make(chan uint8)
|
inboundPayloadType := make(chan uint8)
|
||||||
@@ -109,18 +116,25 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
util.Check(peerConnection.SetRemoteDescription(offer))
|
err = peerConnection.SetRemoteDescription(offer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the LocalDescription and take it to base64 so we can paste in browser
|
// Get the LocalDescription and take it to base64 so we can paste in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
|
|
||||||
outboundSSRC := <-inboundSSRC
|
outboundSSRC := <-inboundSSRC
|
||||||
outboundPayloadType := <-inboundPayloadType
|
outboundPayloadType := <-inboundPayloadType
|
||||||
@@ -129,18 +143,24 @@ func main() {
|
|||||||
fmt.Println("Curl an base64 SDP to start sendonly peer connection")
|
fmt.Println("Curl an base64 SDP to start sendonly peer connection")
|
||||||
|
|
||||||
recvOnlyOffer := webrtc.SessionDescription{}
|
recvOnlyOffer := webrtc.SessionDescription{}
|
||||||
util.Decode(mustReadHTTP(sdp), &recvOnlyOffer)
|
signal.Decode(mustReadHTTP(sdp), &recvOnlyOffer)
|
||||||
|
|
||||||
// Create a new PeerConnection
|
// Create a new PeerConnection
|
||||||
peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig)
|
peerConnection, err := webrtc.NewPeerConnection(peerConnectionConfig)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a single VP8 Track to send videa
|
// Create a single VP8 Track to send videa
|
||||||
vp8Track, err := peerConnection.NewRawRTPTrack(outboundPayloadType, outboundSSRC, "video", "pion")
|
vp8Track, err := peerConnection.NewRawRTPTrack(outboundPayloadType, outboundSSRC, "video", "pion")
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = peerConnection.AddTrack(vp8Track)
|
_, err = peerConnection.AddTrack(vp8Track)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
outboundRTPLock.Lock()
|
outboundRTPLock.Lock()
|
||||||
outboundRTP = append(outboundRTP, vp8Track.RawRTP)
|
outboundRTP = append(outboundRTP, vp8Track.RawRTP)
|
||||||
@@ -148,17 +168,23 @@ func main() {
|
|||||||
|
|
||||||
// Set the remote SessionDescription
|
// Set the remote SessionDescription
|
||||||
err = peerConnection.SetRemoteDescription(recvOnlyOffer)
|
err = peerConnection.SetRemoteDescription(recvOnlyOffer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
answer, err := peerConnection.CreateAnswer(nil)
|
answer, err := peerConnection.CreateAnswer(nil)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the LocalDescription, and starts our UDP listeners
|
// Sets the LocalDescription, and starts our UDP listeners
|
||||||
err = peerConnection.SetLocalDescription(answer)
|
err = peerConnection.SetLocalDescription(answer)
|
||||||
util.Check(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the LocalDescription and take it to base64 so we can paste in browser
|
// Get the LocalDescription and take it to base64 so we can paste in browser
|
||||||
fmt.Println(util.Encode(answer))
|
fmt.Println(signal.Encode(answer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user