# 一、接入流程

# 1.获取WXGameJetPack实例,并设置相应回调

// 一般在MonoBehaviour子类的Start()回调里执行以下代码
void Start()
{
  ...
  WXGameJetPack mJetPackEngine = WXGameJetPack.GetEngine();;

  // 必须实现的回调
  mJetPackEngine.livePush.OnCheckSupportResult += OnCheckSupportResult;
  mJetPackEngine.livePush.OnStartLiveEvent += OnStartLive;
  mJetPackEngine.livePush.OnStopLiveEvent += OnStopLive;
  mJetPackEngine.livePush.OnOpenUrlEvent += OnOpenUrl;
  // 扩展事件回调
   mJetPackEngine.livePush.OnExtraEvent += OnExtra;
  //挂件启动关闭事件回调
  mJetPackEngine.livePush.OnLiveWidgetLoadedEvent += OnWebViewLoadedEvent;
  mJetPackEngine.livePush.OnLiveWidgetClosedEvent += OnWebViewClosedEvent;    
  // MSDK 还需要再实现以下2个回调
  mJetPackEngine.livePush.OnRequireAuthorizeEvent += OnRequireAuthorize;
  mJetPackEngine.livePush.OnStartChannelLiveEvent += OnStartChannelLive;
  ...
}

# 2.直播入口检测(在游戏登录操作后执行)

// 注册检测结果回调
string userInfo = "{\"openid\":\"" + mOpenId + "\",\"appid\":\"" + mWxAppId + "\",\"renderer\":\"" + SystemInfo.graphicsDeviceName +"\"}";
mJetPackEngine.livePush.OnCheckSupportResult += OnCheckSupportResult;   // 可以在登录注销时执行反注OnCheckSupportResult
mJetPackEngine.livePush.CheckSupport(userInfo, mTestEnv);

# 3.处理OnCheckSupportResult回调结果

void OnCheckSupportResult(int result, string message) {
  // result == 0或1时,显示直播入口,并调用Init接口
  if (result == 0||result == 1) {
    // 初始化sdk
    mJetPackEngine.Init(gameName, appId, ilinkAppId);
    // 显示直播入口
    mLiveBtn.SetActive(true);
  }
}

# 4.拉起挂件直播

//点击一键直播按钮
mLiveBtn.GetComponent<Button>().onClick.AddListener(() =>
{
  //支持通过“StartChannelLive”跳转视频号,需要保证MSDK版本>=3.3.26
  mJetPackEngine.livePush.EnableStartChannelLive(true);
  // 建议设置SDK内部处理麦克风权限
  mJetPackEngine.livePush.AuthorizeInnerAudio(true);
  //如果没有接入第三方语音组件(如GVoice),可以启用SDK内部的麦克风采集
  mJetPackEngine.livePush.SwitchInnerAudioRecord(mInnerRecord);
  //拉起直播挂件
  mJetPackEngine.livePush.LoadLiveWebView();
});

# 5.设置直播事件轮训

void Update()
{
  ...
  if (mJetPackEngine != null)
  {
    //设置直播事件轮询
    mJetPackEngine.Poll();
  }
}

# 6.处理打开链接事件

void OnOpenUrl(string url, int screenType, bool isFullScreen, bool isBrowser)
{
    //MSDK V5
    MSDKWebView.OpenUrl(url, (MSDKWebViewOrientation)screenType, isFullScreen,true, "",isBrowser);

    //MSDK V3
    // WGOpenUrl(liveMsg.url, (MSDKWebViewOrientation)liveMsg.screenType, liveMsg.isFullScreen);
    
    // 没有MSDK的情况,请使用游戏内置WebView或者使用系统浏览器
    ...
}

# 7.处理开播事件

void OnStartLive(bool isAudioPermissionGranted)
{
  // 执行挂载WXGameLiveEvent组件
  if (_wxgameliveObject != null)
  {
    GameObject.Destroy(_wxgameliveObject);
  }
  _wxgameliveObject = new GameObject(_gameObjectName);
  _wxgameliveObject.AddComponent<WXGameLiveEvent>();
  
  
  // 接入GVoice的游戏,开始直播时,需要设置GVoice加入语音房间,专门用于采集主播语音
  if (isAudioPermissionGranted) {
      mVoiceEngine.invoke(4, 1, 0, null);     // 启用OnRecordingData回调,务必在同意麦克风权限后调用
      mVoiceEngine.SetMode(Mode.RealTime);
      mVoiceEngine.EnableMultiRoom(true);
      // 加入直播专用的语音房间,mLiveRoomName命名可以是前缀+时戳,确保每个房间名都是唯一的
      mVoiceEngine.JoinTeamRoom(mLiveRoomName, 5000);
  } else {
    // 如果没有设置EnableInnerAudioPermissionAuth(true), 此时需要申请麦克风权限,等用户同意麦克风权限后再调用
    // mVoiceEngine.invoke(4, 1, 0, null)
    // 如果已经设置了EnableInnerAudioPermissionAuth(true),说明用于拒绝了麦克风权限,需要提示用户无法采集主播语音
  }
}

