Repository: pion/mediadevices
Pull request #122 (2020-03-26 01:44:57)

Add Rate limiting interface
This is the initial step for supporting #119 


Patch:
diff --git a/go.sum b/go.sum
index adedd24..75a83ab 100644
--- a/go.sum
+++ b/go.sum
@@ -97,6 +97,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
diff --git a/pkg/codec/codec.go b/pkg/codec/codec.go
index 252a873..07f2e2c 100644
--- a/pkg/codec/codec.go
+++ b/pkg/codec/codec.go
@@ -17,7 +17,7 @@ type AudioEncoderBuilder interface {
 	// Name represents the codec name
 	Name() string
 	// BuildAudioEncoder builds audio encoder by given media params and audio input
-	BuildAudioEncoder(r audio.Reader, p prop.Media) (io.ReadCloser, error)
+	BuildAudioEncoder(r audio.Reader, p prop.Media) (ReadCloser, error)
 }
 
 // VideoEncoderBuilder is the interface that wraps basic operations that are
@@ -29,7 +29,17 @@ type VideoEncoderBuilder interface {
 	// Name represents the codec name
 	Name() string
 	// BuildVideoEncoder builds video encoder by given media params and video input
-	BuildVideoEncoder(r video.Reader, p prop.Media) (io.ReadCloser, error)
+	BuildVideoEncoder(r video.Reader, p prop.Media) (ReadCloser, error)
+}
+
+// ReadCloser is an io.ReadCloser with methods for rate limiting: SetBitRate and ForceKeyFrame
+type ReadCloser interface {
+	io.ReadCloser
+	// SetBitRate sets current target bitrate, lower bitrate means smaller data will be transmitted
+	// but this also means that the quality will also be lower.
+	SetBitRate(int) error
+	// ForceKeyFrame forces the next frame to be a keyframe, aka intra-frame.
+	ForceKeyFrame() error
 }
 
 // BaseParams represents an codec's encoding properties
