Add GetDisplayMedia

* Add selectScreen to select all registered drivers that can screenshare
* Add GetDisplayMedia
* Add screenshare example
This commit is contained in:
Lukas Herman
2020-02-13 21:07:34 -08:00
parent 504b542376
commit ed2323bebc
3 changed files with 182 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
## Instructions
### Download screenshare
```
go get github.com/pion/mediadevices/examples/screenshare
```
### Open example page
[jsfiddle.net](https://jsfiddle.net/z7ms3u5r/) you should see two text-areas and a 'Start Session' button
### Run screenshare with your browsers SessionDescription as stdin
In the jsfiddle the top textarea is your browser, copy that and:
#### Linux
Run `echo $BROWSER_SDP | screenshare`
### Input screenshare's SessionDescription into your browser
Copy the text that `screenshare` just emitted and copy into second text area
### Hit 'Start Session' in jsfiddle, enjoy your video!
A video should start playing in your browser above the input boxes, and will continue playing until you close the application.
Congrats, you have used pion-WebRTC! Now start building something cool
+103
View File
@@ -0,0 +1,103 @@
package main
import (
"fmt"
"github.com/pion/mediadevices"
"github.com/pion/mediadevices/examples/internal/signal"
_ "github.com/pion/mediadevices/pkg/codec/openh264" // This is required to register h264 video encoder
_ "github.com/pion/mediadevices/pkg/codec/opus" // This is required to register opus audio encoder
_ "github.com/pion/mediadevices/pkg/codec/vpx"
"github.com/pion/mediadevices/pkg/frame"
"github.com/pion/webrtc/v2"
)
const (
videoCodecName = webrtc.VP8
)
func main() {
config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
// Wait for the offer to be pasted
offer := webrtc.SessionDescription{}
signal.Decode(signal.MustReadStdin(), &offer)
// Create a new RTCPeerConnection
mediaEngine := webrtc.MediaEngine{}
if err := mediaEngine.PopulateFromSDP(offer); err != nil {
panic(err)
}
api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine))
peerConnection, err := api.NewPeerConnection(config)
if 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("Connection State has changed %s \n", connectionState.String())
})
md := mediadevices.NewMediaDevices(peerConnection)
s, err := md.GetDisplayMedia(mediadevices.MediaStreamConstraints{
Video: func(c *mediadevices.MediaTrackConstraints) {
c.CodecName = videoCodecName
c.FrameFormat = frame.FormatI444
c.Enabled = true
c.Width = 640
c.Height = 480
c.BitRate = 100000 // 100kbps
},
})
if err != nil {
panic(err)
}
for _, tracker := range s.GetTracks() {
t := tracker.Track()
tracker.OnEnded(func(err error) {
fmt.Printf("Track (ID: %s, Label: %s) ended with error: %v\n",
t.ID(), t.Label(), err)
})
_, err = peerConnection.AddTrack(t)
if err != nil {
panic(err)
}
}
// Tweak transceiver direction to work with Firefox
for _, t := range peerConnection.GetTransceivers() {
t.Direction = webrtc.RTPTransceiverDirectionSendonly
}
// Set the remote SessionDescription
err = peerConnection.SetRemoteDescription(offer)
if err != nil {
panic(err)
}
// Create an answer
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
panic(err)
}
// Sets the LocalDescription, and starts our UDP listeners
err = peerConnection.SetLocalDescription(answer)
if err != nil {
panic(err)
}
// Output the answer in base64 so we can paste it in browser
fmt.Println(signal.Encode(answer))
select {}
}
+50
View File
@@ -13,6 +13,7 @@ var errNotFound = fmt.Errorf("failed to find the best driver that fits the const
// MediaDevices is an interface that's defined on https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
type MediaDevices interface {
GetDisplayMedia(constraints MediaStreamConstraints) (MediaStream, error)
GetUserMedia(constraints MediaStreamConstraints) (MediaStream, error)
EnumerateDevices() []MediaDeviceInfo
}
@@ -75,6 +76,34 @@ func WithTrackGenerator(gen TrackGenerator) MediaDevicesOption {
}
}
// GetDisplayMedia prompts the user to select and grant permission to capture the contents
// of a display or portion thereof (such as a window) as a MediaStream.
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
func (m *mediaDevices) GetDisplayMedia(constraints MediaStreamConstraints) (MediaStream, error) {
trackers := make([]Tracker, 0)
var videoConstraints MediaTrackConstraints
if constraints.Video != nil {
constraints.Video(&videoConstraints)
}
if videoConstraints.Enabled {
tracker, err := m.selectScreen(videoConstraints)
if err != nil {
return nil, err
}
trackers = append(trackers, tracker)
}
s, err := NewMediaStream(trackers...)
if err != nil {
return nil, err
}
return s, nil
}
// GetUserMedia prompts the user for permission to use a media input which produces a MediaStream
// with tracks containing the requested types of media.
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
@@ -212,6 +241,27 @@ func (m *mediaDevices) selectVideo(constraints MediaTrackConstraints) (Tracker,
return newVideoTrack(&m.MediaDevicesOptions, d, c)
}
func (m *mediaDevices) selectScreen(constraints MediaTrackConstraints) (Tracker, error) {
typeFilter := driver.FilterVideoRecorder()
screenFilter := driver.FilterDeviceType(driver.Screen)
filter := func(d driver.Driver) bool {
return typeFilter(d) && screenFilter(d)
}
if constraints.DeviceID != "" {
idFilter := driver.FilterID(constraints.DeviceID)
filter = func(d driver.Driver) bool {
return typeFilter(d) && screenFilter(d) && idFilter(d)
}
}
d, c, err := selectBestDriver(filter, constraints)
if err != nil {
return nil, err
}
return newVideoTrack(&m.MediaDevicesOptions, d, c)
}
func (m *mediaDevices) EnumerateDevices() []MediaDeviceInfo {
drivers := driver.GetManager().Query(
driver.FilterFn(func(driver.Driver) bool { return true }))