mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-06-25 11:50:23 +08:00
音视频通话
This commit is contained in:
parent
e6451179d6
commit
4b9412f685
17
config/livekit.yaml
Normal file
17
config/livekit.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
port: 7880
|
||||
rtc:
|
||||
tcp_port: 7881
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
use_external_ip: true
|
||||
|
||||
redis:
|
||||
address: redis:6379
|
||||
password: openIM123
|
||||
|
||||
# LiveKit 要求 API secret 至少 32 字符;生产环境请替换为强随机串并与 openim-rpc-rtc.yml 中 apiSecret 一致
|
||||
keys:
|
||||
devkey: openim-livekit-default-secret-32chars-min
|
||||
|
||||
logging:
|
||||
level: info
|
||||
@ -18,14 +18,14 @@ prometheus:
|
||||
|
||||
liveKit:
|
||||
# LiveKit server address reachable from the RTC service (internal/backend address)
|
||||
# Example: http://livekit:7880
|
||||
internalAddress: http://localhost:7880
|
||||
# When deployed via docker-compose, use the service name 'livekit'
|
||||
internalAddress: http://livekit:7880
|
||||
# LiveKit server address reachable from clients (external/public address)
|
||||
# Example: wss://livekit.example.com
|
||||
externalAddress: ws://localhost:7880
|
||||
# Production should use wss://livekit.example.com with TLS
|
||||
externalAddress: ws://192.168.1.91:7880
|
||||
# LiveKit API key (configured in your LiveKit server)
|
||||
apiKey: devkey
|
||||
# LiveKit API secret (configured in your LiveKit server)
|
||||
apiSecret: secret
|
||||
# LiveKit API secret(须 ≥32 字符,须与 config/livekit.yaml keys 中对应值一致)
|
||||
apiSecret: openim-livekit-default-secret-32chars-min
|
||||
# Token expiry in seconds (default: 3600 = 1 hour)
|
||||
tokenExpiry: 3600
|
||||
|
||||
@ -163,6 +163,24 @@ services:
|
||||
networks:
|
||||
- openim
|
||||
|
||||
livekit:
|
||||
image: "${LIVEKIT_IMAGE:-livekit/livekit-server:latest}"
|
||||
container_name: livekit
|
||||
restart: always
|
||||
ports:
|
||||
- "7880:7880"
|
||||
- "7881:7881"
|
||||
- "50000-50100:50000-50100/udp"
|
||||
volumes:
|
||||
- ./config/livekit.yaml:/etc/livekit.yaml
|
||||
command: --config /etc/livekit.yaml --node-ip=0.0.0.0
|
||||
depends_on:
|
||||
- redis
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
networks:
|
||||
- openim
|
||||
|
||||
minio:
|
||||
image: "${MINIO_IMAGE}"
|
||||
ports:
|
||||
|
||||
@ -108,3 +108,7 @@ func (p *PrometheusDiscoveryApi) MessageGateway(c *gin.Context) {
|
||||
func (p *PrometheusDiscoveryApi) MessageTransfer(c *gin.Context) {
|
||||
p.discovery(c, prommetrics.MessageTransferKeyName)
|
||||
}
|
||||
|
||||
func (p *PrometheusDiscoveryApi) Rtc(c *gin.Context) {
|
||||
p.discovery(c, p.config.Share.RpcRegisterName.Rtc)
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/relation"
|
||||
"github.com/openimsdk/protocol/rtc"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/protocol/user"
|
||||
|
||||
@ -103,6 +104,10 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rtcConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Rtc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.New()
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
@ -301,6 +306,20 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co
|
||||
captchaGroup.POST("/verify", cp.VerifyCaptcha)
|
||||
}
|
||||
|
||||
{
|
||||
rc := NewRtcApi(rtc.NewRtcServiceClient(rtcConn))
|
||||
rtcGroup := r.Group("/rtc")
|
||||
rtcGroup.POST("/signal_message_assemble", rc.SignalMessageAssemble)
|
||||
rtcGroup.POST("/signal_get_room_by_group_id", rc.SignalGetRoomByGroupID)
|
||||
rtcGroup.POST("/signal_get_token_by_room_id", rc.SignalGetTokenByRoomID)
|
||||
rtcGroup.POST("/signal_get_rooms", rc.SignalGetRooms)
|
||||
rtcGroup.POST("/get_signal_invitation_info", rc.GetSignalInvitationInfo)
|
||||
rtcGroup.POST("/get_signal_invitation_info_start_app", rc.GetSignalInvitationInfoStartApp)
|
||||
rtcGroup.POST("/signal_send_custom_signal", rc.SignalSendCustomSignal)
|
||||
rtcGroup.POST("/get_signal_invitation_records", rc.GetSignalInvitationRecords)
|
||||
rtcGroup.POST("/delete_signal_records", rc.DeleteSignalRecords)
|
||||
}
|
||||
|
||||
{
|
||||
statisticsGroup := r.Group("/statistics")
|
||||
statisticsGroup.POST("/user/register", u.UserRegisterCount)
|
||||
@ -330,6 +349,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co
|
||||
proDiscoveryGroup.GET("/push", pd.Push)
|
||||
proDiscoveryGroup.GET("/msg_gateway", pd.MessageGateway)
|
||||
proDiscoveryGroup.GET("/msg_transfer", pd.MessageTransfer)
|
||||
proDiscoveryGroup.GET("/rtc", pd.Rtc)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
65
internal/api/rtc.go
Normal file
65
internal/api/rtc.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright © 2024 OpenIM. All rights reserved.
|
||||
//
|
||||
// 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 api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/protocol/rtc"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
)
|
||||
|
||||
type RtcApi struct {
|
||||
Client rtc.RtcServiceClient
|
||||
}
|
||||
|
||||
func NewRtcApi(client rtc.RtcServiceClient) RtcApi {
|
||||
return RtcApi{Client: client}
|
||||
}
|
||||
|
||||
func (o *RtcApi) SignalMessageAssemble(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.SignalMessageAssemble, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) SignalGetRoomByGroupID(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.SignalGetRoomByGroupID, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) SignalGetTokenByRoomID(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.SignalGetTokenByRoomID, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) SignalGetRooms(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.SignalGetRooms, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) GetSignalInvitationInfo(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.GetSignalInvitationInfo, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) GetSignalInvitationInfoStartApp(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.GetSignalInvitationInfoStartApp, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) SignalSendCustomSignal(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.SignalSendCustomSignal, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) GetSignalInvitationRecords(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.GetSignalInvitationRecords, o.Client)
|
||||
}
|
||||
|
||||
func (o *RtcApi) DeleteSignalRecords(c *gin.Context) {
|
||||
a2r.Call(c, rtc.RtcServiceClient.DeleteSignalRecords, o.Client)
|
||||
}
|
||||
@ -32,7 +32,7 @@ import (
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// SignalMessageAssemble processes a signal request from the WebSocket gateway
|
||||
@ -379,8 +379,8 @@ func (s *rtcServer) SignalSendCustomSignal(ctx context.Context, req *rtc.SignalS
|
||||
if uid == opUserID {
|
||||
continue
|
||||
}
|
||||
if err := s.sendSignalingNotification(ctx, opUserID, uid, int32(constant.SingleChatType), nil, content); err != nil {
|
||||
log.ZWarn(ctx, "sendSignalingNotification customSignal failed", err, "to", uid)
|
||||
if err := s.sendCustomSignalNotification(ctx, opUserID, uid, int32(constant.SingleChatType), content); err != nil {
|
||||
log.ZWarn(ctx, "sendCustomSignalNotification failed", err, "to", uid)
|
||||
}
|
||||
}
|
||||
return &rtc.SignalSendCustomSignalResp{}, nil
|
||||
@ -462,10 +462,28 @@ func (s *rtcServer) sendSignalingNotification(ctx context.Context, sendID, recvI
|
||||
return err
|
||||
}
|
||||
|
||||
// marshalSignalReq serialises a SignalReq to JSON bytes using protojson,
|
||||
// which correctly handles protobuf oneof fields for client-side parsing.
|
||||
// sendCustomSignalNotification sends a CustomSignalNotification (1605) to a user.
|
||||
func (s *rtcServer) sendCustomSignalNotification(ctx context.Context, sendID, recvID string, sessionType int32, content []byte) error {
|
||||
now := time.Now().UnixMilli()
|
||||
msgData := &sdkws.MsgData{
|
||||
SendID: sendID,
|
||||
RecvID: recvID,
|
||||
SessionType: sessionType,
|
||||
ContentType: int32(constant.CustomSignalNotification),
|
||||
MsgFrom: int32(constant.SysMsgType),
|
||||
Content: content,
|
||||
CreateTime: now,
|
||||
SendTime: now,
|
||||
ServerMsgID: uuid.New().String(),
|
||||
ClientMsgID: uuid.New().String(),
|
||||
Options: make(map[string]bool),
|
||||
}
|
||||
_, err := s.msgClient.MsgClient.SendMsg(ctx, &pbmsg.SendMsgReq{MsgData: msgData})
|
||||
return err
|
||||
}
|
||||
|
||||
func marshalSignalReq(req *rtc.SignalReq) []byte {
|
||||
b, _ := protojson.Marshal(req)
|
||||
b, _ := proto.Marshal(req)
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ serviceBinaries:
|
||||
openim-rpc-group: 1
|
||||
openim-rpc-friend: 1
|
||||
openim-rpc-msg: 1
|
||||
openim-rpc-rtc: 1
|
||||
openim-rpc-third: 1
|
||||
toolBinaries:
|
||||
- check-free-memory
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user