# 8.处理关播事件

void OnStopLive()
{
  // 卸载WXGameLiveEvent组件
  if (_wxgameliveObject != null)
  {
    GameObject.Destroy(_wxgameliveObject);
    _wxgameliveObject = null;
  }
  
  // 停止直播时,退出GVoice语音房间
  if (mAudioPermissionGranted)
  {
      mVoiceEngine.invoke(4, 0, 0, null);  // 禁用OnRecordingData回调
      mVoiceEngine.EnableRoomMicrophone(mLiveRoomName, false);
      mVoiceEngine.QuitRoom(mLiveRoomName, 5000);
  }    
}

# 9.扩展事件回调

void OnExtra(string param)
{
  var dict = Json.Deserialize(param) as Dictionary<string,object>;
  
  string type = dict["type"].ToString();
  
  if (type.Equals("micPermission"))
  {
      // 隐私合规需要,在弹出权限框时需对为什么申请该权限做出说明 
      // 显示权限说明提示, 在OnStartLive回调中关闭
  }
  //接入了活动页面需实现
  else if(type.Equals("finishLoadActivity")){
      //发送玩家信息
  }

}

# 10.处理视频号授权事件(接入MSDK的游戏需要处理)

void OnRequireAuthorize()
{

    // MSDK V5
    MSDKLogin.ChannelPermissionAuth(MSDKChannel.WeChat,"snsapi_channels_livestream");
        
    // 老接口
    // MSDKLogin.Login(MSDKChannel.WeChat,"snsapi_channels_livestream");
        
    // MSDK V3
    // WGPlatform.Instance.WGChannelPermissionAuth(ePlatform.ePlatform_WeixinVideoLive, "snsapi_channels_livestream");    
}

# 11.处理跳转微信事件(接入MSDK的游戏需要处理)

void OnStartChannelLive(string liveJsonInfo)
{
  // MSDK V5
  var reqInfo = new MSDKFriendReqInfo
  {
    Type = (int) FriendReqType.Friend_REQ_WX_CHANNEL_START_LIVE,
    ExtraJson = liveJsonInfo,
  };
  MSDKFriend.SendMessage(reqInfo, MSDKChannel.WeChat);
  // MSDK V3
  //WGPlatform.Instance.WGSendToWXChannelStartLive(extraJson,null); 

}

# 12.微信传入信息处理(接入MSDK的游戏需要处理)

  1. 监听游戏被第三方应用拉起时,传递过来的参数,确认是被微信视频号拉起时,调用SetMessageFromWeChat接口将相关参数传给直播SDK。
MSDKLogin.LoginBaseRetEvent += OnLoginBaseRetEvent;
void OnLoginBaseRetEvent(MSDKBaseRet baseRet)
{
  if (baseRet.MethodNameId == (int) MSDKMethodNameID.MSDK_CHANNEL_PERMISSION_AUTH)
  {
    // 视频号授权同意的事件
    var dict = Json.Deserialize(baseRet.ExtraJson) as Dictionary<string,object>;
    string tdiAuthBufferBase64 = dict["tdiAuthBuffer"].ToString();
    byte[] tdiAuthBuffer = System.Convert.FromBase64String(tdiAuthBufferBase64);
    mJetPackEngine.livePush.AuthorizeFinish(baseRet.RetCode, tdiAuthBuffer);
  }
  else if (baseRet.MethodNameId == (int)MSDKMethodNameID.MSDK_LOGIN_WAKEUP)
  {
    // 游戏被唤醒
    var dict = Json.Deserialize(baseRet.ExtraJson) as Dictionary<string,object>;
  
    string _params = dict["params"].ToString();
    var wxDict = Json.Deserialize(_params) as Dictionary<string,object>;
    
#if UNITY_ANDROID
    mMsgFromWx = wxDict["_wxappextendobject_extInfo"].ToString();
#elif UNITY_IOS
    mMsgFromWx = wxDict["messageExt"].ToString();
#endif
    // 确认是被微信拉起
    if (mMsgFromWx.Contains("WeChatLive_ShiPinHao"))
    {
        // 将微信的参数设置给SDK
        mJetPackEngine.livePush.SetMessageFromWeChat(mMsgFromWx);              
    }
  }
    ......
}

# 13.游戏生命周期埋点