diff --git a/pkg/codec/openh264/openh264.go b/pkg/codec/openh264/openh264.go
index 69dd3c3..d5b819e 100644
--- a/pkg/codec/openh264/openh264.go
+++ b/pkg/codec/openh264/openh264.go
@@ -15,6 +15,7 @@ import (
 	"sync"
 	"unsafe"
 
+	"github.com/pion/mediadevices/pkg/codec"
 	mio "github.com/pion/mediadevices/pkg/io"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -29,7 +30,7 @@ type encoder struct {
 	closed bool
 }
 
-func newEncoder(r video.Reader, p prop.Media, params Params) (io.ReadCloser, error) {
+func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
 	if params.BitRate == 0 {
 		params.BitRate = 100000
 	}
@@ -96,6 +97,14 @@ func (e *encoder) Read(p []byte) (n int, err error) {
 	return n, err
 }
 
+func (e *encoder) SetBitRate(b int) error {
+	panic("SetBitRate is not implemented")
+}
+
+func (e *encoder) ForceKeyFrame() error {
+	panic("ForceKeyFrame is not implemented")
+}
+
 func (e *encoder) Close() error {
 	e.mu.Lock()
 	defer e.mu.Unlock()
diff --git a/pkg/codec/openh264/params.go b/pkg/codec/openh264/params.go
index 0a2e962..7981fd6 100644
--- a/pkg/codec/openh264/params.go
+++ b/pkg/codec/openh264/params.go
@@ -1,8 +1,6 @@
 package openh264
 
 import (
-	"io"
-
 	"github.com/pion/mediadevices/pkg/codec"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -29,6 +27,6 @@ func (p *Params) Name() string {
 }
 
 // BuildVideoEncoder builds openh264 encoder with given params
-func (p *Params) BuildVideoEncoder(r video.Reader, property prop.Media) (io.ReadCloser, error) {
+func (p *Params) BuildVideoEncoder(r video.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newEncoder(r, property, *p)
 }
diff --git a/pkg/codec/opus/opus.go b/pkg/codec/opus/opus.go
index 5dd1b01..2662fdc 100644
--- a/pkg/codec/opus/opus.go
+++ b/pkg/codec/opus/opus.go
@@ -2,12 +2,12 @@ package opus
 
 import (
 	"fmt"
-	"io"
 	"math"
 	"reflect"
 	"unsafe"
 
 	"github.com/lherman-cs/opus"
+	"github.com/pion/mediadevices/pkg/codec"
 	"github.com/pion/mediadevices/pkg/io/audio"
 	"github.com/pion/mediadevices/pkg/prop"
 )
@@ -20,9 +20,7 @@ type encoder struct {
 
 var latencies = []float64{5, 10, 20, 40, 60}
 
-var _ io.ReadCloser = &encoder{}
-
-func newEncoder(r audio.Reader, p prop.Media, params Params) (io.ReadCloser, error) {
+func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
 	if p.SampleRate == 0 {
 		return nil, fmt.Errorf("opus: inProp.SampleRate is required")
 	}
@@ -98,6 +96,14 @@ func (e *encoder) Read(p []byte) (n int, err error) {
 	return n, nil
 }
 
+func (e *encoder) SetBitRate(b int) error {
+	panic("SetBitRate is not implemented")
+}
+
+func (e *encoder) ForceKeyFrame() error {
+	panic("ForceKeyFrame is not implemented")
+}
+
 func (e *encoder) Close() error {
 	return nil
 }
diff --git a/pkg/codec/opus/params.go b/pkg/codec/opus/params.go
index fad3440..0490362 100644
--- a/pkg/codec/opus/params.go
+++ b/pkg/codec/opus/params.go
@@ -1,8 +1,6 @@
 package opus
 
 import (
-	"io"
-
 	"github.com/pion/mediadevices/pkg/codec"
 	"github.com/pion/mediadevices/pkg/io/audio"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -24,7 +22,7 @@ func (p *Params) Name() string {
 	return webrtc.Opus
 }
 
-// BuildVideoEncoder builds x264 encoder with given params
-func (p *Params) BuildAudioEncoder(r audio.Reader, property prop.Media) (io.ReadCloser, error) {
+// BuildAudioEncoder builds opus encoder with given params
+func (p *Params) BuildAudioEncoder(r audio.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newEncoder(r, property, *p)
 }
diff --git a/pkg/codec/vaapi/params.go b/pkg/codec/vaapi/params.go
index 1c0ac2c..9b21efb 100644
--- a/pkg/codec/vaapi/params.go
+++ b/pkg/codec/vaapi/params.go
@@ -1,8 +1,6 @@
 package vaapi
 
 import (
-	"io"
-
 	"github.com/pion/mediadevices/pkg/codec"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -52,7 +50,7 @@ func (p *ParamsVP8) Name() string {
 }
 
 // BuildVideoEncoder builds VP8 encoder with given params
-func (p *ParamsVP8) BuildVideoEncoder(r video.Reader, property prop.Media) (io.ReadCloser, error) {
+func (p *ParamsVP8) BuildVideoEncoder(r video.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newVP8Encoder(r, property, *p)
 }
 
@@ -121,6 +119,6 @@ func (p *ParamsVP9) Name() string {
 }
 
 // BuildVideoEncoder builds VP9 encoder with given params
-func (p *ParamsVP9) BuildVideoEncoder(r video.Reader, property prop.Media) (io.ReadCloser, error) {
+func (p *ParamsVP9) BuildVideoEncoder(r video.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newVP9Encoder(r, property, *p)
 }
diff --git a/pkg/codec/vaapi/vp8.go b/pkg/codec/vaapi/vp8.go
index 25d7496..b89c30b 100644
--- a/pkg/codec/vaapi/vp8.go
+++ b/pkg/codec/vaapi/vp8.go
@@ -50,6 +50,7 @@ import (
 	"sync"
 	"unsafe"
 
+	"github.com/pion/mediadevices/pkg/codec"
 	mio "github.com/pion/mediadevices/pkg/io"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -92,7 +93,7 @@ type encoderVP8 struct {
 }
 
 // newVP8Encoder creates new VP8 encoder
-func newVP8Encoder(r video.Reader, p prop.Media, params ParamsVP8) (io.ReadCloser, error) {
+func newVP8Encoder(r video.Reader, p prop.Media, params ParamsVP8) (codec.ReadCloser, error) {
 	if p.Width%16 != 0 || p.Width == 0 {
 		return nil, errors.New("width must be 16*n")
 	}
@@ -496,6 +497,14 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
 	return n, err
 }
 
+func (e *encoderVP8) SetBitRate(b int) error {
+	panic("SetBitRate is not implemented")
+}
+
+func (e *encoderVP8) ForceKeyFrame() error {
+	panic("ForceKeyFrame is not implemented")
+}
+
 func (e *encoderVP8) Close() error {
 	e.mu.Lock()
 	defer e.mu.Unlock()
diff --git a/pkg/codec/vaapi/vp9.go b/pkg/codec/vaapi/vp9.go
index b9bc5d8..19bb39d 100644
--- a/pkg/codec/vaapi/vp9.go
+++ b/pkg/codec/vaapi/vp9.go
@@ -45,6 +45,7 @@ import (
 	"sync"
 	"unsafe"
 
+	"github.com/pion/mediadevices/pkg/codec"
 	mio "github.com/pion/mediadevices/pkg/io"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -96,7 +97,7 @@ type encoderVP9 struct {
 }
 
 // newVP9Encoder creates new VP9 encoder
-func newVP9Encoder(r video.Reader, p prop.Media, params ParamsVP9) (io.ReadCloser, error) {
+func newVP9Encoder(r video.Reader, p prop.Media, params ParamsVP9) (codec.ReadCloser, error) {
 	if p.Width%16 != 0 || p.Width == 0 {
 		return nil, errors.New("width must be 16*n")
 	}
@@ -485,6 +486,14 @@ func (e *encoderVP9) Read(p []byte) (int, error) {
 	return n, err
 }
 
+func (e *encoderVP9) SetBitRate(b int) error {
+	panic("SetBitRate is not implemented")
+}
+
+func (e *encoderVP9) ForceKeyFrame() error {
+	panic("ForceKeyFrame is not implemented")
+}
+
 func (e *encoderVP9) Close() error {
 	e.mu.Lock()
 	defer e.mu.Unlock()
diff --git a/pkg/codec/vpx/vpx.go b/pkg/codec/vpx/vpx.go
index 5c9b275..cd8ef64 100644
--- a/pkg/codec/vpx/vpx.go
+++ b/pkg/codec/vpx/vpx.go
@@ -55,10 +55,10 @@ import (
 	"time"
 	"unsafe"
 
+	"github.com/pion/mediadevices/pkg/codec"
 	mio "github.com/pion/mediadevices/pkg/io"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
-
 	"github.com/pion/webrtc/v2"
 )
 
@@ -100,7 +100,7 @@ func (p *VP8Params) Name() string {
 }
 
 // BuildVideoEncoder builds VP8 encoder with given params
-func (p *VP8Params) BuildVideoEncoder(r video.Reader, property prop.Media) (io.ReadCloser, error) {
+func (p *VP8Params) BuildVideoEncoder(r video.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newEncoder(r, property, p.Params, C.ifaceVP8())
 }
 
@@ -127,7 +127,7 @@ func (p *VP9Params) Name() string {
 }
 
 // BuildVideoEncoder builds VP9 encoder with given params
-func (p *VP9Params) BuildVideoEncoder(r video.Reader, property prop.Media) (io.ReadCloser, error) {
+func (p *VP9Params) BuildVideoEncoder(r video.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newEncoder(r, property, p.Params, C.ifaceVP9())
 }
 
@@ -145,7 +145,7 @@ func newParams(codecIface *C.vpx_codec_iface_t) (Params, error) {
 	}, nil
 }
 
-func newEncoder(r video.Reader, p prop.Media, params Params, codecIface *C.vpx_codec_iface_t) (io.ReadCloser, error) {
+func newEncoder(r video.Reader, p prop.Media, params Params, codecIface *C.vpx_codec_iface_t) (codec.ReadCloser, error) {
 	if params.BitRate == 0 {
 		params.BitRate = 100000
 	}
@@ -273,6 +273,14 @@ func (e *encoder) Read(p []byte) (int, error) {
 	return n, err
 }
 
+func (e *encoder) SetBitRate(b int) error {
+	panic("SetBitRate is not implemented")
+}
+
+func (e *encoder) ForceKeyFrame() error {
+	panic("ForceKeyFrame is not implemented")
+}
+
 func (e *encoder) Close() error {
 	e.mu.Lock()
 	defer e.mu.Unlock()
diff --git a/pkg/codec/x264/params.go b/pkg/codec/x264/params.go
index 153734f..01ed978 100644
--- a/pkg/codec/x264/params.go
+++ b/pkg/codec/x264/params.go
@@ -1,8 +1,6 @@
 package x264
 
 import (
-	"io"
-
 	"github.com/pion/mediadevices/pkg/codec"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -48,6 +46,6 @@ func (p *Params) Name() string {
 }
 
 // BuildVideoEncoder builds x264 encoder with given params
-func (p *Params) BuildVideoEncoder(r video.Reader, property prop.Media) (io.ReadCloser, error) {
+func (p *Params) BuildVideoEncoder(r video.Reader, property prop.Media) (codec.ReadCloser, error) {
 	return newEncoder(r, property, *p)
 }
diff --git a/pkg/codec/x264/x264.go b/pkg/codec/x264/x264.go
index f65242e..e48feb9 100644
--- a/pkg/codec/x264/x264.go
+++ b/pkg/codec/x264/x264.go
@@ -13,6 +13,7 @@ import (
 	"sync"
 	"unsafe"
 
+	"github.com/pion/mediadevices/pkg/codec"
 	mio "github.com/pion/mediadevices/pkg/io"
 	"github.com/pion/mediadevices/pkg/io/video"
 	"github.com/pion/mediadevices/pkg/prop"
@@ -61,7 +62,7 @@ var (
 	errEncode        = fmt.Errorf("failed to encode")
 )
 
-func newEncoder(r video.Reader, p prop.Media, params Params) (io.ReadCloser, error) {
+func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
 	if params.KeyFrameInterval == 0 {
 		params.KeyFrameInterval = 60
 	}
@@ -133,6 +134,14 @@ func (e *encoder) Read(p []byte) (int, error) {
 	return n, err
 }
 
+func (e *encoder) SetBitRate(b int) error {
+	panic("SetBitRate is not implemented")
+}
+
+func (e *encoder) ForceKeyFrame() error {
+	panic("ForceKeyFrame is not implemented")
+}
+
 func (e *encoder) Close() error {
 	e.mu.Lock()
 	defer e.mu.Unlock()
diff --git a/track.go b/track.go
index d3abf27..05b3dc3 100644
--- a/track.go
+++ b/track.go
@@ -2,10 +2,10 @@ package mediadevices
 
 import (
 	"fmt"
-	"io"
 	"math/rand"
 	"sync"
 
+	"github.com/pion/mediadevices/pkg/codec"
 	"github.com/pion/mediadevices/pkg/driver"
 	mio "github.com/pion/mediadevices/pkg/io"
 	"github.com/pion/webrtc/v2"
@@ -102,7 +102,7 @@ type videoTrack struct {
 	*track
 	d           driver.Driver
 	constraints MediaTrackConstraints
-	encoder     io.ReadCloser
+	encoder     codec.ReadCloser
 }
 
 var _ Tracker = &videoTrack{}
@@ -198,7 +198,7 @@ type audioTrack struct {
 	*track
 	d           driver.Driver
 	constraints MediaTrackConstraints
-	encoder     io.ReadCloser
+	encoder     codec.ReadCloser
 }
 
 var _ Tracker = &audioTrack{}


Test patch:
diff --git a/mediadevices_test.go b/mediadevices_test.go
index 6bc18d1..7730638 100644
--- a/mediadevices_test.go
+++ b/mediadevices_test.go
@@ -161,7 +161,7 @@ func (params *mockParams) Name() string {
 	return params.name
 }
 
-func (params *mockParams) BuildVideoEncoder(r video.Reader, p prop.Media) (io.ReadCloser, error) {
+func (params *mockParams) BuildVideoEncoder(r video.Reader, p prop.Media) (codec.ReadCloser, error) {
 	if params.BitRate == 0 {
 		// This is a dummy error to test the failure condition.
 		return nil, errors.New("wrong codec parameter")
@@ -172,14 +172,25 @@ func (params *mockParams) BuildVideoEncoder(r video.Reader, p prop.Media) (io.Re
 	}, nil
 }
 
-func (params *mockParams) BuildAudioEncoder(r audio.Reader, p prop.Media) (io.ReadCloser, error) {
+func (params *mockParams) BuildAudioEncoder(r audio.Reader, p prop.Media) (codec.ReadCloser, error) {
 	return &mockAudioCodec{
 		r:      r,
 		closed: make(chan struct{}),
 	}, nil
 }
 
+type mockCodec struct{}
+
+func (e *mockCodec) SetBitRate(b int) error {
+	return nil
+}
+
+func (e *mockCodec) ForceKeyFrame() error {
+	return nil
+}
+
 type mockVideoCodec struct {
+	mockCodec
 	r      video.Reader
 	closed chan struct{}
 }
@@ -190,9 +201,11 @@ func (m *mockVideoCodec) Read(b []byte) (int, error) {
 	}
 	return len(b), nil
 }
+
 func (m *mockVideoCodec) Close() error { return nil }
 
 type mockAudioCodec struct {
+	mockCodec
 	r      audio.Reader
 	closed chan struct{}
 }