diff --git a/examples/simple/main.go b/examples/simple/main.go index 87d91ff..1816e25 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -52,6 +52,7 @@ func main() { Audio: func(c *mediadevices.MediaTrackConstraints) { c.Codec = webrtc.Opus c.Enabled = true + c.BitRate = 32000 // 32kbps }, Video: func(c *mediadevices.MediaTrackConstraints) { c.Codec = videoCodecName @@ -59,6 +60,7 @@ func main() { c.Enabled = true c.Width = 640 c.Height = 480 + c.BitRate = 100000 // 100kbps }, }) if err != nil { diff --git a/pkg/codec/codec.go b/pkg/codec/codec.go index be600ae..0d83f8f 100644 --- a/pkg/codec/codec.go +++ b/pkg/codec/codec.go @@ -8,5 +8,5 @@ import ( "github.com/pion/mediadevices/pkg/prop" ) -type VideoEncoderBuilder func(r video.Reader, p prop.Video) (io.ReadCloser, error) -type AudioEncoderBuilder func(r audio.Reader, p prop.Audio) (io.ReadCloser, error) +type VideoEncoderBuilder func(r video.Reader, p prop.Media) (io.ReadCloser, error) +type AudioEncoderBuilder func(r audio.Reader, p prop.Media) (io.ReadCloser, error) diff --git a/pkg/codec/openh264/openh264.go b/pkg/codec/openh264/openh264.go index 891b697..4a2e5a3 100644 --- a/pkg/codec/openh264/openh264.go +++ b/pkg/codec/openh264/openh264.go @@ -35,7 +35,11 @@ func init() { codec.Register(webrtc.H264, codec.VideoEncoderBuilder(NewEncoder)) } -func NewEncoder(r video.Reader, p prop.Video) (io.ReadCloser, error) { +func NewEncoder(r video.Reader, p prop.Media) (io.ReadCloser, error) { + if p.BitRate == 0 { + p.BitRate = 100000 + } + cEncoder, err := C.enc_new(C.EncoderOptions{ width: C.int(p.Width), height: C.int(p.Height), diff --git a/pkg/codec/opus/opus.go b/pkg/codec/opus/opus.go index e883196..b5f5025 100644 --- a/pkg/codec/opus/opus.go +++ b/pkg/codec/opus/opus.go @@ -29,7 +29,7 @@ func init() { codec.Register(webrtc.Opus, codec.AudioEncoderBuilder(NewEncoder)) } -func NewEncoder(r audio.Reader, p prop.Audio) (io.ReadCloser, error) { +func NewEncoder(r audio.Reader, p prop.Media) (io.ReadCloser, error) { if p.SampleRate == 0 { return nil, fmt.Errorf("opus: inProp.SampleRate is required") } @@ -38,6 +38,10 @@ func NewEncoder(r audio.Reader, p prop.Audio) (io.ReadCloser, error) { p.Latency = 20 } + if p.BitRate == 0 { + p.BitRate = 32000 + } + // Select the nearest supported latency var targetLatency float64 latencyInMS := float64(p.Latency.Milliseconds()) @@ -59,6 +63,9 @@ func NewEncoder(r audio.Reader, p prop.Audio) (io.ReadCloser, error) { if err != nil { return nil, err } + if err := engine.SetBitrate(p.BitRate); err != nil { + return nil, err + } inBuffSize := targetLatency * float64(p.SampleRate) / 1000 inBuff := make([][2]float32, int(inBuffSize)) diff --git a/pkg/codec/registrar.go b/pkg/codec/registrar.go index a381417..f1cae5a 100644 --- a/pkg/codec/registrar.go +++ b/pkg/codec/registrar.go @@ -23,7 +23,7 @@ func Register(name string, builder interface{}) { } } -func BuildVideoEncoder(name string, r video.Reader, p prop.Video) (io.ReadCloser, error) { +func BuildVideoEncoder(name string, r video.Reader, p prop.Media) (io.ReadCloser, error) { b, ok := videoEncoders[name] if !ok { return nil, fmt.Errorf("codec: can't find %s video encoder", name) @@ -32,7 +32,7 @@ func BuildVideoEncoder(name string, r video.Reader, p prop.Video) (io.ReadCloser return b(r, p) } -func BuildAudioEncoder(name string, r audio.Reader, p prop.Audio) (io.ReadCloser, error) { +func BuildAudioEncoder(name string, r audio.Reader, p prop.Media) (io.ReadCloser, error) { b, ok := audioEncoders[name] if !ok { return nil, fmt.Errorf("codec: can't find %s audio encoder", name) diff --git a/pkg/codec/vpx/vpx.go b/pkg/codec/vpx/vpx.go index 1acb123..85bbbb3 100644 --- a/pkg/codec/vpx/vpx.go +++ b/pkg/codec/vpx/vpx.go @@ -63,16 +63,24 @@ func init() { } // NewVP8Encoder creates new VP8 encoder -func NewVP8Encoder(r video.Reader, p prop.Video) (io.ReadCloser, error) { +func NewVP8Encoder(r video.Reader, p prop.Media) (io.ReadCloser, error) { return newEncoder(r, p, C.ifaceVP8()) } // NewVP9Encoder creates new VP9 encoder -func NewVP9Encoder(r video.Reader, p prop.Video) (io.ReadCloser, error) { +func NewVP9Encoder(r video.Reader, p prop.Media) (io.ReadCloser, error) { return newEncoder(r, p, C.ifaceVP9()) } -func newEncoder(r video.Reader, p prop.Video, codecIface *C.vpx_codec_iface_t) (io.ReadCloser, error) { +func newEncoder(r video.Reader, p prop.Media, codecIface *C.vpx_codec_iface_t) (io.ReadCloser, error) { + if p.BitRate == 0 { + p.BitRate = 100000 + } + + if p.KeyFrameInterval == 0 { + p.KeyFrameInterval = 60 + } + cfg := &C.vpx_codec_enc_cfg_t{} if ec := C.vpx_codec_enc_config_default(codecIface, cfg, 0); ec != 0 { return nil, fmt.Errorf("vpx_codec_enc_config_default failed (%d)", ec) @@ -101,7 +109,7 @@ func newEncoder(r video.Reader, p prop.Video, codecIface *C.vpx_codec_iface_t) ( r: video.ToI420(r), codec: codec, raw: rawNoBuffer, - keyframeInterval: 30, // TODO: Set via prop.Video + keyframeInterval: p.KeyFrameInterval, }, nil } diff --git a/pkg/prop/prop.go b/pkg/prop/prop.go index 7f495aa..3280555 100644 --- a/pkg/prop/prop.go +++ b/pkg/prop/prop.go @@ -12,6 +12,7 @@ import ( type Media struct { Video Audio + Codec } func (p *Media) FitnessDistance(o Media) float64 { @@ -62,7 +63,6 @@ type Video struct { Width, Height int FrameRate float32 FrameFormat frame.Format - BitRate int } // Audio represents an audio's properties @@ -72,3 +72,17 @@ type Audio struct { SampleRate int SampleSize int } + +// Codec represents an codec's encoding properties +type Codec struct { + // Target bitrate in bps. + BitRate int + + // Quolity of the encoding [0-9]. + // Larger value results higher quality and higher CPU usage. + // It depends on the selected codec. + Quality int + + // Expected interval of the keyframes in frames. + KeyFrameInterval int +} diff --git a/track.go b/track.go index db1a59a..7cb1477 100644 --- a/track.go +++ b/track.go @@ -99,9 +99,7 @@ func newVideoTrack(codecs []*webrtc.RTPCodec, d driver.Driver, constraints Media return nil, err } - // TODO: Remove hardcoded bitrate - constraints.BitRate = 100000 - encoder, err := codec.BuildVideoEncoder(codecName, r, constraints.Video) + encoder, err := codec.BuildVideoEncoder(codecName, r, constraints.Media) if err != nil { return nil, err } @@ -172,7 +170,7 @@ func newAudioTrack(codecs []*webrtc.RTPCodec, d driver.Driver, constraints Media return nil, err } - encoder, err := codec.BuildAudioEncoder(codecName, reader, constraints.Audio) + encoder, err := codec.BuildAudioEncoder(codecName, reader, constraints.Media) if err != nil { return nil, err }