mirror of
https://github.com/pion/webrtc.git
synced 2025-12-24 11:51:03 +08:00
29
examples/play-from-disk-renegotation/README.md
Normal file
29
examples/play-from-disk-renegotation/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# play-from-disk-renegotiation
|
||||
play-from-disk-renegotiation demonstrates Pion WebRTC's renegotiation abilities.
|
||||
|
||||
For a simpler example of playing a file from disk we also have [examples/play-from-disk](examples/play-from-disk)
|
||||
|
||||
## Instructions
|
||||
|
||||
### Download play-from-disk-renegotiation
|
||||
This example requires you to clone the repo since it is serving static HTML.
|
||||
|
||||
```
|
||||
mkdir -p $GOPATH/src/github.com/pion
|
||||
cd $GOPATH/src/github.com/pion
|
||||
git clone https://github.com/pion/webrtc.git
|
||||
cd webrtc/examples/play-from-disk-renegotiation
|
||||
```
|
||||
|
||||
### Create IVF named `output.ivf` that contains a VP8 track
|
||||
```
|
||||
ffmpeg -i $INPUT_FILE -g 30 output.ivf
|
||||
```
|
||||
|
||||
### Run play-from-disk-renegotiation
|
||||
The `output.ivf` you created should be in the same directory as `play-from-disk-renegotiation`. Execute `go run *.go`
|
||||
|
||||
### Open the Web UI
|
||||
Open [http://localhost:8080](http://localhost:8080) and you should have a `Add Track` and `Remove Track` button. Press these to add as many tracks as you want, or to remove as many as you wish.
|
||||
|
||||
Congrats, you have used Pion WebRTC! Now start building something cool
|
||||
71
examples/play-from-disk-renegotation/index.html
Normal file
71
examples/play-from-disk-renegotation/index.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>play-from-disk-renegotation</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button onclick="window.addVideo()"> Add Video </button><br />
|
||||
<button onclick="window.removeVideo()"> Remove Video </button><br />
|
||||
|
||||
|
||||
<h3> Video </h3>
|
||||
<div id="remoteVideos"></div> <br />
|
||||
|
||||
<h3> Logs </h3>
|
||||
<div id="logs"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
let activeVideos = 0
|
||||
let pc = new RTCPeerConnection()
|
||||
pc.ontrack = function (event) {
|
||||
var el = document.createElement(event.track.kind)
|
||||
el.srcObject = event.streams[0]
|
||||
el.autoplay = true
|
||||
el.controls = true
|
||||
|
||||
event.track.onmute = function(event) {
|
||||
el.parentNode.removeChild(el);
|
||||
}
|
||||
|
||||
document.getElementById('remoteVideos').appendChild(el)
|
||||
}
|
||||
|
||||
let doSignaling = method => {
|
||||
pc.createOffer()
|
||||
.then(offer => {
|
||||
pc.setLocalDescription(offer)
|
||||
|
||||
return fetch(`/${method}`, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(offer)
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => pc.setRemoteDescription(res))
|
||||
.catch(alert)
|
||||
}
|
||||
|
||||
// Create a noop DataChannel. By default PeerConnections do not connect
|
||||
// if they have no media tracks or DataChannels
|
||||
pc.createDataChannel('noop')
|
||||
doSignaling('createPeerConnection')
|
||||
|
||||
window.addVideo = () => {
|
||||
if (pc.getTransceivers().length <= activeVideos) {
|
||||
pc.addTransceiver('video')
|
||||
activeVideos++
|
||||
}
|
||||
|
||||
doSignaling('addVideo')
|
||||
};
|
||||
|
||||
window.removeVideo = () => {
|
||||
doSignaling('removeVideo')
|
||||
};
|
||||
</script>
|
||||
</html>
|
||||
136
examples/play-from-disk-renegotation/main.go
Normal file
136
examples/play-from-disk-renegotation/main.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pion/webrtc/v2"
|
||||
"github.com/pion/webrtc/v2/pkg/media"
|
||||
"github.com/pion/webrtc/v2/pkg/media/ivfreader"
|
||||
)
|
||||
|
||||
var peerConnection *webrtc.PeerConnection //nolint
|
||||
|
||||
// doSignaling exchanges all state of the local PeerConnection and is called
|
||||
// every time a video is added or removed
|
||||
func doSignaling(w http.ResponseWriter, r *http.Request) {
|
||||
var offer webrtc.SessionDescription
|
||||
if err := json.NewDecoder(r.Body).Decode(&offer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := peerConnection.SetRemoteDescription(offer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
answer, err := peerConnection.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if err = peerConnection.SetLocalDescription(answer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
response, err := json.Marshal(answer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if _, err := w.Write(response); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a single video track
|
||||
func createPeerConnection(w http.ResponseWriter, r *http.Request) {
|
||||
if peerConnection.ConnectionState() != webrtc.PeerConnectionStateNew {
|
||||
panic(fmt.Sprintf("createPeerConnection called in non-new state (%s)", peerConnection.ConnectionState()))
|
||||
}
|
||||
|
||||
doSignaling(w, r)
|
||||
fmt.Println("PeerConnection has been created")
|
||||
}
|
||||
|
||||
// Add a single video track
|
||||
func addVideo(w http.ResponseWriter, r *http.Request) {
|
||||
videoTrack, err := peerConnection.NewTrack(webrtc.DefaultPayloadTypeVP8, rand.Uint32(), fmt.Sprintf("video-%d", rand.Uint32()), fmt.Sprintf("video-%d", rand.Uint32()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = peerConnection.AddTrack(videoTrack); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go writeVideoToTrack(videoTrack)
|
||||
doSignaling(w, r)
|
||||
fmt.Println("Video track has been added")
|
||||
}
|
||||
|
||||
// Remove a single sender
|
||||
func removeVideo(w http.ResponseWriter, r *http.Request) {
|
||||
if senders := peerConnection.GetSenders(); len(senders) != 0 {
|
||||
if err := peerConnection.RemoveTrack(senders[0]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
doSignaling(w, r)
|
||||
fmt.Println("Video track has been removed")
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
var err error
|
||||
if peerConnection, err = webrtc.NewPeerConnection(webrtc.Configuration{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 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("ICE Connection State has changed: %s\n", connectionState.String())
|
||||
})
|
||||
|
||||
http.Handle("/", http.FileServer(http.Dir(".")))
|
||||
http.HandleFunc("/createPeerConnection", createPeerConnection)
|
||||
http.HandleFunc("/addVideo", addVideo)
|
||||
http.HandleFunc("/removeVideo", removeVideo)
|
||||
panic(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
// Read a video file from disk and write it to a webrtc.Track
|
||||
// When the video has been completely read this exits without error
|
||||
func writeVideoToTrack(t *webrtc.Track) {
|
||||
// Open a IVF file and start reading using our IVFReader
|
||||
file, err := os.Open("output.ivf")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ivf, header, err := ivfreader.NewWith(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Send our video file frame at a time. Pace our sending so we send it at the same speed it should be played back as.
|
||||
// This isn't required since the video is timestamped, but we will such much higher loss if we send all at once.
|
||||
sleepTime := time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)
|
||||
for {
|
||||
frame, _, err := ivf.ParseNextFrame()
|
||||
if err != nil {
|
||||
fmt.Printf("Finish writing video track: %s ", err)
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(sleepTime)
|
||||
if err = t.WriteSample(media.Sample{Data: frame, Samples: 90000}); err != nil {
|
||||
fmt.Printf("Finish writing video track: %s ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,9 @@ func (t *RTPTransceiver) setDirection(d RTPTransceiverDirection) {
|
||||
|
||||
func (t *RTPTransceiver) setSendingTrack(track *Track) error {
|
||||
t.Sender().track = track
|
||||
if track == nil {
|
||||
t.setSender(nil)
|
||||
}
|
||||
|
||||
switch {
|
||||
case track != nil && t.Direction() == RTPTransceiverDirectionRecvonly:
|
||||
|
||||
Reference in New Issue
Block a user