Skip to content

Commit

Permalink
Implement Interceptors
Browse files Browse the repository at this point in the history
Provide API so that handling around RTP can be easily defined by the
user. See the design doc here[0]

[0] pion/webrtc-v3-design#34
  • Loading branch information
masterada committed Nov 20, 2020
1 parent d0b119a commit c5da1c6
Show file tree
Hide file tree
Showing 17 changed files with 721 additions and 72 deletions.
13 changes: 13 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type API struct {
settingEngine *SettingEngine
mediaEngine *MediaEngine
interceptor Interceptor
}

// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
Expand All @@ -35,6 +36,10 @@ func NewAPI(options ...func(*API)) *API {
a.mediaEngine = &MediaEngine{}
}

if a.interceptor == nil {
a.interceptor = &NoOpInterceptor{}
}

return a
}

Expand All @@ -57,3 +62,11 @@ func WithSettingEngine(s SettingEngine) func(a *API) {
a.settingEngine = &s
}
}

// WithInterceptorRegistry allows providing Interceptors to the API.
// Settings should not be changed after passing the registry to an API.
func WithInterceptorRegistry(interceptorRegistry *InterceptorRegistry) func(a *API) {
return func(a *API) {
a.interceptor = interceptorRegistry.build()
}
}
8 changes: 7 additions & 1 deletion examples/save-to-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/pion/rtcp"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/examples/internal/signal"
"github.com/pion/webrtc/v3/pkg/interceptor"
"github.com/pion/webrtc/v3/pkg/media"
"github.com/pion/webrtc/v3/pkg/media/ivfwriter"
"github.com/pion/webrtc/v3/pkg/media/oggwriter"
Expand Down Expand Up @@ -54,8 +55,13 @@ func main() {
panic(err)
}

ir := &webrtc.InterceptorRegistry{}
if err := interceptor.RegisterDefaults(&m, ir); err != nil {
panic(err)
}

// Create the API object with the MediaEngine
api := webrtc.NewAPI(webrtc.WithMediaEngine(&m))
api := webrtc.NewAPI(webrtc.WithMediaEngine(&m), webrtc.WithInterceptorRegistry(ir))

// Prepare the configuration
config := webrtc.Configuration{
Expand Down
90 changes: 90 additions & 0 deletions interceptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// +build !js

package webrtc

import (
"io"

"github.com/pion/rtcp"
"github.com/pion/rtp"
)

// WriteRTP is used by Interceptor.BindLocalTrack.
type WriteRTP func(p *rtp.Packet, attributes map[interface{}]interface{}) (int, error)

// ReadRTP is used by Interceptor.BindRemoteTrack.
type ReadRTP func() (*rtp.Packet, map[interface{}]interface{}, error)

// WriteRTCP is used by Interceptor.BindWriteRTCP.
type WriteRTCP func(pkts []rtcp.Packet, attributes map[interface{}]interface{}) (int, error)

// ReadRTCP is used by Interceptor.BindReadRTCP.
type ReadRTCP func() ([]rtcp.Packet, map[interface{}]interface{}, error)

// Interceptor can be used to add functionality to you PeerConnections by modifying any incoming/outgoing rtp/rtcp
// packets, or sending your own packets as needed.
type Interceptor interface {

// BindReadRTCP lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
BindReadRTCP(read ReadRTCP) ReadRTCP

// BindWriteRTCP lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
BindWriteRTCP(write WriteRTCP) WriteRTCP

// BindLocalTrack lets you modify any outgoing RTP packets. It is called once for per LocalTrack. The returned method
// will be called once per rtp packet.
BindLocalTrack(ctx *TrackLocalContext, write WriteRTP) WriteRTP

// UnbindLocalTrack is called when the Track is removed. It can be used to clean up any data related to that track.
UnbindLocalTrack(ctx *TrackLocalContext)

// BindRemoteTrack lets you modify any incoming RTP packets. It is called once for per RemoteTrack. The returned method
// will be called once per rtp packet.
BindRemoteTrack(ctx *TrackRemoteContext, read ReadRTP) ReadRTP

// UnbindRemoteTrack is called when the Track is removed. It can be used to clean up any data related to that track.
UnbindRemoteTrack(ctx *TrackRemoteContext)

io.Closer
}

// NoOpInterceptor is an Interceptor that does not modify any packets. It can embedded in other interceptors, so it's
// possible to implement only a subset of the methods.
type NoOpInterceptor struct{}

// BindReadRTCP lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func (i *NoOpInterceptor) BindReadRTCP(read ReadRTCP) ReadRTCP {
return read
}

