mirror of
https://github.com/hanzoai/livekit.git
synced 2026-10-30 18:29:34 +00:00
This fork lived at github.com/hanzoai/livekit but still declared
`module github.com/livekit/livekit-server`, so all 505 self-references pointed
at upstream. A fork that does not own its module path is the exact thing the
owned-module-paths rule exists to prevent: every import line in every consumer
carries someone else's brand, and `go get` resolves to their code, not ours.
module + 505 self-imports github.com/livekit/livekit-server -> github.com/hanzoai/livekit
KV client github.com/redis/go-redis/v9 -> github.com/hanzokv/go/v9
message bus github.com/livekit/psrpc -> github.com/hanzoai/psrpc
psrpc had to be forked (hanzoai/psrpc, Apache-2.0) because its bus is typed
against the KV client — the same shape redsync had for hanzoai/git.
DELIBERATELY NOT FORKED: github.com/livekit/{protocol,mediatransportutil,
mageutil} keep their addresses. Only `protocol` still reaches go-redis, through
a single file (redis/redis.go) out of 305. Forking an 11.5MB core wire-protocol
library that this server must track in lockstep — and that upstream pushed
yesterday — to change one file trades a tidy dependency graph for permanent
divergence on the fastest-moving dependency here. So one indirect
github.com/redis/go-redis entry remains, via livekit/protocol/redis, and that
is the correct place for it to live.
184 lines
5.2 KiB
Go
184 lines
5.2 KiB
Go
// Copyright 2023 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package interceptor
|
|
|
|
import (
|
|
"github.com/pion/interceptor"
|
|
"github.com/pion/rtp"
|
|
"github.com/pion/sdp/v3"
|
|
"github.com/pion/webrtc/v4"
|
|
"go.uber.org/zap/zapcore"
|
|
|
|
"github.com/hanzoai/livekit/pkg/sfu/utils"
|
|
"github.com/livekit/protocol/logger"
|
|
)
|
|
|
|
const (
|
|
simulcastProbeCount = 10
|
|
)
|
|
|
|
type SimulcastTrackInfo struct {
|
|
Mid string
|
|
StreamID string
|
|
RepairSSRC uint32 // set only when `IsRepairStream: false`, i. e. RTX SSRC for the primary stream
|
|
IsRepairStream bool
|
|
}
|
|
|
|
func (s *SimulcastTrackInfo) MarshalLogObject(e zapcore.ObjectEncoder) error {
|
|
e.AddString("Mid", s.Mid)
|
|
e.AddString("StreamID", s.StreamID)
|
|
e.AddUint32("RepairSSRC", s.RepairSSRC)
|
|
e.AddBool("IsRepairStream", s.IsRepairStream)
|
|
return nil
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
type UnhandleSimulcastOption func(u *UnhandleSimulcastInterceptor) error
|
|
|
|
func UnhandleSimulcastTracks(logger logger.Logger, tracks map[uint32]SimulcastTrackInfo) UnhandleSimulcastOption {
|
|
return func(u *UnhandleSimulcastInterceptor) error {
|
|
u.logger = logger
|
|
u.simTracks = tracks
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type UnhandleSimulcastInterceptorFactory struct {
|
|
opts []UnhandleSimulcastOption
|
|
}
|
|
|
|
func (f *UnhandleSimulcastInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
|
|
i := &UnhandleSimulcastInterceptor{simTracks: map[uint32]SimulcastTrackInfo{}}
|
|
for _, o := range f.opts {
|
|
if err := o(i); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return i, nil
|
|
}
|
|
|
|
func NewUnhandleSimulcastInterceptorFactory(opts ...UnhandleSimulcastOption) (*UnhandleSimulcastInterceptorFactory, error) {
|
|
return &UnhandleSimulcastInterceptorFactory{opts: opts}, nil
|
|
}
|
|
|
|
type unhandleSimulcastRTPReader struct {
|
|
SimulcastTrackInfo
|
|
logger logger.Logger
|
|
tryTimes int
|
|
reader interceptor.RTPReader
|
|
midExtID uint8
|
|
ridExtID uint8
|
|
rsidExtID uint8
|
|
}
|
|
|
|
func (u *unhandleSimulcastRTPReader) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
|
n, a, err := u.reader.Read(b, a)
|
|
if u.tryTimes < 0 || err != nil {
|
|
return n, a, err
|
|
}
|
|
|
|
header := rtp.Header{}
|
|
hsize, err := header.Unmarshal(b[:n])
|
|
if err != nil {
|
|
return n, a, nil
|
|
}
|
|
var mid, rid, rsid string
|
|
if payload := header.GetExtension(u.midExtID); payload != nil {
|
|
mid = string(payload)
|
|
}
|
|
|
|
if payload := header.GetExtension(u.ridExtID); payload != nil {
|
|
rid = string(payload)
|
|
}
|
|
|
|
if payload := header.GetExtension(u.rsidExtID); payload != nil {
|
|
rid = string(payload)
|
|
}
|
|
|
|
if mid != "" && (rid != "" || rsid != "") {
|
|
u.logger.Debugw(
|
|
"unhandle stream found",
|
|
"mid", mid,
|
|
"rid", rid,
|
|
"rsid", rsid,
|
|
"ssrc", header.SSRC,
|
|
"simulcastTrackInfo", u.SimulcastTrackInfo,
|
|
)
|
|
u.tryTimes = -1
|
|
return n, a, nil
|
|
} else {
|
|
// ignore padding only packet for probe count
|
|
if !header.Padding || n-header.MarshalSize()-int(b[n-1]) != 0 {
|
|
u.tryTimes--
|
|
}
|
|
}
|
|
|
|
if mid == "" {
|
|
header.SetExtension(u.midExtID, []byte(u.Mid))
|
|
}
|
|
if rid == "" && !u.IsRepairStream {
|
|
header.SetExtension(u.ridExtID, []byte(u.StreamID))
|
|
}
|
|
if rsid == "" && u.IsRepairStream {
|
|
header.SetExtension(u.rsidExtID, []byte(u.StreamID))
|
|
}
|
|
|
|
hsize2 := header.MarshalSize()
|
|
|
|
if hsize2-hsize+n > len(b) { // no enough buf to set extension
|
|
return n, a, nil
|
|
}
|
|
copy(b[hsize2:], b[hsize:n])
|
|
header.MarshalTo(b)
|
|
u.logger.Debugw(
|
|
"unhandle stream injecting",
|
|
"mid", mid,
|
|
"rid", rid,
|
|
"rsid", rsid,
|
|
"ssrc", header.SSRC,
|
|
"simulcastTrackInfo", u.SimulcastTrackInfo,
|
|
)
|
|
return hsize2 - hsize + n, a, nil
|
|
}
|
|
|
|
type UnhandleSimulcastInterceptor struct {
|
|
interceptor.NoOp
|
|
logger logger.Logger
|
|
simTracks map[uint32]SimulcastTrackInfo
|
|
}
|
|
|
|
func (u *UnhandleSimulcastInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
|
if t, ok := u.simTracks[info.SSRC]; ok {
|
|
midExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESMidURI})
|
|
streamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI})
|
|
repairStreamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRepairRTPStreamIDURI})
|
|
if midExtensionID == 0 || streamIDExtensionID == 0 || repairStreamIDExtensionID == 0 {
|
|
return reader
|
|
}
|
|
|
|
return &unhandleSimulcastRTPReader{
|
|
SimulcastTrackInfo: t,
|
|
logger: u.logger,
|
|
reader: reader,
|
|
tryTimes: simulcastProbeCount,
|
|
midExtID: uint8(midExtensionID),
|
|
ridExtID: uint8(streamIDExtensionID),
|
|
rsidExtID: uint8(repairStreamIDExtensionID),
|
|
}
|
|
}
|
|
return reader
|
|
}
|