grumble: Opus support.

This commit is contained in:
Mikkel Krautz 2012-11-18 23:26:37 +01:00
parent dd76f22eb7
commit a415ae75a9
4 changed files with 89 additions and 27 deletions

View file

@ -43,6 +43,7 @@ type Client struct {
lastResync int64 lastResync int64
crypt *cryptstate.CryptState crypt *cryptstate.CryptState
codecs []int32 codecs []int32
opus bool
udp bool udp bool
voiceTargets map[uint32]*VoiceTarget voiceTargets map[uint32]*VoiceTarget
@ -170,6 +171,8 @@ func (client *Client) disconnect(kicked bool) {
client.Printf("Disconnected") client.Printf("Disconnected")
client.conn.Close() client.conn.Close()
client.server.updateCodecVersions(nil)
} }
} }
@ -306,7 +309,11 @@ func (client *Client) udpRecvLoop() {
case mumbleproto.UDPMessageVoiceCELTAlpha: case mumbleproto.UDPMessageVoiceCELTAlpha:
fallthrough fallthrough
case mumbleproto.UDPMessageVoiceCELTBeta: case mumbleproto.UDPMessageVoiceCELTBeta:
kind := buf[0] & 0xe0 if (client.server.Opus) {
return
}
fallthrough
case mumbleproto.UDPMessageVoiceOpus:
target := buf[0] & 0x1f target := buf[0] & 0x1f
var counter uint8 var counter uint8
outbuf := make([]byte, 1024) outbuf := make([]byte, 1024)
@ -315,6 +322,7 @@ func (client *Client) udpRecvLoop() {
outgoing := packetdatastream.New(outbuf[1 : 1+(len(outbuf)-1)]) outgoing := packetdatastream.New(outbuf[1 : 1+(len(outbuf)-1)])
_ = incoming.GetUint32() _ = incoming.GetUint32()
if kind != mumbleproto.UDPMessageVoiceOpus {
for { for {
counter = incoming.Next8() counter = incoming.Next8()
incoming.Skip(int(counter & 0x7f)) incoming.Skip(int(counter & 0x7f))
@ -322,10 +330,14 @@ func (client *Client) udpRecvLoop() {
break break
} }
} }
} else {
size := int(incoming.GetUint16())
incoming.Skip(size & 0x1fff)
}
outgoing.PutUint32(client.Session) outgoing.PutUint32(client.Session)
outgoing.PutBytes(buf[1 : 1+(len(buf)-1)]) outgoing.PutBytes(buf[1 : 1+(len(buf)-1)])
outbuf[0] = kind outbuf[0] = buf[0] & 0xe0 // strip target
if target != 0x1f { // VoiceTarget if target != 0x1f { // VoiceTarget
client.server.voicebroadcast <- &VoiceBroadcast{ client.server.voicebroadcast <- &VoiceBroadcast{

View file

@ -1368,6 +1368,7 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) {
} }
stats.Version = version stats.Version = version
stats.CeltVersions = target.codecs stats.CeltVersions = target.codecs
stats.Opus = proto.Bool(target.opus)
stats.Address = target.tcpaddr.IP stats.Address = target.tcpaddr.IP
} }

View file

@ -37,6 +37,7 @@ const (
UDPMessagePing UDPMessagePing
UDPMessageVoiceSpeex UDPMessageVoiceSpeex
UDPMessageVoiceCELTBeta UDPMessageVoiceCELTBeta
UDPMessageVoiceOpus
) )
// Returns the numeric value identifying the message type of msg on the wire. // Returns the numeric value identifying the message type of msg on the wire.

View file