// BindWriteRTCP lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (i *NoOpInterceptor) BindWriteRTCP(write WriteRTCP) WriteRTCP {
return write
}

// BindLocalTrack lets you modify any outgoing RTP packets. It is called once for per LocalTrack. The returned method
// will be called once per rtp packet.
func (i *NoOpInterceptor) BindLocalTrack(_ *TrackLocalContext, write WriteRTP) WriteRTP {
return write
}

// UnbindLocalTrack is called when the Track is removed. It can be used to clean up any data related to that track.
func (i *NoOpInterceptor) UnbindLocalTrack(_ *TrackLocalContext) {}

// BindRemoteTrack lets you modify any incoming RTP packets. It is called once for per RemoteTrack. The returned method
// will be called once per rtp packet.
func (i *NoOpInterceptor) BindRemoteTrack(_ *TrackRemoteContext, read ReadRTP) ReadRTP {
return read
}

// UnbindRemoteTrack is called when the Track is removed. It can be used to clean up any data related to that track.
func (i *NoOpInterceptor) UnbindRemoteTrack(_ *TrackRemoteContext) {}

// Close closes the Interceptor, cleaning up any data if necessary.
func (i *NoOpInterceptor) Close() error {
return nil
}
77 changes: 77 additions & 0 deletions interceptor_chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// +build !js

package webrtc

import (
"github.com/pion/webrtc/v3/internal/util"
)

type chainInterceptor struct {
interceptors []Interceptor
}

// BindReadRTCP lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func (i *chainInterceptor) BindReadRTCP(read ReadRTCP) ReadRTCP {
for _, interceptor := range i.interceptors {
read = interceptor.BindReadRTCP(read)
}

return read
}

// BindWriteRTCP lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (i *chainInterceptor) BindWriteRTCP(write WriteRTCP) WriteRTCP {
for _, interceptor := range i.interceptors {
write = interceptor.BindWriteRTCP(write)
}

return write
}

// BindLocalTrack lets you modify any outgoing RTP packets. It is called once for per LocalTrack. The returned method
// will be called once per rtp packet.
func (i *chainInterceptor) BindLocalTrack(ctx *TrackLocalContext, write WriteRTP) WriteRTP {
for _, interceptor := range i.interceptors {
write = interceptor.BindLocalTrack(ctx, write)
}

return write
}

// UnbindLocalTrack is called when the Track is removed. It can be used to clean up any data related to that track.
func (i *chainInterceptor) UnbindLocalTrack(ctx *TrackLocalContext) {
for _, interceptor := range i.interceptors {
interceptor.UnbindLocalTrack(ctx)
}
}

// BindRemoteTrack lets you modify any incoming RTP packets. It is called once for per RemoteTrack. The returned method
// will be called once per rtp packet.
func (i *chainInterceptor) BindRemoteTrack(ctx *TrackRemoteContext, read ReadRTP) ReadRTP {
for _, interceptor := range i.interceptors {
read = interceptor.BindRemoteTrack(ctx, read)
}

return read
}

// UnbindRemoteTrack is called when the Track is removed. It can be used to clean up any data related to that track.
func (i *chainInterceptor) UnbindRemoteTrack(ctx *TrackRemoteContext) {
for _, interceptor := range i.interceptors {
interceptor.UnbindRemoteTrack(ctx)
}
}

// Close closes the Interceptor, cleaning up any data if necessary.
func (i *chainInterceptor) Close() error {
var errs []error
for _, interceptor := range i.interceptors {
if err := interceptor.Close(); err != nil {
errs = append(errs, err)
}
}

return util.FlattenErrs(errs)
}
21 changes: 21 additions & 0 deletions interceptor_registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// +build !js

package webrtc

// InterceptorRegistry is a collector for interceptors.
type InterceptorRegistry struct {
interceptors []Interceptor
}

// Add adds a new Interceptor to the registry.
func (i *InterceptorRegistry) Add(interceptor Interceptor) {
i.interceptors = append(i.interceptors, interceptor)
}

func (i *InterceptorRegistry) build() Interceptor {
if len(i.interceptors) == 0 {
return &NoOpInterceptor{}
}

return &chainInterceptor{interceptors: i.interceptors}
}
Loading

0 comments on commit c5da1c6

Please sign in to comment.