Add examples/rtcp-processing

rtcp-processing demonstrates how to access RTCP Packets via ReadRTCP

Resolves #2027
This commit is contained in:
Sean DuBois
2021-11-20 22:04:00 -05:00
parent 883973804d
commit fa72a9529f
9 changed files with 234 additions and 3 deletions

View File

@@ -18,6 +18,7 @@ For more full featured examples that use 3rd party libraries see our **[example-
* [RTP to WebRTC](rtp-to-webrtc): The rtp-to-webrtc example demonstrates how to take RTP packets sent to a Pion process into your browser.
* [Simulcast](simulcast): The simulcast example demonstrates how to accept and demux 1 Track that contains 3 Simulcast streams. It then returns the media as 3 independent Tracks back to the sender.
* [Swap Tracks](swap-tracks): The swap-tracks example demonstrates deeper usage of the Pion Media API. The server accepts 3 media streams, and then dynamically routes them back as a single stream to the user.
* [RTCP Processing](rtcp-processing) The rtcp-processing example demonstrates Pion's RTCP APIs. This allow access to media statistics and control information.
#### Data Channel API
* [Data Channels](data-channels): The data-channels example shows how you can send/recv DataChannel messages from a web browser.

View File

@@ -115,7 +115,7 @@
},
{
"title": "Swap Tracks",
"link": "#",
"link": "swap-tracks",
"description": "The swap-tracks example demonstrates deeper usage of the Pion Media API. The server accepts 3 media streams, and then dynamically routes them back as a single stream to the user.",
"type": "browser"
},
@@ -124,5 +124,11 @@
"link": "#",
"description": "The vnet example demonstrates Pion's network virtualisation library. This example connects two PeerConnections over a virtual network and prints statistics about the data traveling over it.",
"type": "browser"
},
{
"title": "rtcp-processing",
"link": "rtcp-processing",
"description": "The rtcp-processing example demonstrates Pion's RTCP APIs. This allow access to media statistics and control information.",
"type": "browser"
}
]

View File

@@ -0,0 +1,38 @@
# rtcp-processing
rtcp-processing demonstrates the Public API for processing RTCP packets in Pion WebRTC.
This example is only processing messages for a RTPReceiver. A RTPReceiver is used for accepting
media from a remote peer. These APIs also exist on the RTPSender when sending media to a remote peer.
RTCP is used for statistics and control information for media in WebRTC. Using these messages
you can get information about the quality of the media, round trip time and packet loss. You can
also craft messages to influence the media quality.
## Instructions
### Download rtcp-processing
```
export GO111MODULE=on
go get github.com/pion/webrtc/v3/examples/rtcp-processing
```
### Open rtcp-processing example page
[jsfiddle.net](https://jsfiddle.net/Le3zg7sd/) you should see two text-areas, 'Start Session' button and 'Copy browser SessionDescription to clipboard'
### Run rtcp-processing with your browsers Session Description as stdin
In the jsfiddle press 'Copy browser Session Description to clipboard' or copy the base64 string manually.
Now use this value you just copied as the input to `rtcp-processing`
#### Linux/macOS
Run `echo $BROWSER_SDP | rtcp-processing`
#### Windows
1. Paste the SessionDescription into a file.
1. Run `rtcp-processing < my_file`
### Input rtcp-processing's Session Description into your browser
Copy the text that `rtcp-processing` just emitted and copy into the second text area in the jsfiddle
### Hit 'Start Session' in jsfiddle
You will see console messages for each inbound RTCP message from the remote peer.
Congrats, you have used Pion WebRTC! Now start building something cool

View File

@@ -0,0 +1,4 @@
textarea {
width: 500px;
min-height: 75px;
}

View File

@@ -0,0 +1,5 @@
---
name: rtcp-processing
description: play-from-disk demonstrates how to process RTCP messages from Pion WebRTC
authors:
- Sean DuBois

View File

@@ -0,0 +1,25 @@
Browser Session Description
<br/>
<textarea id="localSessionDescription" readonly="true"></textarea>
<br/>
<button onclick="window.copySessionDescription()">Copy browser Session Description to clipboard</button>
<br/>
<br/>
<br/>
Remote Session Description
<br/>
<textarea id="remoteSessionDescription"></textarea>
<br/>
<button onclick="window.startSession()">Start Session</button>
<br/>
<br/>
Video<br />
<video id="video1" width="160" height="120" autoplay muted></video> <br />
Logs
<br/>
<div id="div"></div>

View File

@@ -0,0 +1,62 @@
/* eslint-env browser */
const pc = new RTCPeerConnection({
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
})
const log = msg => {
document.getElementById('div').innerHTML += msg + '<br>'
}
pc.ontrack = function (event) {
const el = document.createElement(event.track.kind)
el.srcObject = event.streams[0]
el.autoplay = true
el.controls = true
document.getElementById('remoteVideos').appendChild(el)
}
pc.oniceconnectionstatechange = e => log(pc.iceConnectionState)
pc.onicecandidate = event => {
if (event.candidate === null) {
document.getElementById('localSessionDescription').value = btoa(JSON.stringify(pc.localDescription))
}
}
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
document.getElementById('video1').srcObject = stream
stream.getTracks().forEach(track => pc.addTrack(track, stream))
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
}).catch(log)
window.startSession = () => {
const sd = document.getElementById('remoteSessionDescription').value
if (sd === '') {
return alert('Session Description must not be empty')
}
try {
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))))
} catch (e) {
alert(e)
}
}
window.copySessionDescription = () => {
const browserSessionDescription = document.getElementById('localSessionDescription')
browserSessionDescription.focus()
browserSessionDescription.select()
try {
const successful = document.execCommand('copy')
const msg = successful ? 'successful' : 'unsuccessful'
log('Copying SessionDescription was ' + msg)
} catch (err) {
log('Oops, unable to copy SessionDescription ' + err)
}
}

