github.com/qiniu/go-sdk/v7@v7.27.0/rtc/api.go (about)

     1  package rtc
     2  
     3  import (
     4  	"crypto/hmac"
     5  	"crypto/sha1"
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/qiniu/go-sdk/v7/auth"
    14  )
    15  
    16  // RtcHost 为 Qiniu RTC Server API服务域名
    17  var RtcHost = "rtc.qiniuapi.com"
    18  
    19  // Manager 提供了 Qiniu RTC Server API 相关功能
    20  type Manager struct {
    21  	mac        *auth.Credentials
    22  	httpClient *http.Client
    23  }
    24  
    25  // MergePublishRtmp  连麦合流转推 RTMP 的配置
    26  // Enable: 布尔类型,用于开启和关闭所有房间的合流功能。
    27  // AudioOnly: 布尔类型,可选,指定是否只合成音频。
    28  // Height, Width: int,可选,指定合流输出的高和宽,默认为 640 x 480。
    29  // OutputFps: int,可选,指定合流输出的帧率,默认为 25 fps 。
    30  // OutputKbps: int,可选,指定合流输出的码率,默认为 1000 。
    31  // URL: 合流后转推旁路直播的地址,可选,支持魔法变量配置按照连麦房间号生成不同的推流地址。如果是转推到七牛直播云,不建议使用该配置
    32  // StreamTitle: 转推七牛直播云的流名,可选,支持魔法变量配置按照连麦房间号生成不同的流名。例如,配置 Hub 为 qn-zhibo ,配置 StreamTitle 为 $(roomName) ,则房间 meeting-001 的合流将会被转推到 rtmp://pili-publish.qn-zhibo.***.com/qn-zhibo/meeting-001地址。详细配置细则,请咨询七牛技术支持。
    33  type MergePublishRtmp struct {
    34  	Enable      bool   `json:"enable,omitempty"`
    35  	AudioOnly   bool   `json:"audioOnly,omitempty"`
    36  	Height      int    `json:"height,omitempty"`
    37  	Width       int    `json:"width,omitempty"`
    38  	OutputFps   int    `json:"fps,omitempty"`
    39  	OutputKbps  int    `json:"kbps,omitempty"`
    40  	URL         string `json:"url,omitempty"`
    41  	StreamTitle string `json:"streamTitle,omitempty"`
    42  }
    43  
    44  // App 完整信息
    45  // AppID: app 的唯一标识,创建的时候由系统生成。
    46  type App struct {
    47  	AppID string `json:"appId"`
    48  	AppInitConf
    49  	MergePublishRtmp MergePublishRtmp `json:"mergePublishRtmp,omitempty"`
    50  	CreatedAt        time.Time        `json:"createdAt"`
    51  	UpdatedAt        time.Time        `json:"updatedAt"`
    52  }
    53  
    54  // AppInitConf 创建 App 请求参数
    55  // Title: app 的名称, 可选。
    56  // Hub: 绑定的直播 hub,可选,用于合流后 rtmp 推流。
    57  // MaxUsers: int 类型,可选,连麦房间支持的最大在线人数。
    58  // NoAutoKickUser: bool 类型,可选,禁止自动踢人。
    59  type AppInitConf struct {
    60  	Hub            string `json:"hub,omitempty"`
    61  	Title          string `json:"title,omitempty"`
    62  	MaxUsers       int    `json:"maxUsers,omitempty"`
    63  	NoAutoKickUser bool   `json:"noAutoKickUser,omitempty"`
    64  }
    65  
    66  // MergePublishRtmpInfo 连麦合流转推 RTMP 的配置更改信息
    67  type MergePublishRtmpInfo struct {
    68  	Enable      *bool   `json:"enable,omitempty"`
    69  	AudioOnly   *bool   `json:"audioOnly,omitempty"`
    70  	Height      *int    `json:"height,omitempty"`
    71  	Width       *int    `json:"width,omitempty"`
    72  	OutputFps   *int    `json:"fps,omitempty"`
    73  	OutputKbps  *int    `json:"kbps,omitempty"`
    74  	URL         *string `json:"url,omitempty"`
    75  	StreamTitle *string `json:"streamTitle,omitempty"`
    76  }
    77  
    78  // AppUpdateInfo 更改信息
    79  // MergePublishRtmpInfo 连麦合流转推 RTMP 的配置更改信息
    80  type AppUpdateInfo struct {
    81  	Hub              *string               `json:"hub,omitempty"`
    82  	Title            *string               `json:"title,omitempty"`
    83  	MaxUsers         *int                  `json:"maxUsers,omitempty"`
    84  	NoAutoKickUser   *bool                 `json:"noAutoKickUser,omitempty"`
    85  	MergePublishRtmp *MergePublishRtmpInfo `json:"mergePublishRtmp,omitempty"`
    86  }
    87  
    88  // User 连麦房间里的用户
    89  type User struct {
    90  	UserID string `json:"userId"`
    91  }
    92  
    93  // NewManager 用来构建一个新的 Manager
    94  func NewManager(mac *auth.Credentials) *Manager {
    95  	httpClient := http.DefaultClient
    96  	return &Manager{mac: mac, httpClient: httpClient}
    97  }
    98  
    99  // CreateApp 新建实时音视频云
   100  func (r *Manager) CreateApp(appReq AppInitConf) (App, error) {
   101  	url := buildURL("/v3/apps")
   102  	ret := App{}
   103  	info := postReq(r.httpClient, r.mac, url, &appReq, &ret)
   104  	return ret, info.Err
   105  }
   106  
   107  // GetApp 根据 appID 获取 实时音视频云 信息
   108  func (r *Manager) GetApp(appID string) (App, error) {
   109  	url := buildURL("/v3/apps/" + appID)
   110  	ret := App{}
   111  	info := getReq(r.httpClient, r.mac, url, &ret)
   112  	return ret, info.Err
   113  }
   114  
   115  // DeleteApp 根据 appID 删除 实时音视频云
   116  func (r *Manager) DeleteApp(appID string) error {
   117  	url := buildURL("/v3/apps/" + appID)
   118  	info := delReq(r.httpClient, r.mac, url, nil)
   119  	return info.Err
   120  }
   121  
   122  // UpdateApp 根据 appID, App 更改实时音视频云 信息
   123  func (r *Manager) UpdateApp(appID string, appInfo AppUpdateInfo) (App, error) {
   124  	url := buildURL("/v3/apps/" + appID)
   125  	ret := App{}
   126  	info := postReq(r.httpClient, r.mac, url, &appInfo, &ret)
   127  	return ret, info.Err
   128  }
   129  
   130  // ListUser 根据 appID, roomName 获取连麦房间里在线的用户
   131  // appID: 连麦房间所属的 app 。
   132  // roomName: 操作所查询的连麦房间。
   133  func (r *Manager) ListUser(appID, roomName string) ([]User, error) {
   134  	url := buildURL("/v3/apps/" + appID + "/rooms/" + roomName + "/users")
   135  	users := struct {
   136  		Users []User `json:"users"`
   137  	}{}
   138  	info := getReq(r.httpClient, r.mac, url, &users)
   139  	return users.Users, info.Err
   140  }
   141  
   142  // KickUser 根据 appID, roomName, UserID 剔除在线的用户
   143  // appID: 连麦房间所属的 app 。
   144  // roomName: 连麦房间。
   145  // userID: 操作所剔除的用户。
   146  func (r *Manager) KickUser(appID, roomName, userID string) error {
   147  	url := buildURL("/v3/apps/" + appID + "/rooms/" + roomName + "/users/" + userID)
   148  	info := delReq(r.httpClient, r.mac, url, nil)
   149  	return info.Err
   150  }
   151  
   152  // RoomQuery 房间查询响应结果
   153  // IsEnd: bool 类型,分页查询是否已经查完所有房间。
   154  // Offset: int 类型,下次分页查询使用的位移标记。
   155  // Rooms: 当前活跃的房间名列表。
   156  type RoomQuery struct {
   157  	IsEnd  bool       `json:"end"`
   158  	Offset int        `json:"offset"`
   159  	Rooms  []RoomName `json:"rooms"`
   160  }
   161  
   162  // RoomName 房间名
   163  type RoomName string
   164  
   165  // ListActiveRooms 根据 appID, roomNamePrefix, offset, limit 查询当前活跃的房间
   166  // appID: 连麦房间所属的 app 。
   167  // roomNamePrefix: 所查询房间名的前缀索引,可以为空。
   168  // offset: int 类型,分页查询的位移标记。
   169  // limit: int 类型,此次查询的最大长度。
   170  func (r *Manager) ListActiveRooms(appID, roomNamePrefix string, offset, limit int) (RoomQuery, error) {
   171  	ret, _, err := r.doListActiveRoom(appID, roomNamePrefix, offset, limit)
   172  	return ret, err
   173  }
   174  
   175  // ListAllActiveRooms 根据 appID, roomNamePrefix 查询当前活跃的房间
   176  // appID: 连麦房间所属的 app 。
   177  // roomNamePrefix: 所查询房间名的前缀索引,可以为空。
   178  func (r *Manager) ListAllActiveRooms(appID, roomNamePrefix string) ([]RoomName, error) {
   179  	ns := []RoomName{}
   180  	var outErr error
   181  	for offset := 0; ; {
   182  		q, info, err := r.doListActiveRoom(appID, roomNamePrefix, offset, 100)
   183  		if err != nil && info.Code != 401 {
   184  			time.Sleep(500 * time.Millisecond)
   185  			q, info, err = r.doListActiveRoom(appID, roomNamePrefix, offset, 100)
   186  		}
   187  
   188  		if err != nil || len(q.Rooms) == 0 {
   189  			outErr = err
   190  			break
   191  		}
   192  		offset = q.Offset
   193  		ns = append(ns, q.Rooms...)
   194  		if q.IsEnd {
   195  			break
   196  		}
   197  	}
   198  	return ns, outErr
   199  }
   200  
   201  func (r *Manager) doListActiveRoom(appID, roomNamePrefix string, offset, limit int) (RoomQuery, resInfo, error) {
   202  	query := ""
   203  	roomNamePrefix = strings.TrimSpace(roomNamePrefix)
   204  	if len(roomNamePrefix) != 0 {
   205  		query = "prefix=" + roomNamePrefix + "&"
   206  	}
   207  	query += fmt.Sprintf("offset=%v&limit=%v", offset, limit)
   208  	url := buildURL("/v3/apps/" + appID + "/rooms?" + query)
   209  	ret := RoomQuery{}
   210  	info := getReq(r.httpClient, r.mac, url, &ret)
   211  	return ret, *info, info.Err
   212  }
   213  
   214  // RoomAccess 房间管理凭证
   215  // AppID: 房间所属帐号的 app 。
   216  // RoomName: 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$
   217  // UserID: 请求加入房间的用户 ID,需满足规格 ^[a-zA-Z0-9_-]{3,50}$
   218  // ExpireAt: int64 类型,鉴权的有效时间,传入以秒为单位的64位Unix绝对时间,token 将在该时间后失效。
   219  // Permission: 该用户的房间管理权限,"admin" 或 "user",默认为 "user" 。当权限角色为 "admin" 时,拥有将其他用户移除出房间等特权.
   220  type RoomAccess struct {
   221  	AppID      string `json:"appId"`
   222  	RoomName   string `json:"roomName"`
   223  	UserID     string `json:"userId"`
   224  	ExpireAt   int64  `json:"expireAt"`
   225  	Permission string `json:"permission"`
   226  }
   227  
   228  // GetRoomToken 生成房间管理鉴权,连麦用户终端通过房间管理鉴权获取七牛连麦服务。
   229  func (r *Manager) GetRoomToken(roomAccess RoomAccess) (token string, err error) {
   230  	roomAccessByte, err := json.Marshal(roomAccess)
   231  	if err != nil {
   232  		return
   233  	}
   234  	buf := make([]byte, base64.URLEncoding.EncodedLen(len(roomAccessByte)))
   235  	base64.URLEncoding.Encode(buf, roomAccessByte)
   236  
   237  	hmacsha1 := hmac.New(sha1.New, r.mac.SecretKey)
   238  	hmacsha1.Write(buf)
   239  	sign := hmacsha1.Sum(nil)
   240  
   241  	encodedSign := base64.URLEncoding.EncodeToString(sign)
   242  	token = r.mac.AccessKey + ":" + encodedSign + ":" + string(buf)
   243  	return
   244  }