//游戏退出
void OnApplicationQuit()
{
  Debug.Log("====OnApplicationQuit");
  WXGameJetPack.GetEngine().AppWillTerminate();
}

//游戏切到前后台等情况(个别安卓机型会出现回调问题,安卓机型推荐在安卓Activity生命周期进行回调)
void OnApplicationPause(bool pauseStatus)
{
  Debug.Log("OnApplicationPause  pauseStatus:" + pauseStatus);
  if (pauseStatus)
  {
    WXGameJetPack.GetEngine().AppDidEnterBackground();
  }
  else
  {
    WXGameJetPack.GetEngine().AppWillEnterForeground();
  }
}
  • Android端还需要在游戏的Activity中埋点如下接口
import com.tencent.wx.gamelive.WxGameLifeCycle;
public class MainActivity extends UnityPlayerActivity {

    ...    
       
    @Override
    public void onResume() {

        super.onResume();
        ...
        WxGameLifeCycle.onResume();
    }

    @Override
    public void onPause() {

        super.onPause();
        ...
        WxGameLifeCycle.onPause();
    }


    @Override
	public void onDestroy() {
		super.onDestroy();
		WxGameLifeCycle.onDestroy();
	}


    @Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {

		super.onActivityResult(requestCode, resultCode, data);
		...
		WxGameLifeCycle.onActivityResult(requestCode,resultCode, data);
	}

    @Override
	public void onRequestPermissionsResult(int arg0, String[] arg1, int[] arg2)
	{
		super.onRequestPermissionsResult(arg0, arg1, arg2);
		...
		WxGameLifeCycle.onRequestPermissionsResult(arg0, arg1, arg2);
	}

    @Override 
    public void onWindowFocusChanged(boolean hasFocus)
	{
		super.onWindowFocusChanged(hasFocus);
		...
		WxGameLifeCycle.onWindowFocusChanged(hasFocus);
	}
}

# 14.GVoice 语音采集

// mVoiceEngine 为GVoice实例:GCloudVoice.GetEngine()
// 设置队友语音回调
mVoiceEngine.OnPlayingData += OnPlayingData;
// invoke(5, 1, 1, null)必须在Init之前调用,作用为开启OnPlayingData回调
mVoiceEngine.invoke(5, 1, 1, null);
mLiveEngine.LogToSDK("调用【GVoiceEngine invoke(5, 1, 1, null)】"); //一键开播SDK v1.3.4才支持,若无LogToSDK接口则可删除此行代码
mVoiceEngine.Init();

// 设置主播语音回调
mVoiceEngine.OnRecording += OnRecordingData;
// 设置进房结果回调
mVoiceEngine.OnJoinRoomCompleteEvent += OnJoinRoomCompleteEvent;
// 设置退房结果回调
mVoiceEngine.OnQuitRoomCompleteEvent += OnQuitRoomCompleteEvent;
// 设置实时语音模式
mVoiceEngine.SetMode(Mode.RealTime);

// 队友语音回调函数
[AOT.MonoPInvokeCallback(typeof(IGCloudVoiceEngine.PlayingDataHandler))]
static void OnPlayingData(IntPtr buf, int len, int nSampleRate, int nChannel)
{
  // 发送队友语音
  if (isLiving)
    {
      mLiveEngine.OnGVoiceTeamPcmS16(buf, len, nSampleRate, nChannel, false);
    }
}

// 主播语音回调函数
[AOT.MonoPInvokeCallback(typeof(IGCloudVoiceEngine.RecordingDataHandler))]
static void OnRecordingData(IntPtr buf, int len) {
  // 发送主播语音
  if (isLiving)
  {
    mLiveEngine.OnGVoicePcmS16(buf, len, 16000, 1, false);
  }      
}

// 进房结果回调函数
void OnJoinRoomCompleteEvent(CompleteCode code, string roomName, int memberID)
{
  roomName = roomName.Trim('\0');
  if (code == CompleteCode.JoinRoomSucc)
  {
    // 进房成功后,打开麦克风
    mVoiceEngine.OpenMic();
    if (roomName.Equals(mLiveRoomName)) {
      // 进入直播语音房间,打开扬声器,用于播放背景音乐
      mVoiceEngine.OpenSpeaker();
      // 发送gvoice注册信息及房间信息,用于在后台往指定的语音房间注入背景音乐
      string gvoice_notify = "{\"type\": \"gvoice_notify\", \"room_name\": \"" + roomName + "\", \"game_id\": \"" + mGVoiceId + "\", \"game_key\": \"" + mGVoiceKey + "\"}";
      mLiveEngine.PostGameMessage(gvoice_notify);
    }
  }
}