@ -90,6 +90,7 @@ type Server struct {
AlphaCodec int32 AlphaCodec int32
BetaCodec int32 BetaCodec int32
PreferAlphaCodec bool PreferAlphaCodec bool
Opus bool
// Channels // Channels
Channels map[int]*Channel Channels map[int]*Channel
@ -529,9 +530,7 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
// Add codecs // Add codecs
client.codecs = auth.CeltVersions client.codecs = auth.CeltVersions
if len(client.codecs) == 0 { client.opus = auth.GetOpus()
server.Printf("Client %i connected without CELT codecs.", client.Session)
}
client.state = StateClientAuthenticated client.state = StateClientAuthenticated
server.clientAuthenticated <- client server.clientAuthenticated <- client
@ -567,9 +566,21 @@ func (server *Server) finishAuthenticate(client *Client) {
// Add the client to the connected list // Add the client to the connected list
server.clients[client.Session] = client server.clients[client.Session] = client
// Warn clients without CELT support that they might not be able to talk to everyone else.
if len(client.codecs) == 0 {
client.codecs = []int32{CeltCompatBitstream}
server.Printf("Client %i connected without CELT codecs. Faking compat bitstream.", client.Session)
if server.Opus && !client.opus {
client.sendMessage(&mumbleproto.TextMessage{
Session: []uint32{client.Session},
Message: proto.String("<strong>WARNING:</strong> Your client doesn't support the CELT codec, you won't be able to talk to or hear most clients. Please make sure your client was built with CELT support."),
})
}
}
// First, check whether we need to tell the other connected // First, check whether we need to tell the other connected
// clients to switch to a codec so the new guy can actually speak. // clients to switch to a codec so the new guy can actually speak.
server.updateCodecVersions() server.updateCodecVersions(client)
client.sendChannelList() client.sendChannelList()
@ -669,12 +680,24 @@ func (server *Server) finishAuthenticate(client *Client) {
client.clientReady <- true client.clientReady <- true
} }
func (server *Server) updateCodecVersions() { func (server *Server) updateCodecVersions(connecting *Client) {
codecusers := map[int32]int{} codecusers := map[int32]int{}
var winner int32 var (
var count int winner int32
count int
users int
opus int
enableOpus bool
txtMsg *mumbleproto.TextMessage = &mumbleproto.TextMessage{
Message: proto.String("<strong>WARNING:</strong> Your client doesn't support the Opus codec the server is switching to, you won't be able to talk or hear anyone. Please upgrade to a client with Opus support."),
}
)
for _, client := range server.clients { for _, client := range server.clients {
users++
if client.opus {
opus++
}
for _, codec := range client.codecs { for _, codec := range client.codecs {
codecusers[codec] += 1 codecusers[codec] += 1
} }
@ -697,10 +720,9 @@ func (server *Server) updateCodecVersions() {
current = server.BetaCodec current = server.BetaCodec
} }
if winner == current { enableOpus = users == opus
return
}
if winner != current {
if winner == CeltCompatBitstream { if winner == CeltCompatBitstream {
server.PreferAlphaCodec = true server.PreferAlphaCodec = true
} else { } else {
@ -712,18 +734,44 @@ func (server *Server) updateCodecVersions() {
} else { } else {
server.BetaCodec = winner server.BetaCodec = winner
} }
} else if server.Opus == enableOpus {
if connecting != nil && !connecting.opus {
txtMsg.Session = []uint32{connecting.Session}
connecting.sendMessage(txtMsg)
}
return
}
server.Opus = enableOpus
err := server.broadcastProtoMessage(&mumbleproto.CodecVersion{ err := server.broadcastProtoMessage(&mumbleproto.CodecVersion{
Alpha: proto.Int32(server.AlphaCodec), Alpha: proto.Int32(server.AlphaCodec),
Beta: proto.Int32(server.BetaCodec), Beta: proto.Int32(server.BetaCodec),
PreferAlpha: proto.Bool(server.PreferAlphaCodec), PreferAlpha: proto.Bool(server.PreferAlphaCodec),
Opus: proto.Bool(server.Opus),
}) })
if err != nil { if err != nil {
server.Printf("Unable to broadcast.") server.Printf("Unable to broadcast.")
return return
} }
server.Printf("CELT codec switch %#x %#x (PreferAlpha %v)", uint32(server.AlphaCodec), uint32(server.BetaCodec), server.PreferAlphaCodec) if server.Opus {
for _, client := range server.clients {
if !client.opus && client.state == StateClientReady {
txtMsg.Session = []uint32{connecting.Session}
err := client.sendMessage(txtMsg)
if err != nil {
client.Panicf("%v", err)
}
}
}
if connecting != nil && !connecting.opus {
txtMsg.Session = []uint32{connecting.Session}
connecting.sendMessage(txtMsg)
}
}
server.Printf("CELT codec switch %#x %#x (PreferAlpha %v) (Opus %v)", uint32(server.AlphaCodec), uint32(server.BetaCodec), server.PreferAlphaCodec, server.Opus)
return return
} }