Use MediaDeviceInfo instead of webrtc.RTPCodecType

Changes:
  * Add unit tests for mediastream
  * Remove webrtc.RTPCodecType dependency in mediastream
  * Add Kind to Tracker interface
This commit is contained in:
Lukas Herman
2020-10-10 00:09:25 -07:00
parent 0210ec6ca6
commit 238f190e71
4 changed files with 109 additions and 19 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ type MediaDeviceType int
// MediaDeviceType definitions. // MediaDeviceType definitions.
const ( const (
VideoInput MediaDeviceType = iota VideoInput MediaDeviceType = iota + 1
AudioInput AudioInput
AudioOutput AudioOutput
) )
+14 -18
View File
@@ -2,8 +2,6 @@ package mediadevices
import ( import (
"sync" "sync"
"github.com/pion/webrtc/v2"
) )
// MediaStream is an interface that represents a collection of existing tracks. // MediaStream is an interface that represents a collection of existing tracks.
@@ -21,21 +19,20 @@ type MediaStream interface {
} }
type mediaStream struct { type mediaStream struct {
trackers map[string]Tracker trackers map[Tracker]struct{}
l sync.RWMutex l sync.RWMutex
} }
const rtpCodecTypeDefault webrtc.RTPCodecType = 0 const trackTypeDefault MediaDeviceType = 0
// NewMediaStream creates a MediaStream interface that's defined in // NewMediaStream creates a MediaStream interface that's defined in
// https://w3c.github.io/mediacapture-main/#dom-mediastream // https://w3c.github.io/mediacapture-main/#dom-mediastream
func NewMediaStream(trackers ...Tracker) (MediaStream, error) { func NewMediaStream(trackers ...Tracker) (MediaStream, error) {
m := mediaStream{trackers: make(map[string]Tracker)} m := mediaStream{trackers: make(map[Tracker]struct{})}
for _, tracker := range trackers { for _, tracker := range trackers {
id := tracker.LocalTrack().ID() if _, ok := m.trackers[tracker]; !ok {
if _, ok := m.trackers[id]; !ok { m.trackers[tracker] = struct{}{}
m.trackers[id] = tracker
} }
} }
@@ -43,26 +40,26 @@ func NewMediaStream(trackers ...Tracker) (MediaStream, error) {
} }
func (m *mediaStream) GetAudioTracks() []Tracker { func (m *mediaStream) GetAudioTracks() []Tracker {
return m.queryTracks(webrtc.RTPCodecTypeAudio) return m.queryTracks(AudioInput)
} }
func (m *mediaStream) GetVideoTracks() []Tracker { func (m *mediaStream) GetVideoTracks() []Tracker {
return m.queryTracks(webrtc.RTPCodecTypeVideo) return m.queryTracks(VideoInput)
} }
func (m *mediaStream) GetTracks() []Tracker { func (m *mediaStream) GetTracks() []Tracker {
return m.queryTracks(rtpCodecTypeDefault) return m.queryTracks(trackTypeDefault)
} }
// queryTracks returns all tracks that are the same kind as t. // 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. // 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() m.l.RLock()
defer m.l.RUnlock() defer m.l.RUnlock()
result := make([]Tracker, 0) result := make([]Tracker, 0)
for _, tracker := range m.trackers { for tracker := range m.trackers {
if tracker.LocalTrack().Kind() == t || t == rtpCodecTypeDefault { if tracker.Kind() == t || t == trackTypeDefault {
result = append(result, tracker) result = append(result, tracker)
} }
} }
@@ -74,17 +71,16 @@ func (m *mediaStream) AddTrack(t Tracker) {
m.l.Lock() m.l.Lock()
defer m.l.Unlock() defer m.l.Unlock()
id := t.LocalTrack().ID() if _, ok := m.trackers[t]; ok {
if _, ok := m.trackers[id]; ok {
return return
} }
m.trackers[id] = t m.trackers[t] = struct{}{}
} }
func (m *mediaStream) RemoveTrack(t Tracker) { func (m *mediaStream) RemoveTrack(t Tracker) {
m.l.Lock() m.l.Lock()
defer m.l.Unlock() defer m.l.Unlock()
delete(m.trackers, t.LocalTrack().ID()) delete(m.trackers, t)
} }
+83
View File
@@ -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
View File
@@ -18,6 +18,7 @@ type Tracker interface {
Track() *webrtc.Track Track() *webrtc.Track
LocalTrack() LocalTrack LocalTrack() LocalTrack
Stop() Stop()
Kind() MediaDeviceType
// OnEnded registers a handler to receive an error from the media stream track. // 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 // If the error is already occured before registering, the handler will be
// immediately called. // immediately called.
@@ -41,12 +42,14 @@ type track struct {
err error err error
mu sync.Mutex mu sync.Mutex
endOnce sync.Once endOnce sync.Once
kind MediaDeviceType
} }
func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrackConstraints) (*track, error) { func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrackConstraints) (*track, error) {
var encoderBuilders []encoderBuilder var encoderBuilders []encoderBuilder
var rtpCodecs []*webrtc.RTPCodec var rtpCodecs []*webrtc.RTPCodec
var buildSampler func(t LocalTrack) samplerFunc var buildSampler func(t LocalTrack) samplerFunc
var kind MediaDeviceType
var err error var err error
err = d.Open() err = d.Open()
@@ -56,10 +59,12 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack
switch r := d.(type) { switch r := d.(type) {
case driver.VideoRecorder: case driver.VideoRecorder:
kind = VideoInput
rtpCodecs = opts.codecs[webrtc.RTPCodecTypeVideo] rtpCodecs = opts.codecs[webrtc.RTPCodecTypeVideo]
buildSampler = newVideoSampler buildSampler = newVideoSampler
encoderBuilders, err = newVideoEncoderBuilders(r, constraints) encoderBuilders, err = newVideoEncoderBuilders(r, constraints)
case driver.AudioRecorder: case driver.AudioRecorder:
kind = AudioInput
rtpCodecs = opts.codecs[webrtc.RTPCodecTypeAudio] rtpCodecs = opts.codecs[webrtc.RTPCodecTypeAudio]
buildSampler = func(t LocalTrack) samplerFunc { buildSampler = func(t LocalTrack) samplerFunc {
return newAudioSampler(t, constraints.selectedMedia.Latency) return newAudioSampler(t, constraints.selectedMedia.Latency)
@@ -108,6 +113,7 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack
sample: buildSampler(localTrack), sample: buildSampler(localTrack),
d: d, d: d,
encoder: encoder, encoder: encoder,
kind: kind,
} }
go t.start() go t.start()
return &t, nil return &t, nil
@@ -117,6 +123,11 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack
return nil, errors.New("newTrack: failed to find a matching codec") 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 // 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. // error occurs, handler will get called with the error given to the parameter.
func (t *track) OnEnded(handler func(error)) { func (t *track) OnEnded(handler func(error)) {