Skip to content

Commit d9ca384

Browse files
committed
Add rtmp-to-webrtc
Example that demonstrates how you can pull from a RTMP server and send into the browser. Resolves pion/webrtc#1644
1 parent 195f681 commit d9ca384

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

rtmp-to-webrtc/README.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# rtmp-to-webrtc
2+
rtmp-to-webrtc demonstrates how you could re-stream media from a RTMP server to WebRTC.
3+
This example was heavily inspired by [rtp-to-webrtc](https://github.com/pion/webrtc/tree/master/examples/rtp-to-webrtc)
4+
5+
This example re-encodes to VP8. Pion WebRTC supports H264, but browser support is inconsistent. To switch video codecs replace all occurrences
6+
of VP8 with H264 in `main.go`
7+
8+
## Instructions
9+
### Download rtmp-to-webrtc
10+
```
11+
export GO111MODULE=on
12+
go get github.com/pion/webrtc/v3/examples/rtmp-to-webrtc
13+
```
14+
15+
### Open jsfiddle example page
16+
[jsfiddle.net](https://jsfiddle.net/z7ms3u5r/) you should see two text-areas and a 'Start Session' button
17+
18+
19+
### Run rtmp-to-webrtc with your browsers SessionDescription as stdin
20+
In the jsfiddle the top textarea is your browser's SessionDescription, copy that and:
21+
22+
#### Linux/macOS
23+
Run `echo $BROWSER_SDP | rtmp-to-webrtc`
24+
25+
#### Windows
26+
1. Paste the SessionDescription into a file.
27+
1. Run `rtmp-to-webrtc < my_file`
28+
29+
### Send RTP to listening socket
30+
On startup you will get a message `Waiting for RTP Packets`, you can use any software to send VP8 packets to port 5004 and Opus packets to port 5006. We have an example using ffmpeg below
31+
32+
#### ffmpeg
33+
```
34+
ffmpeg -i '$RTMP_URL' -an -vcodec libvpx -cpu-used 5 -deadline 1 -g 10 -error-resilient 1 -auto-alt-ref 1 -f rtp rtp://127.0.0.1:5004 -vn -c:a libopus -f rtp rtp:/127.0.0.1:5006
35+
```
36+
37+
### Input rtmp-to-webrtc's SessionDescription into your browser
38+
Copy the text that `rtmp-to-webrtc` just emitted and copy into second text area
39+
40+
### Hit 'Start Session' in jsfiddle, enjoy your video!
41+
A video should start playing in your browser above the input boxes.
42+
43+
Congrats, you have used Pion WebRTC! Now start building something cool

rtmp-to-webrtc/main.go

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// +build !js
2+
3+
package main
4+
5+
import (
6+
"fmt"
7+
"net"
8+
9+
"github.com/pion/example-webrtc-applications/internal/signal"
10+
"github.com/pion/rtp"
11+
"github.com/pion/rtp/codecs"
12+
"github.com/pion/webrtc/v3"
13+
"github.com/pion/webrtc/v3/pkg/media/samplebuilder"
14+
)
15+
16+
func main() {
17+
peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{
18+
ICEServers: []webrtc.ICEServer{
19+
{
20+
URLs: []string{"stun:stun.l.google.com:19302"},
21+
},
22+
},
23+
})
24+
if err != nil {
25+
panic(err)
26+
}
27+
28+
// Create a video track
29+
videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: "video/vp8"}, "video", "pion")
30+
if err != nil {
31+
panic(err)
32+
}
33+
rtpSender, err := peerConnection.AddTrack(videoTrack)
34+
if err != nil {
35+
panic(err)
36+
}
37+
processRTCP(rtpSender)
38+
39+
// Create a video track
40+
audioTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: "audio/opus"}, "audio", "pion")
41+
if err != nil {
42+
panic(err)
43+
}
44+
rtpSender, err = peerConnection.AddTrack(audioTrack)
45+
if err != nil {
46+
panic(err)
47+
}
48+
processRTCP(rtpSender)
49+
50+
// Set the handler for ICE connection state
51+
// This will notify you when the peer has connected/disconnected
52+
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
53+
fmt.Printf("Connection State has changed %s \n", connectionState.String())
54+
})
55+
56+
// Wait for the offer to be pasted
57+
offer := webrtc.SessionDescription{}
58+
signal.Decode(signal.MustReadStdin(), &offer)
59+
60+
// Set the remote SessionDescription
61+
if err = peerConnection.SetRemoteDescription(offer); err != nil {
62+
panic(err)
63+
}
64+
65+
// Create answer
66+
answer, err := peerConnection.CreateAnswer(nil)
67+
if err != nil {
68+
panic(err)
69+
}
70+
71+
// Create channel that is blocked until ICE Gathering is complete
72+
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
73+
74+
// Sets the LocalDescription, and starts our UDP listeners
75+
if err = peerConnection.SetLocalDescription(answer); err != nil {
76+
panic(err)
77+
}
78+
79+
// Block until ICE Gathering is complete, disabling trickle ICE
80+
// we do this because we only can exchange one signaling message
81+
// in a production application you should exchange ICE Candidates via OnICECandidate
82+
<-gatherComplete
83+
84+
// Output the answer in base64 so we can paste it in browser
85+
fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
86+
87+
go rtpToTrack(videoTrack, &codecs.VP8Packet{}, 90000, 5004)
88+
rtpToTrack(audioTrack, &codecs.OpusPacket{}, 48000, 5006)
89+
}
90+
91+
// Listen for incoming packets on a port and write them to a Track
92+
func rtpToTrack(track *webrtc.TrackLocalStaticSample, depacketizer rtp.Depacketizer, sampleRate uint32, port int) {
93+
// Open a UDP Listener for RTP Packets on port 5004
94+
listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: port})
95+
if err != nil {
96+
panic(err)
97+
}
98+
defer func() {
99+
if err = listener.Close(); err != nil {
100+
panic(err)
101+
}
102+
}()
103+
104+
sampleBuffer := samplebuilder.New(10, depacketizer, sampleRate)
105+
106+
// Read RTP packets forever and send them to the WebRTC Client
107+
for {
108+
inboundRTPPacket := make([]byte, 1500) // UDP MTU
109+
packet := &rtp.Packet{}
110+
111+
n, _, err := listener.ReadFrom(inboundRTPPacket)
112+
if err != nil {
113+
panic(fmt.Sprintf("error during read: %s", err))
114+
}
115+
116+
if err = packet.Unmarshal(inboundRTPPacket[:n]); err != nil {
117+
panic(err)
118+
}
119+
120+
sampleBuffer.Push(packet)
121+
for {
122+
sample := sampleBuffer.Pop()
123+
if sample == nil {
124+
break
125+
}
126+
127+
if writeErr := track.WriteSample(*sample); writeErr != nil {
128+
panic(writeErr)
129+
}
130+
}
131+
}
132+
}
133+
134+
// Read incoming RTCP packets
135+
// Before these packets are retuned they are processed by interceptors. For things
136+
// like NACK this needs to be called.
137+
func processRTCP(rtpSender *webrtc.RTPSender) {
138+
go func() {
139+
rtcpBuf := make([]byte, 1500)
140+
141+
for {
142+
if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
143+
return
144+
}
145+
}
146+
}()
147+
}

0 commit comments

Comments
 (0)