// 退房结果回调函数
void OnQuitRoomCompleteEvent(CompleteCode code, string roomName, int memberID)
{
  roomName = roomName.Trim('\0');
  if (code == CompleteCode.QuitRoomSucc)
  {
    if (noRoom)
    {
      // 全部语音房间都退出后,关闭麦克风和扬声器
      // 游戏可能加入了多个语音房间
      mVoiceEngine.CloseMic();
      mVoiceEngine.CloseSpeaker();
    }
  }
}

# 15.微信OpenSDK 相关逻辑

对于接入了微信OpenSDK的外部游戏,需要再原生平台做如下修改:

  • Android
// Step 1 : 传入WXAPI句柄
api = WXAPIFactory.createWXAPI(this, Constants.WX_APP_ID, true);
api.registerApp(Constants.WX_APP_ID);
WxApiHelper.sharedInstance().setWxApi(api);
// Step 2: 处理微信返回的票据
// 在WXEntryAcivity的onResp回调中加入:
@Override
public void onResp(BaseResp resp) {
  ...
  if (resp.getType() == ConstantsAPI.COMMAND_LAUNCH_WX_SEND_TDI_AUTH) {
    SendTdiAuth.Resp authResp = (SendTdiAuth.Resp)resp;
    // 完成视频号授权登录
    WXLivePush.sharedInstance().authorizeFinish(authResp.errCode, authResp.tdiAuthBuffer);
  }

  finish();
}

// 处理微信传递过来的开播票据
@Override
public void onReq(BaseReq req) {
  switch (req.getType()) {
    .....
    case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
    ShowMessageFromWX.Req wxReq = (ShowMessageFromWX.Req) req;
    WXMediaMessage wxMsg = wxReq.message;
    WXAppExtendObject obj = (WXAppExtendObject) wxMsg.mediaObject;
  
    if (obj.extInfo.contains("WeChatLive_ShiPinHao")) {
        // 实现微信拉起游戏自动开播
        WXLivePush.sharedInstance().onMessageFromWeChat(obj.extInfo);
    }
  
    finish();
    break;
    .....
    default:
      break;
  }
  finish();
}
  • iOS
// Step 1 : 设置WXApiDelegate
// 在实现WXApiDelegate的类里设置
[WXGameLiveAdapterBridge shareInstance].delegate = self;

// Step 2 : 处理微信传递过来的数据
-(void) onReq:(BaseReq*)req {
  if ([req isKindOfClass:[LaunchFromWXReq class]]) {
    LaunchFromWXReq *launchFromWXReq = (LaunchFromWXReq*)req;
    NSString* messageExt = launchFromWXReq.message.messageExt;
    if ([messageExt containsString:@"WeChatLive_ShiPinHao"]) {
      // 实现微信拉起游戏自动开播
      [[WXGameJetPack getJetPackEngine].livePush onMessageFromWeChat:messageExt];
    }
  }
}

-(void) onResp:(BaseResp*)resp {
  ....
  if ([resp isKindOfClass:[SendTdiAuthResp class]]) {
    // 完成视频号授权登录
    SendTdiAuthResp *authResp = (SendTdiAuthResp *)resp;
    [[WXGameJetPack getJetPackEngine].livePush authorizeFinish:authResp.errCode tdiAuthBuffer:authResp.tdiAuthBuffer];
  }
}

注意事项

  1. 使用Unity内置音频引擎时,启用需要在Player Settings --> Other Settings --> Scripting Define Symbols中添加UNITY_AUDIO_CAPTURE,如果Unity中的AudioListener是全局的,并不是每个Scene放置一个独立的AudioListener的话,则需要再添加UNITY_GLOBAL_AUDIO_LISTENER。
  2. 使用Fmod for Unity音频引擎时,启用需要在Player Settings --> Other Settings --> Scripting Define Symbols中添加FMOD_AUDIO_CAPTURE。
  3. 使用Wwise情况下,sdk提供了wwise的录音插件:WGLive,游戏侧需将该插件挂在到wwise的工程的master总线上后,重新build sound bank资源。
  4. GVoice的时序需要特别注意:

mVoiceEngine.invoke(4, 1, 0, null) 必须在麦克风权限同意后再调用 mVoiceEngine.invoke(5, 1, 1, null) 必须在mVoiceEngine.Init()之前调用

  1. 游戏注销当前登录的帐号时需要关闭当前的直播,调用如下接口:
mLiveEngine.PostGameMessage("{\"type\": \"finishLive\"}");

# 二、一键开播完整流程图

flow_chart

# 三、相关文档

  1. WXGameJetpack接入QA文档

https://docs.qq.com/doc/DRlh6RkhBUXpNdlVL?_t=1697005219736

  1. WXGamejetpack API文档

https://docs.qq.com/doc/DRnpxa3NlSUd6TXpi