Skip to content

Commit

Permalink
Use MediaDeviceInfo instead of webrtc.RTPCodecType
Browse files Browse the repository at this point in the history
Changes:
  * Add unit tests for mediastream
  * Remove webrtc.RTPCodecType dependency in mediastream
  * Add Kind to Tracker interface
  • Loading branch information
lherman-cs committed Oct 11, 2020
1 parent 0210ec6 commit 238f190
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 19 deletions.
2 changes: 1 addition & 1 deletion mediadeviceinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type MediaDeviceType int

// MediaDeviceType definitions.
const (
VideoInput MediaDeviceType = iota
VideoInput MediaDeviceType = iota + 1
AudioInput
AudioOutput
)
Expand Down
32 changes: 14 additions & 18 deletions mediastream.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package mediadevices

import (
"sync"

"github.com/pion/webrtc/v2"
)

// MediaStream is an interface that represents a collection of existing tracks.
Expand All @@ -21,48 +19,47 @@ type MediaStream interface {
}

type mediaStream struct {
trackers map[string]Tracker
trackers map[Tracker]struct{}
l sync.RWMutex
}

const rtpCodecTypeDefault webrtc.RTPCodecType = 0
const trackTypeDefault MediaDeviceType = 0

// NewMediaStream creates a MediaStream interface that's defined in
// https://w3c.github.io/mediacapture-main/#dom-mediastream
func NewMediaStream(trackers ...Tracker) (MediaStream, error) {
m := mediaStream{trackers: make(map[string]Tracker)}
m := mediaStream{trackers: make(map[Tracker]struct{})}

for _, tracker := range trackers {
id := tracker.LocalTrack().ID()
if _, ok := m.trackers[id]; !ok {
m.trackers[id] = tracker
if _, ok := m.trackers[tracker]; !ok {
m.trackers[tracker] = struct{}{}
}
}

return &m, nil
}

func (m *mediaStream) GetAudioTracks() []Tracker {
return m.queryTracks(webrtc.RTPCodecTypeAudio)
return m.queryTracks(AudioInput)
}

func (m *mediaStream) GetVideoTracks() []Tracker {
return m.queryTracks(webrtc.RTPCodecTypeVideo)
return m.queryTracks(VideoInput)
}

func (m *mediaStream) GetTracks() []Tracker {
return m.queryTracks(rtpCodecTypeDefault)
return m.queryTracks(trackTypeDefault)
}

// queryTracks returns all tracks that are the same kind as t.
// If t is 0, which is the default, queryTracks will return all the tracks.
func (m *mediaStream) queryTracks(t webrtc.RTPCodecType) []Tracker {
func (m *mediaStream) queryTracks(t MediaDeviceType) []Tracker {
m.l.RLock()
defer m.l.RUnlock()

result := make([]Tracker, 0)
for _, tracker := range m.trackers {
if tracker.LocalTrack().Kind() == t || t == rtpCodecTypeDefault {
for tracker := range m.trackers {
if tracker.Kind() == t || t == trackTypeDefault {
result = append(result, tracker)
}
}
Expand All @@ -74,17 +71,16 @@ func (m *mediaStream) AddTrack(t Tracker) {
m.l.Lock()
defer m.l.Unlock()

id := t.LocalTrack().ID()
if _, ok := m.trackers[id]; ok {
if _, ok := m.trackers[t]; ok {
return
}

m.trackers[id] = t
m.trackers[t] = struct{}{}
}

func (m *mediaStream) RemoveTrack(t Tracker) {
m.l.Lock()
defer m.l.Unlock()

delete(m.trackers, t.LocalTrack().ID())
delete(m.trackers, t)
}
83 changes: 83 additions & 0 deletions mediastream_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package mediadevices

import (
"testing"

"github.com/pion/webrtc/v2"
)

type mockMediaStreamTrack struct {
kind MediaDeviceType
}

func (track *mockMediaStreamTrack) Track() *webrtc.Track {
return nil
}

func (track *mockMediaStreamTrack) LocalTrack() LocalTrack {
return nil
}

func (track *mockMediaStreamTrack) Stop() {
}

func (track *mockMediaStreamTrack) Kind() MediaDeviceType {
return track.kind
}

func (track *mockMediaStreamTrack) OnEnded(handler func(error)) {
}

func TestMediaStreamFilters(t *testing.T) {
audioTracks := []Tracker{
&mockMediaStreamTrack{AudioInput},
&mockMediaStreamTrack{AudioInput},
&mockMediaStreamTrack{AudioInput},
&mockMediaStreamTrack{AudioInput},
&mockMediaStreamTrack{AudioInput},
}

videoTracks := []Tracker{
&mockMediaStreamTrack{VideoInput},
&mockMediaStreamTrack{VideoInput},
&mockMediaStreamTrack{VideoInput},
}

tracks := append(audioTracks, videoTracks...)
stream, err := NewMediaStream(tracks...)
if err != nil {
t.Fatal(err)
}

expect := func(t *testing.T, actual, expected []Tracker) {
if len(actual) != len(expected) {
t.Fatalf("%s: Expected to get %d trackers, but got %d trackers", t.Name(), len(expected), len(actual))
}

for _, a := range actual {
found := false
for _, e := range expected {
if e == a {
found = true
break
}
}

if !found {
t.Fatalf("%s: Expected to find %p in the query results", t.Name(), a)
}
}
}

t.Run("GetAudioTracks", func(t *testing.T) {
expect(t, stream.GetAudioTracks(), audioTracks)
})

t.Run("GetVideoTracks", func(t *testing.T) {
expect(t, stream.GetVideoTracks(), videoTracks)
})

t.Run("GetTracks", func(t *testing.T) {
expect(t, stream.GetTracks(), tracks)
})
}
11 changes: 11 additions & 0 deletions track.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Tracker interface {
Track() *webrtc.Track
LocalTrack() LocalTrack
Stop()
Kind() MediaDeviceType
// OnEnded registers a handler to receive an error from the media stream track.
// If the error is already occured before registering, the handler will be
// immediately called.
Expand All @@ -41,12 +42,14 @@ type track struct {
err error
mu sync.Mutex
endOnce sync.Once
kind MediaDeviceType
}

func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrackConstraints) (*track, error) {
var encoderBuilders []encoderBuilder
var rtpCodecs []*webrtc.RTPCodec
var buildSampler func(t LocalTrack) samplerFunc
var kind MediaDeviceType
var err error

err = d.Open()
Expand All @@ -56,10 +59,12 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack

switch r := d.(type) {
case driver.VideoRecorder:
kind = VideoInput
rtpCodecs = opts.codecs[webrtc.RTPCodecTypeVideo]
buildSampler = newVideoSampler
encoderBuilders, err = newVideoEncoderBuilders(r, constraints)
case driver.AudioRecorder:
kind = AudioInput
rtpCodecs = opts.codecs[webrtc.RTPCodecTypeAudio]
buildSampler = func(t LocalTrack) samplerFunc {
return newAudioSampler(t, constraints.selectedMedia.Latency)
Expand Down Expand Up @@ -108,6 +113,7 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack
sample: buildSampler(localTrack),
d: d,
encoder: encoder,
kind: kind,
}
go t.start()
return &t, nil
Expand All @@ -117,6 +123,11 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack
return nil, errors.New("newTrack: failed to find a matching codec")
}

// Kind returns track's kind
func (t *track) Kind() MediaDeviceType {
return t.kind
}

// OnEnded sets an error handler. When a track has been created and started, if an
// error occurs, handler will get called with the error given to the parameter.
func (t *track) OnEnded(handler func(error)) {
Expand Down

0 comments on commit 238f190

Please sign in to comment.