View File

@@ -0,0 +1,91 @@
// +build !js
package main
import (
"fmt"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/examples/internal/signal"
)
func main() {
// Everything below is the Pion WebRTC API! Thanks for using it ❤️.
// Prepare the configuration
config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
// Create a new RTCPeerConnection
peerConnection, err := webrtc.NewPeerConnection(config)
if err != nil {
panic(err)
}
// Set a handler for when a new remote track starts
peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
fmt.Printf("Track has started streamId(%s) id(%s) rid(%s) \n", track.StreamID(), track.ID(), track.RID())
for {
// Read the RTCP packets as they become available for our new remote track
rtcpPackets, _, rtcpErr := receiver.ReadRTCP()
if rtcpErr != nil {
panic(rtcpErr)
}
for _, r := range rtcpPackets {
// Print a string description of the packets
if stringer, canString := r.(fmt.Stringer); canString {
fmt.Printf("Received RTCP Packet: %v", stringer.String())
}
}
}
})
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
})
// Wait for the offer to be pasted
offer := webrtc.SessionDescription{}
signal.Decode(signal.MustReadStdin(), &offer)
// Set the remote SessionDescription
err = peerConnection.SetRemoteDescription(offer)
if err != nil {
panic(err)
}
// Create answer
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
panic(err)
}
// Create channel that is blocked until ICE Gathering is complete
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
// Sets the LocalDescription, and starts our UDP listeners
err = peerConnection.SetLocalDescription(answer)
if err != nil {
panic(err)
}
// Block until ICE Gathering is complete, disabling trickle ICE
// we do this because we only can exchange one signaling message
// in a production application you should exchange ICE Candidates via OnICECandidate
<-gatherComplete
// Output the answer in base64 so we can paste it in browser
fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
// Block forever
select {}
}

View File

@@ -13,9 +13,8 @@ var log = msg => {
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
document.getElementById('video1').srcObject = stream
stream.getTracks().forEach(track => pc.addTrack(track, stream));
stream.getTracks().forEach(track => pc.addTrack(track, stream))
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
}).catch(log)