From ecff5e63a59a935bfea9ce06ee4ecd8a2e6fab9a Mon Sep 17 00:00:00 2001 From: Atsushi Watanabe Date: Thu, 21 May 2020 11:44:35 +0900 Subject: [PATCH] prop: support ranged/exact/oneof constraints --- examples/facedetection/main.go | 7 +- examples/rtp-send/main.go | 7 +- examples/simple/main.go | 7 +- mediadevices.go | 9 +- mediadevices_test.go | 8 +- mediastreamconstraints.go | 4 +- pkg/prop/duration.go | 83 ++++++++++++++++++ pkg/prop/float.go | 82 ++++++++++++++++++ pkg/prop/format.go | 45 ++++++++++ pkg/prop/int.go | 82 ++++++++++++++++++ pkg/prop/prop.go | 128 ++++++++++++++++++++-------- pkg/prop/prop_test.go | 148 +++++++++++++++++++++++++++++++-- track.go | 16 ++-- 13 files changed, 558 insertions(+), 68 deletions(-) create mode 100644 pkg/prop/duration.go create mode 100644 pkg/prop/float.go create mode 100644 pkg/prop/format.go create mode 100644 pkg/prop/int.go diff --git a/examples/facedetection/main.go b/examples/facedetection/main.go index 1fb9d3b..7288229 100644 --- a/examples/facedetection/main.go +++ b/examples/facedetection/main.go @@ -11,6 +11,7 @@ import ( _ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter "github.com/pion/mediadevices/pkg/frame" "github.com/pion/mediadevices/pkg/io/video" + "github.com/pion/mediadevices/pkg/prop" "github.com/pion/webrtc/v2" ) @@ -66,10 +67,10 @@ func main() { s, err := md.GetUserMedia(mediadevices.MediaStreamConstraints{ Video: func(c *mediadevices.MediaTrackConstraints) { - c.FrameFormat = frame.FormatI420 // most of the encoder accepts I420 + c.FrameFormat = prop.FrameFormatExact(frame.FormatI420) // most of the encoder accepts I420 c.Enabled = true - c.Width = 640 - c.Height = 480 + c.Width = prop.Int(640) + c.Height = prop.Int(480) c.VideoTransform = markFacesTransformer c.VideoEncoderBuilders = []codec.VideoEncoderBuilder{&vp8Params} }, diff --git a/examples/rtp-send/main.go b/examples/rtp-send/main.go index 588550c..b8a38a4 100644 --- a/examples/rtp-send/main.go +++ b/examples/rtp-send/main.go @@ -10,6 +10,7 @@ import ( "github.com/pion/mediadevices/pkg/codec/vpx" // This is required to use VP8/VP9 video encoder _ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter "github.com/pion/mediadevices/pkg/frame" + "github.com/pion/mediadevices/pkg/prop" "github.com/pion/rtp" "github.com/pion/webrtc/v2" "github.com/pion/webrtc/v2/pkg/media" @@ -48,10 +49,10 @@ func main() { _, err = md.GetUserMedia(mediadevices.MediaStreamConstraints{ Video: func(c *mediadevices.MediaTrackConstraints) { - c.FrameFormat = frame.FormatYUY2 + c.FrameFormat = prop.FrameFormat(frame.FormatYUY2) c.Enabled = true - c.Width = 640 - c.Height = 480 + c.Width = prop.Int(640) + c.Height = prop.Int(480) c.VideoEncoderBuilders = []codec.VideoEncoderBuilder{&vp8Params} }, }) diff --git a/examples/simple/main.go b/examples/simple/main.go index a3c877f..e8372f0 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -7,6 +7,7 @@ import ( "github.com/pion/mediadevices/examples/internal/signal" "github.com/pion/mediadevices/pkg/codec" "github.com/pion/mediadevices/pkg/frame" + "github.com/pion/mediadevices/pkg/prop" "github.com/pion/webrtc/v2" // This is required to use opus audio encoder @@ -80,10 +81,10 @@ func main() { c.AudioEncoderBuilders = []codec.AudioEncoderBuilder{&opusParams} }, Video: func(c *mediadevices.MediaTrackConstraints) { - c.FrameFormat = frame.FormatYUY2 + c.FrameFormat = prop.FrameFormat(frame.FormatYUY2) c.Enabled = true - c.Width = 640 - c.Height = 480 + c.Width = prop.Int(640) + c.Height = prop.Int(480) c.VideoEncoderBuilders = []codec.VideoEncoderBuilder{&vp8Params} }, }) diff --git a/mediadevices.go b/mediadevices.go index 1fd9644..1462df5 100644 --- a/mediadevices.go +++ b/mediadevices.go @@ -200,7 +200,11 @@ func selectBestDriver(filter driver.FilterFn, constraints MediaTrackConstraints) for d, props := range driverProperties { priority := float64(d.Info().Priority) for _, p := range props { - fitnessDist := constraints.Media.FitnessDistance(p) - priority + fitnessDist, ok := constraints.MediaConstraints.FitnessDistance(p) + if !ok { + continue + } + fitnessDist -= priority if fitnessDist < minFitnessDist { minFitnessDist = fitnessDist bestDriver = d @@ -213,7 +217,8 @@ func selectBestDriver(filter driver.FilterFn, constraints MediaTrackConstraints) return nil, MediaTrackConstraints{}, errNotFound } - constraints.Merge(bestProp) + constraints.selectedMedia = bestProp + constraints.selectedMedia.Merge(constraints.MediaConstraints) return bestDriver, constraints, nil } diff --git a/mediadevices_test.go b/mediadevices_test.go index 825b0c8..e771bcb 100644 --- a/mediadevices_test.go +++ b/mediadevices_test.go @@ -50,8 +50,8 @@ func TestGetUserMedia(t *testing.T) { constraints := MediaStreamConstraints{ Video: func(c *MediaTrackConstraints) { c.Enabled = true - c.Width = 640 - c.Height = 480 + c.Width = prop.Int(640) + c.Height = prop.Int(480) params := videoParams c.VideoEncoderBuilders = []codec.VideoEncoderBuilder{¶ms} }, @@ -64,8 +64,8 @@ func TestGetUserMedia(t *testing.T) { constraintsWrong := MediaStreamConstraints{ Video: func(c *MediaTrackConstraints) { c.Enabled = true - c.Width = 640 - c.Height = 480 + c.Width = prop.Int(640) + c.Height = prop.Int(480) params := videoParams params.BitRate = 0 c.VideoEncoderBuilders = []codec.VideoEncoderBuilder{¶ms} diff --git a/mediastreamconstraints.go b/mediastreamconstraints.go index c01c911..8e3e429 100644 --- a/mediastreamconstraints.go +++ b/mediastreamconstraints.go @@ -14,7 +14,7 @@ type MediaStreamConstraints struct { // MediaTrackConstraints represents https://w3c.github.io/mediacapture-main/#dom-mediatrackconstraints type MediaTrackConstraints struct { - prop.Media + prop.MediaConstraints Enabled bool // VideoEncoderBuilders are codec builders that are used for encoding the video // and later being used for sending the appropriate RTP payload type. @@ -34,6 +34,8 @@ type MediaTrackConstraints struct { // AudioTransform will be used to transform the audio that's coming from the driver. // So, basically it'll look like following: driver -> AudioTransform -> code AudioTransform audio.TransformFunc + + selectedMedia prop.Media } type MediaOption func(*MediaTrackConstraints) diff --git a/pkg/prop/duration.go b/pkg/prop/duration.go new file mode 100644 index 0000000..14c929c --- /dev/null +++ b/pkg/prop/duration.go @@ -0,0 +1,83 @@ +package prop + +import ( + "math" + "time" +) + +type DurationConstraint interface { + Compare(time.Duration) (float64, bool) + Value() (time.Duration, bool) +} + +type Duration time.Duration + +func (d Duration) Compare(a time.Duration) (float64, bool) { + return math.Abs(float64(a-time.Duration(d))) / math.Max(math.Abs(float64(a)), math.Abs(float64(d))), true +} + +func (d Duration) Value() (time.Duration, bool) { return time.Duration(d), true } + +type DurationExact time.Duration + +func (d DurationExact) Compare(a time.Duration) (float64, bool) { + if time.Duration(d) == a { + return 0.0, true + } + return 1.0, false +} + +func (d DurationExact) Value() (time.Duration, bool) { return time.Duration(d), true } + +type DurationOneOf []time.Duration + +func (d DurationOneOf) Compare(a time.Duration) (float64, bool) { + for _, ii := range d { + if ii == a { + return 0.0, true + } + } + return 1.0, false +} + +func (DurationOneOf) Value() (time.Duration, bool) { return 0, false } + +type DurationRanged struct { + Min time.Duration + Max time.Duration + Ideal time.Duration +} + +func (d DurationRanged) Compare(a time.Duration) (float64, bool) { + if d.Min != 0 && d.Min > a { + // Out of range + return 1.0, false + } + if d.Max != 0 && d.Max < a { + // Out of range + return 1.0, false + } + if d.Ideal == 0 { + // If the value is in the range and Ideal is not specified, + // any value is evenly acceptable. + return 0.0, true + } + switch { + case a == d.Ideal: + return 0.0, true + case a < d.Ideal: + if d.Min == 0 { + // If Min is not specified, smaller values than Ideal are even. + return 0.0, true + } + return float64(d.Ideal-a) / float64(d.Ideal-d.Min), true + default: + if d.Max == 0 { + // If Max is not specified, larger values than Ideal are even. + return 0.0, true + } + return float64(a-d.Ideal) / float64(d.Max-d.Ideal), true + } +} + +func (DurationRanged) Value() (time.Duration, bool) { return 0, false } diff --git a/pkg/prop/float.go b/pkg/prop/float.go new file mode 100644 index 0000000..d0c4606 --- /dev/null +++ b/pkg/prop/float.go @@ -0,0 +1,82 @@ +package prop + +import ( + "math" +) + +type FloatConstraint interface { + Compare(float32) (float64, bool) + Value() (float32, bool) +} + +type Float float32 + +func (f Float) Compare(a float32) (float64, bool) { + return math.Abs(float64(a-float32(f))) / math.Max(math.Abs(float64(a)), math.Abs(float64(f))), true +} + +func (f Float) Value() (float32, bool) { return float32(f), true } + +type FloatExact float32 + +func (f FloatExact) Compare(a float32) (float64, bool) { + if float32(f) == a { + return 0.0, true + } + return 1.0, false +} + +func (f FloatExact) Value() (float32, bool) { return float32(f), true } + +type FloatOneOf []float32 + +func (f FloatOneOf) Compare(a float32) (float64, bool) { + for _, ff := range f { + if ff == a { + return 0.0, true + } + } + return 1.0, false +} + +func (FloatOneOf) Value() (float32, bool) { return 0, false } + +type FloatRanged struct { + Min float32 + Max float32 + Ideal float32 +} + +func (f FloatRanged) Compare(a float32) (float64, bool) { + if f.Min != 0 && f.Min > a { + // Out of range + return 1.0, false + } + if f.Max != 0 && f.Max < a { + // Out of range + return 1.0, false + } + if f.Ideal == 0 { + // If the value is in the range and Ideal is not specified, + // any value is evenly acceptable. + return 0.0, true + } + switch { + case a == f.Ideal: + return 0.0, true + case a < f.Ideal: + if f.Min == 0 { + // If Min is not specified, smaller values than Ideal are even. + return 0.0, true + } + return float64(f.Ideal-a) / float64(f.Ideal-f.Min), true + default: + if f.Max == 0 { + // If Max is not specified, larger values than Ideal are even. + return 0.0, true + } + return float64(a-f.Ideal) / float64(f.Max-f.Ideal), true + } +} + +func (FloatRanged) Value() (float32, bool) { return 0, false } diff --git a/pkg/prop/format.go b/pkg/prop/format.go new file mode 100644 index 0000000..d0a21f9 --- /dev/null +++ b/pkg/prop/format.go @@ -0,0 +1,45 @@ +package prop + +import ( + "github.com/pion/mediadevices/pkg/frame" +) + +type FrameFormatConstraint interface { + Compare(frame.Format) (float64, bool) + Value() (frame.Format, bool) +} + +type FrameFormat frame.Format + +func (f FrameFormat) Compare(a frame.Format) (float64, bool) { + if frame.Format(f) == a { + return 0.0, true + } + return 1.0, true +} + +func (f FrameFormat) Value() (frame.Format, bool) { return frame.Format(f), true } + +type FrameFormatExact frame.Format + +func (f FrameFormatExact) Compare(a frame.Format) (float64, bool) { + if frame.Format(f) == a { + return 0.0, true + } + return 1.0, false +} + +func (f FrameFormatExact) Value() (frame.Format, bool) { return frame.Format(f), true } + +type FrameFormatOneOf []frame.Format + +func (f FrameFormatOneOf) Compare(a frame.Format) (float64, bool) { + for _, ff := range f { + if ff == a { + return 0.0, true + } + } + return 1.0, false +} + +func (FrameFormatOneOf) Value() (frame.Format, bool) { return "", false } diff --git a/pkg/prop/int.go b/pkg/prop/int.go new file mode 100644 index 0000000..de5edbb --- /dev/null +++ b/pkg/prop/int.go @@ -0,0 +1,82 @@ +package prop + +import ( + "math" +) + +type IntConstraint interface { + Compare(int) (float64, bool) + Value() (int, bool) +} + +type Int int + +func (i Int) Compare(a int) (float64, bool) { + return math.Abs(float64(a-int(i))) / math.Max(math.Abs(float64(a)), math.Abs(float64(i))), true +} + +func (i Int) Value() (int, bool) { return int(i), true } + +type IntExact int + +func (i IntExact) Compare(a int) (float64, bool) { + if int(i) == a { + return 0.0, true + } + return 1.0, false +} + +func (i IntExact) Value() (int, bool) { return int(i), true } + +type IntOneOf []int + +func (i IntOneOf) Compare(a int) (float64, bool) { + for _, ii := range i { + if ii == a { + return 0.0, true + } + } + return 1.0, false +} + +func (IntOneOf) Value() (int, bool) { return 0, false } + +type IntRanged struct { + Min int + Max int + Ideal int +} + +func (i IntRanged) Compare(a int) (float64, bool) { + if i.Min != 0 && i.Min > a { + // Out of range + return 1.0, false + } + if i.Max != 0 && i.Max < a { + // Out of range + return 1.0, false + } + if i.Ideal == 0 { + // If the value is in the range and Ideal is not specified, + // any value is evenly acceptable. + return 0.0, true + } + switch { + case a == i.Ideal: + return 0.0, true + case a < i.Ideal: + if i.Min == 0 { + // If Min is not specified, smaller values than Ideal are even. + return 0.0, true + } + return float64(i.Ideal-a) / float64(i.Ideal-i.Min), true + default: + if i.Max == 0 { + // If Max is not specified, larger values than Ideal are even. + return 0.0, true + } + return float64(a-i.Ideal) / float64(i.Max-i.Ideal), true + } +} + +func (IntRanged) Value() (int, bool) { return 0, false } diff --git a/pkg/prop/prop.go b/pkg/prop/prop.go index 80bf1b5..135696e 100644 --- a/pkg/prop/prop.go +++ b/pkg/prop/prop.go @@ -1,15 +1,18 @@ package prop import ( - "fmt" - "math" "reflect" - "strconv" "time" "github.com/pion/mediadevices/pkg/frame" ) +type MediaConstraints struct { + DeviceID string + VideoConstraints + AudioConstraints +} + type Media struct { DeviceID string Video @@ -17,7 +20,7 @@ type Media struct { } // Merge merges all the field values from o to p, except zero values. -func (p *Media) Merge(o Media) { +func (p *Media) Merge(o MediaConstraints) { rp := reflect.ValueOf(p).Elem() ro := reflect.ValueOf(o) @@ -29,9 +32,9 @@ func (p *Media) Merge(o Media) { fieldA := a.Field(i) fieldB := b.Field(i) - // if a is a struct, b is also a struct. Then, + // if b is a struct, a is also a struct. Then, // we recursively merge them - if fieldA.Kind() == reflect.Struct { + if fieldB.Kind() == reflect.Struct { merge(fieldA, fieldB) continue } @@ -43,67 +46,122 @@ func (p *Media) Merge(o Media) { continue } - fieldA.Set(fieldB) + switch c := fieldB.Interface().(type) { + case IntConstraint: + if v, ok := c.Value(); ok { + fieldA.Set(reflect.ValueOf(v)) + } + case FloatConstraint: + if v, ok := c.Value(); ok { + fieldA.Set(reflect.ValueOf(v)) + } + case DurationConstraint: + if v, ok := c.Value(); ok { + fieldA.Set(reflect.ValueOf(v)) + } + case FrameFormatConstraint: + if v, ok := c.Value(); ok { + fieldA.Set(reflect.ValueOf(v)) + } + default: + panic("unsupported property type") + } } } merge(rp, ro) } -func (p *Media) FitnessDistance(o Media) float64 { +func (p *MediaConstraints) FitnessDistance(o Media) (float64, bool) { cmps := comparisons{} cmps.add(p.Width, o.Width) cmps.add(p.Height, o.Height) cmps.add(p.FrameFormat, o.FrameFormat) cmps.add(p.SampleRate, o.SampleRate) cmps.add(p.Latency, o.Latency) + return cmps.fitnessDistance() } -type comparisons map[string]string +type comparisons []struct { + desired, actual interface{} +} -func (c comparisons) add(actual, ideal interface{}) { - c[fmt.Sprint(actual)] = fmt.Sprint(ideal) +func (c *comparisons) add(desired, actual interface{}) { + if desired != nil { + *c = append(*c, + struct{ desired, actual interface{} }{ + desired, actual, + }, + ) + } } // fitnessDistance is an implementation for https://w3c.github.io/mediacapture-main/#dfn-fitness-distance -func (c comparisons) fitnessDistance() float64 { +func (c *comparisons) fitnessDistance() (float64, bool) { var dist float64 - - for actual, ideal := range c { - if actual == ideal { - continue - } - - actualF, err1 := strconv.ParseFloat(actual, 64) - idealF, err2 := strconv.ParseFloat(ideal, 64) - - switch { - // If both of the values are numeric, we need to normalize the values to get the distance - case err1 == nil && err2 == nil: - dist += math.Abs(actualF-idealF) / math.Max(math.Abs(actualF), math.Abs(idealF)) - // If both of the values are not numeric, the only comparison value is either 0 (matched) or 1 (not matched) - case err1 != nil && err2 != nil: - if actual != ideal { - dist++ + for _, field := range *c { + var d float64 + var ok bool + switch c := field.desired.(type) { + case IntConstraint: + if actual, typeOK := field.actual.(int); typeOK { + d, ok = c.Compare(actual) + } else { + panic("wrong type of actual value") + } + case FloatConstraint: + if actual, typeOK := field.actual.(float32); typeOK { + d, ok = c.Compare(actual) + } else { + panic("wrong type of actual value") + } + case DurationConstraint: + if actual, typeOK := field.actual.(time.Duration); typeOK { + d, ok = c.Compare(actual) + } else { + panic("wrong type of actual value") + } + case FrameFormatConstraint: + if actual, typeOK := field.actual.(frame.Format); typeOK { + d, ok = c.Compare(actual) + } else { + panic("wrong type of actual value") } - // Comparing a numeric value with a non-numeric value is a an internal error, so panic. default: - panic("fitnessDistance can't mix comparisons.") + panic("unsupported constraint type") + } + dist += d + if !ok { + return 0, false } } - - return dist + return dist, true } -// Video represents a video's properties +// VideoConstraints represents a video's constraints +type VideoConstraints struct { + Width, Height IntConstraint + FrameRate FloatConstraint + FrameFormat FrameFormatConstraint +} + +// Video represents a video's constraints type Video struct { Width, Height int FrameRate float32 FrameFormat frame.Format } -// Audio represents an audio's properties +// AudioConstraints represents an audio's constraints +type AudioConstraints struct { + ChannelCount IntConstraint + Latency DurationConstraint + SampleRate IntConstraint + SampleSize IntConstraint +} + +// Audio represents an audio's constraints type Audio struct { ChannelCount int Latency time.Duration diff --git a/pkg/prop/prop_test.go b/pkg/prop/prop_test.go index 6264704..eda2a7a 100644 --- a/pkg/prop/prop_test.go +++ b/pkg/prop/prop_test.go @@ -2,8 +2,138 @@ package prop import ( "testing" + "time" + + "github.com/pion/mediadevices/pkg/frame" ) +func TestCompareMatch(t *testing.T) { + testDataSet := map[string]struct { + a MediaConstraints + b Media + match bool + }{ + "IntIdealUnmatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + Width: Int(30), + }}, + Media{Video: Video{ + Width: 50, + }}, + true, + }, + "IntIdealMatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + Width: Int(30), + }}, + Media{Video: Video{ + Width: 30, + }}, + true, + }, + "IntExactUnmatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + Width: IntExact(30), + }}, + Media{Video: Video{ + Width: 50, + }}, + false, + }, + "IntExactMatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + Width: IntExact(30), + }}, + Media{Video: Video{ + Width: 30, + }}, + true, + }, + "IntRangeUnmatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + Width: IntRanged{Min: 30, Max: 40}, + }}, + Media{Video: Video{ + Width: 50, + }}, + false, + }, + "IntRangeMatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + Width: IntRanged{Min: 30, Max: 40}, + }}, + Media{Video: Video{ + Width: 35, + }}, + true, + }, + "FrameFormatOneOfUnmatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + FrameFormat: FrameFormatOneOf{frame.FormatYUYV, frame.FormatUYVY}, + }}, + Media{Video: Video{ + FrameFormat: frame.FormatYUYV, + }}, + true, + }, + "FrameFormatOneOfMatch": { + MediaConstraints{VideoConstraints: VideoConstraints{ + FrameFormat: FrameFormatOneOf{frame.FormatYUYV, frame.FormatUYVY}, + }}, + Media{Video: Video{ + FrameFormat: frame.FormatMJPEG, + }}, + false, + }, + "DurationExactUnmatch": { + MediaConstraints{AudioConstraints: AudioConstraints{ + Latency: DurationExact(time.Second), + }}, + Media{Audio: Audio{ + Latency: time.Second + time.Millisecond, + }}, + false, + }, + "DurationExactMatch": { + MediaConstraints{AudioConstraints: AudioConstraints{ + Latency: DurationExact(time.Second), + }}, + Media{Audio: Audio{ + Latency: time.Second, + }}, + true, + }, + "DurationRangedUnmatch": { + MediaConstraints{AudioConstraints: AudioConstraints{ + Latency: DurationRanged{Max: time.Second}, + }}, + Media{Audio: Audio{ + Latency: time.Second + time.Millisecond, + }}, + false, + }, + "DurationRangedMatch": { + MediaConstraints{AudioConstraints: AudioConstraints{ + Latency: DurationRanged{Max: time.Second}, + }}, + Media{Audio: Audio{ + Latency: time.Millisecond, + }}, + true, + }, + } + + for name, testData := range testDataSet { + testData := testData + t.Run(name, func(t *testing.T) { + _, match := testData.a.FitnessDistance(testData.b) + if match != testData.match { + t.Errorf("matching flag differs, expected: %v, got: %v", testData.match, match) + } + }) + } +} + func TestMergeWithZero(t *testing.T) { a := Media{ Video: Video{ @@ -11,9 +141,9 @@ func TestMergeWithZero(t *testing.T) { }, } - b := Media{ - Video: Video{ - Height: 100, + b := MediaConstraints{ + VideoConstraints: VideoConstraints{ + Height: Int(100), }, } @@ -35,9 +165,9 @@ func TestMergeWithSameField(t *testing.T) { }, } - b := Media{ - Video: Video{ - Width: 100, + b := MediaConstraints{ + VideoConstraints: VideoConstraints{ + Width: Int(100), }, } @@ -61,9 +191,9 @@ func TestMergeNested(t *testing.T) { }, } - b := Media{ - Video: Video{ - Width: 100, + b := MediaConstraints{ + VideoConstraints: VideoConstraints{ + Width: Int(100), }, } diff --git a/track.go b/track.go index 620260a..f4e5ca8 100644 --- a/track.go +++ b/track.go @@ -1,7 +1,7 @@ package mediadevices import ( - "fmt" + "errors" "math/rand" "sync" @@ -62,11 +62,11 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack case driver.AudioRecorder: rtpCodecs = opts.codecs[webrtc.RTPCodecTypeAudio] buildSampler = func(t LocalTrack) samplerFunc { - return newAudioSampler(t, constraints.Latency) + return newAudioSampler(t, constraints.selectedMedia.Latency) } encoderBuilders, err = newAudioEncoderBuilders(r, constraints) default: - err = fmt.Errorf("newTrack: invalid driver type") + err = errors.New("newTrack: invalid driver type") } if err != nil { @@ -114,7 +114,7 @@ func newTrack(opts *MediaDevicesOptions, d driver.Driver, constraints MediaTrack } d.Close() - return nil, fmt.Errorf("newTrack: failed to find a matching codec") + return nil, errors.New("newTrack: failed to find a matching codec") } // OnEnded sets an error handler. When a track has been created and started, if an @@ -196,7 +196,7 @@ type encoderBuilder struct { // newVideoEncoderBuilders transforms video given by VideoRecorder with the video transformer that is passed through // constraints and create a list of generic encoder builders func newVideoEncoderBuilders(vr driver.VideoRecorder, constraints MediaTrackConstraints) ([]encoderBuilder, error) { - r, err := vr.VideoRecord(constraints.Media) + r, err := vr.VideoRecord(constraints.selectedMedia) if err != nil { return nil, err } @@ -209,7 +209,7 @@ func newVideoEncoderBuilders(vr driver.VideoRecorder, constraints MediaTrackCons for i, b := range constraints.VideoEncoderBuilders { encoderBuilders[i].name = b.Name() encoderBuilders[i].build = func() (codec.ReadCloser, error) { - return b.BuildVideoEncoder(r, constraints.Media) + return b.BuildVideoEncoder(r, constraints.selectedMedia) } } return encoderBuilders, nil @@ -218,7 +218,7 @@ func newVideoEncoderBuilders(vr driver.VideoRecorder, constraints MediaTrackCons // newAudioEncoderBuilders transforms audio given by AudioRecorder with the audio transformer that is passed through // constraints and create a list of generic encoder builders func newAudioEncoderBuilders(ar driver.AudioRecorder, constraints MediaTrackConstraints) ([]encoderBuilder, error) { - r, err := ar.AudioRecord(constraints.Media) + r, err := ar.AudioRecord(constraints.selectedMedia) if err != nil { return nil, err } @@ -231,7 +231,7 @@ func newAudioEncoderBuilders(ar driver.AudioRecorder, constraints MediaTrackCons for i, b := range constraints.AudioEncoderBuilders { encoderBuilders[i].name = b.Name() encoderBuilders[i].build = func() (codec.ReadCloser, error) { - return b.BuildAudioEncoder(r, constraints.Media) + return b.BuildAudioEncoder(r, constraints.selectedMedia) } } return encoderBuilders, nil