最近刚用完互动直播sdk 做完直播,简单记录下。

一.旁路推流

首先先弄懂“旁路”的概念

凡是通过url观看的直播都是旁路直播,旁路直播要进行旁路推流,否则旁路直播无法观看直播。

向在app内我们是通过joinRoom() 加入的房间,所以不属于旁路直播。

二.主播方

大概流程是这个样子,根据自己需求进行变更

1.向服务器请求房间号。

2.根据房间号,创建房间。

public void createRoom() {

ILVLiveRoomOption hostOption = new ILVLiveRoomOption(”主播ID“)

.roomDisconnectListener(this)

.autoCamera(true)

.videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)

.controlRole(CurLiveInfo.getCurRole())

//角色是主播 一定不能设置错 主播走核心机房 观众走边缘机房

// .controlRole("LiveMaster")

.videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO);

int ret = ILVLiveManager.getInstance().createRoom(“房间ID”, hostOption, new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

if (getView()!=null)

getView().enterRoomComplete(MySelfInfo.getInstance().getIdStatus(), true);

ILiveLog.d(TAG, "ILVB-SXB|startEnterRoom->create room sucess");

}

@Override

public void onError(String module, int errCode, String errMsg) {

if (getView()!=null)

getView().errRoomFail();

ILiveLog.d(TAG, "ILVB-SXB|createRoom->create room failed:" + module + "|" + errCode + "|" + errMsg);

}

});

}

4.创建房间成功后有旁路直播开启旁路推流。

public void pushStream() {

/**

* 旁路直播 退出房间时必须退出推流。否则会占用后台channel。

*/

ILivePushOption option = new ILivePushOption();

option.encode(ILivePushOption.Encode.HLS);

// option.setRecordFileType(ILivePushOption.RecordFileType.RECORD_HLS_FLV_MP4);

option.setRecordFileType(ILivePushOption.RecordFileType.RECORD_HLS_FLV_MP4);

// option.channelName("跟ios协商");

//推流格式 到时候跟ios统一

pushStream(option);

}

/**

* 开启推流

*

* @param option

*/

public void pushStream(ILivePushOption option) {

if (!isPushed) {

ILiveRoomManager.getInstance().startPushStream(option, new ILiveCallBack() {

@Override

public void onSuccess(ILivePushRes data) {

// List liveUrls = data.getUrls();

Log.i("pushStream", "推流成功!!!!!!!");

}

@Override

public void onError(String module, int errCode, String errMsg) {

Log.i("pushStream", "推流失败!!!!!!!");

}

});

}

}

5.退出房间前:在房间发送自定义消息告诉观众主播要退出房间,方便观众端切换主播已离开界面,并停止推流。

发送自定义消息退出房间

public void sendExitRoomMSG() {

ILVCustomCmd cmd = new ILVCustomCmd();

cmd.setCmd(Constans.AVIMCMD_EXITLIVE);

cmd.setParam(sendExitRoomJson());

cmd.setType(ILVText.ILVTextType.eGroupMsg);

ILVLiveManager.getInstance().sendCustomCmd(cmd, new ILiveCallBack() {

@Override

public void onSuccess(TIMMessage data) {

if (isPushed) {

stopStream();

}

callExitRoom();

}

@Override

public void onError(String module, int errCode, String errMsg) {

}

});

}

停止推流

/**

* 退出直播的时候关闭推流

*/

public void stopStream() {

ILiveRoomManager.getInstance().stopPushStream(streamChannelID, new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

SxbLog.e(TAG, "stopPush->success");

isPushed = false;

}

@Override

public void onError(String module, int errCode, String errMsg) {

SxbLog.e(TAG, "stopPush->failed:" + module + "|" + errCode + "|" + errMsg);

}

});

}

6.退出房间。

private void callExitRoom() {

ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);

quitLiveRoom();

}

三.观众方

大概流程是这个样子,根据自己需求进行变更。

1.向服务器请求房间号。

2.根据房间号加入房间。

//加入房间

public void joinRoom(int roomId) {

ILVLiveManager.getInstance().quitRoom(null);

this.rommId = roomId + "";

ILVLiveRoomOption memberOption = new ILVLiveRoomOption(ConfigUtils.getUid())

.autoCamera(false)

.roomDisconnectListener(this)

.videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)

.controlRole("Guest")

.authBits(AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO)

.videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO)

.autoMic(false);

int ret = ILVLiveManager.getInstance().joinRoom(roomId, memberOption, new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

ILiveLog.d(TAG, "ILVB-Suixinbo|startEnterRoom->join room sucess");

if (null != mGuestLiveView)

mGuestLiveView.enterRoomComplete(true);

}

@Override

public void onError(String module, int errCode, String errMsg) {

LogUtils.d("suyan", "===========加入房间失败" + module + errCode + errMsg);

if (null != mGuestLiveView) {

mGuestLiveView.enterRoomComplete(false);

}

}

});

}

3.加入房间成功后,在房间发送自定义消息,告诉房间内有人加入,方便主播统计目前的房间人数。

/**

* 发送信令,点赞,进入房间

*/

public int sendGroupCmd(int cmd, String param) {

ILVCustomCmd customCmd = new ILVCustomCmd();

//1.传cmd

customCmd.setCmd(cmd);

//2.创建自定义数据

String json = sendCusJsn();

//3.传自定义数据

customCmd.setParam(json);

//4.设置自定义类型

customCmd.setType(ILVText.ILVTextType.eGroupMsg);

return sendCmd(customCmd);

}

4.退出房间前:发送房间自定义消息,告诉主播观众退出,方便主播统计目前的在线人数。

5.退出直播间。

public void startExitRoom() {

if (ILiveSDK.getInstance() != null && ILiveSDK.getInstance().getAvVideoCtrl() != null) {

ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);

}

quitLiveRoom();

}

private void quitLiveRoom() {

ILVLiveManager.getInstance().quitRoom(new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

//1.回调给页面

if (null != mGuestLiveView) {

LogUtils.d("suyan", "=========退出成功");

mGuestLiveView.quiteRoomComplete(true);

}

}

@Override

public void onError(String module, int errCode, String errMsg) {

if (null != mGuestLiveView) {

mGuestLiveView.quiteRoomComplete(false);

}

}

});

}

自己封装了一个观众端的使用类,也是根据互动直播demo改的

package com.jyjt.ydyl.txlive.presenters;

import android.app.Activity;

import android.content.Context;

import android.text.TextUtils;

import com.google.gson.Gson;

import com.google.gson.GsonBuilder;

import com.google.gson.JsonSyntaxException;

import com.jyjt.ydyl.Entity.LiveCusEntity;

import com.jyjt.ydyl.tools.ConfigUtils;

import com.jyjt.ydyl.tools.Constans;

import com.jyjt.ydyl.tools.LogUtils;

import com.jyjt.ydyl.tools.ToastUtil;

import com.jyjt.ydyl.txlive.LiveDetailActivity;

import com.jyjt.ydyl.txlive.event.LiveMessageEvent;

import com.jyjt.ydyl.txlive.views.GuestLiveView;

import com.tencent.TIMConversationType;

import com.tencent.TIMCustomElem;

import com.tencent.TIMElem;

import com.tencent.TIMElemType;

import com.tencent.TIMGroupSystemElem;

import com.tencent.TIMGroupSystemElemType;

import com.tencent.TIMMessage;

import com.tencent.av.sdk.AVRoomMulti;

import com.tencent.ilivesdk.ILiveCallBack;

import com.tencent.ilivesdk.ILiveConstants;

import com.tencent.ilivesdk.ILiveSDK;

import com.tencent.ilivesdk.core.ILiveLog;

import com.tencent.ilivesdk.core.ILiveRoomManager;

import com.tencent.ilivesdk.core.ILiveRoomOption;

import com.tencent.livesdk.ILVCustomCmd;

import com.tencent.livesdk.ILVLiveConstants;

import com.tencent.livesdk.ILVLiveManager;

import com.tencent.livesdk.ILVLiveRoomOption;

import com.tencent.livesdk.ILVText;

import com.tencent.openqq.protocol.imsdk.im_common;

import org.json.JSONObject;

import org.json.JSONTokener;

import java.util.Observable;

import java.util.Observer;

/**

* 观众端,直播帮助类

*

* 1.加入房间

* 2.离开房间

* 3.发送点赞,和加入房间指令

* 4.解析传过来的自定义消息(点赞,其他成员加入,用户退出直播, 达到上限,主播回来了)

* 5.房间链接断开监听

*

* 苏艳

*/

public class GuestLiveHelper implements ILiveRoomOption.onRoomDisconnectListener, Observer {

private final String TAG = "LiveHelper";

private GuestLiveView mGuestLiveView;

public Activity mContext;

private String rommId;

Gson gson;

public GuestLiveHelper(Activity context, GuestLiveView mGuestLiveView) {

mContext = context;

this.mGuestLiveView = mGuestLiveView;

LiveMessageEvent.getInstance().addObserver(this);

GsonBuilder builder = new GsonBuilder();

gson = builder.create();

}

//加入房间

public void joinRoom(int roomId) {

ILVLiveManager.getInstance().quitRoom(null);

this.rommId = roomId + "";

ILVLiveRoomOption memberOption = new ILVLiveRoomOption(ConfigUtils.getUid())

.autoCamera(false)

.roomDisconnectListener(this)

.videoMode(ILiveConstants.VIDEOMODE_BSUPPORT)

.controlRole("Guest")

.authBits(AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO)

.videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO)

.autoMic(false);

int ret = ILVLiveManager.getInstance().joinRoom(roomId, memberOption, new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

ILiveLog.d(TAG, "ILVB-Suixinbo|startEnterRoom->join room sucess");

if (null != mGuestLiveView)

mGuestLiveView.enterRoomComplete(true);

}

@Override

public void onError(String module, int errCode, String errMsg) {

LogUtils.d("suyan", "===========加入房间失败" + module + errCode + errMsg);

if (null != mGuestLiveView) {

mGuestLiveView.enterRoomComplete(false);

}

}

});

}

//退出房间

public void startExitRoom() {

if (ILiveSDK.getInstance() != null && ILiveSDK.getInstance().getAvVideoCtrl() != null) {

ILiveSDK.getInstance().getAvVideoCtrl().setLocalVideoPreProcessCallback(null);

}

quitLiveRoom();

}

private void quitLiveRoom() {

ILVLiveManager.getInstance().quitRoom(new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

//1.回调给页面

if (null != mGuestLiveView) {

LogUtils.d("suyan", "=========退出成功");

mGuestLiveView.quiteRoomComplete(true);

}

}

@Override

public void onError(String module, int errCode, String errMsg) {

if (null != mGuestLiveView) {

mGuestLiveView.quiteRoomComplete(false);

}

}

});

}

/**

* 发送信令,点赞,进入房间

*/

public int sendGroupCmd(int cmd, String param) {

ILVCustomCmd customCmd = new ILVCustomCmd();

//1.传cmd

customCmd.setCmd(cmd);

//2.创建自定义数据

String json = sendCusJsn();

//3.传自定义数据

customCmd.setParam(json);

//4.设置自定义类型

customCmd.setType(ILVText.ILVTextType.eGroupMsg);

return sendCmd(customCmd);

}

//邀请上麦用的,暂时用不到

public int sendC2CCmd(final int cmd, String param, String destId) {

ILVCustomCmd customCmd = new ILVCustomCmd();

customCmd.setDestId(destId);

customCmd.setCmd(cmd);

customCmd.setParam(param);

customCmd.setType(ILVText.ILVTextType.eC2CMsg);

return sendCmd(customCmd);

}

private int sendCmd(final ILVCustomCmd cmd) {

return ILVLiveManager.getInstance().sendCustomCmd(cmd, new ILiveCallBack() {

@Override

public void onSuccess(Object data) {

LogUtils.d(TAG, "suyan" + cmd.getCmd() + "|" + cmd.getParam() + "====发送成功");

}

@Override

public void onError(String module, int errCode, String errMsg) {

// Toast.makeText(mContext, "sendCmd->failed:" + module + "|" + errCode + "|" + errMsg, Toast.LENGTH_SHORT).show();

}

});

}

@Override

public void onRoomDisconnect(int errCode, String errMsg) {

if (null != mGuestLiveView) {

//房间异常退出,通常指断网,或者没电

mGuestLiveView.roomDisconnect(errCode, errMsg);

}

}

// 解析文本消息

private void processTextMsg(LiveMessageEvent.SxbMsgInfo info) {

if (null == info.data || !(info.data instanceof ILVText)) {

return;

}

ILVText text = (ILVText) info.data;

if (text.getType() == ILVText.ILVTextType.eGroupMsg

&& !text.getDestId().equals(rommId)) {

if (TextUtils.isEmpty(rommId)) {

LogUtils.d("suyan","========当前的roomid为空");

}

return;

}

String name = info.senderId;

if (null != info.profile && !TextUtils.isEmpty(info.profile.getNickName())) {

name = info.profile.getNickName();

}

if (null != mGuestLiveView)

mGuestLiveView.refreshText(text.getText(), name);

}

// 解析自定义信令

private void processCmdMsg(LiveMessageEvent.SxbMsgInfo info) {

if (null == info.data || !(info.data instanceof ILVCustomCmd)) {

return;

}

ILVCustomCmd cmd = (ILVCustomCmd) info.data;

if (cmd.getType() == ILVText.ILVTextType.eGroupMsg

&& !cmd.getDestId().equals(rommId)) {

if (TextUtils.isEmpty(rommId)) {

LogUtils.d("suyan","========当前的roomid为空");

}

return;

}

String name = info.senderId;

if (null != info.profile && !TextUtils.isEmpty(info.profile.getNickName())) {

name = info.profile.getNickName();

}

handleCustomMsg(cmd.getCmd(), cmd.getParam(), info.senderId, name);

}

private void processOtherMsg(LiveMessageEvent.SxbMsgInfo info) {

if (null == info.data || !(info.data instanceof TIMMessage)) {

return;

}

TIMMessage currMsg = (TIMMessage) info.data;

// 过滤非当前群组消息

if (currMsg.getConversation() != null && currMsg.getConversation().getPeer() != null) {

if (currMsg.getConversation().getType() == TIMConversationType.Group

&& !rommId.equals(currMsg.getConversation().getPeer())) {

if (TextUtils.isEmpty(rommId)) {

LogUtils.d("suyan","========当前的roomid为空");

}

return;

}

}

for (int j = 0; j < currMsg.getElementCount(); j++) {

if (currMsg.getElement(j) == null)

continue;

TIMElem elem = currMsg.getElement(j);

TIMElemType type = elem.getType();

//系统消息

if (type == TIMElemType.GroupSystem) { // 群组解散消息

if (TIMGroupSystemElemType.TIM_GROUP_SYSTEM_DELETE_GROUP_TYPE == ((TIMGroupSystemElem) elem).getSubtype()) {

if (null != mGuestLiveView)

mGuestLiveView.dismissGroup("host", null);

}

} else if (type == TIMElemType.Custom) {

try {

final String strMagic = "__ACTION__";

String customText = new String(((TIMCustomElem) elem).getData(), "UTF-8");

if (!customText.startsWith(strMagic)) // 检测前缀

continue;

JSONTokener jsonParser = new JSONTokener(customText.substring(strMagic.length() + 1));

JSONObject json = (JSONObject) jsonParser.nextValue();

String action = json.optString("action", "");

if (action.equals("force_exit_room") || action.equals("force_disband_room")) {

JSONObject objData = json.getJSONObject("data");

String strRoomNum = objData.optString("room_num", "");

if (strRoomNum.equals(String.valueOf(ILiveRoomManager.getInstance().getRoomId()))) {

if (null != mGuestLiveView) {

mGuestLiveView.forceQuitRoom("管理员已将房间解散或将您踢出房间!");

}

}

}

} catch (Exception e) {

}

}

}

}

@Override

public void update(Observable observable, Object o) {

LiveMessageEvent.SxbMsgInfo info = (LiveMessageEvent.SxbMsgInfo) o;

LogUtils.d("suyan", "======收到消息" + info.msgType);

switch (info.msgType) {

case LiveMessageEvent.MSGTYPE_TEXT:

processTextMsg(info);

break;

case LiveMessageEvent.MSGTYPE_CMD:

processCmdMsg(info);

break;

case LiveMessageEvent.MSGTYPE_OTHER:

LogUtils.d("suyan", "=======其他消息");

processOtherMsg(info);

case LiveMessageEvent.MSGTYPE_DDETAIL:

LogUtils.d("suyan", "=======刷新房间数据");

processDetailMsg(info);

break;

}

}

// 解析自定义信令

private void processDetailMsg(LiveMessageEvent.SxbMsgInfo info) {

if (null != info && !TextUtils.isEmpty(info.senderId)) {

if (null != mGuestLiveView) {

mGuestLiveView.refreshData(info.senderId);

}

}

}

private void handleCustomMsg(int action, String param, String identifier, String nickname) {

LogUtils.d(TAG, "handleCustomMsg->action: " + action);

if (null == mGuestLiveView) {

return;

}

LiveCusEntity mliveCusEntity = updateGson(param);

if (mliveCusEntity != null) {

LogUtils.d("suyan", "===========解析后的数据" + mliveCusEntity.getContent().getLike_num() + "=" + mliveCusEntity.getContent().getOnline_num() + "=" + mliveCusEntity.getUser().getUid());

}

switch (action) {

case Constans.AVIMCMD_PRAISE://点赞

mGuestLiveView.refreshGoodLike();

break;

case Constans.AVIMCMD_ENTERLIVE://其他成员加入

mGuestLiveView.memberJoin(identifier, nickname);

break;

case Constans.AVIMCMD_EXITLIVE: //用户退出直播

mGuestLiveView.hostOffline(identifier, nickname);

break;

case ILVLiveConstants.ILVLIVE_CMD_LINKROOM_LIMIT: // 达到上限

LogUtils.d("suyan","========到达上限");

break;

case Constans.AVIMCMD_HOST_BACK://主播回来了

LogUtils.d("suyan","========主播回来了");

//昵称:nickname id:identifier

case Constans.AVIMCMD_PRAISE_TOTAL://观众进入房间,主播发送现在的总点赞数

try {

if (mliveCusEntity != null && !TextUtils.isEmpty(mliveCusEntity.getContent().getLike_num())) {

mGuestLiveView.refreshTotalGoodLike(Long.parseLong(mliveCusEntity.getContent().getLike_num()));

}

} catch (NumberFormatException e) {

e.printStackTrace();

}

default:

break;

}

}

//解析jsong

public LiveCusEntity updateGson(String jsonTest) {

if (!TextUtils.isEmpty(jsonTest)) {

LiveCusEntity mLiveCusEntity;

try {

mLiveCusEntity = gson.fromJson(jsonTest, LiveCusEntity.class);

return mLiveCusEntity;

} catch (JsonSyntaxException e) {

e.printStackTrace();

return null;

}

} else {

return null;

}

}

//发送json

public String sendCusJsn() {

LiveCusEntity liveCusEntity = new LiveCusEntity();

LiveCusEntity.LiveContent mLiveContent = new LiveCusEntity.LiveContent();

liveCusEntity.setContent(mLiveContent);

LiveCusEntity.User mUser = new LiveCusEntity.User();

mUser.setUid(ConfigUtils.getUid());

liveCusEntity.setUser(mUser);

String json = gson.toJson(liveCusEntity, LiveCusEntity.class);

LogUtils.d("suyan", "==========自定义数据" + json);

return json;

}

//销毁

public void onDestory() {

mGuestLiveView = null;

mContext = null;

LiveMessageEvent.getInstance().deleteObserver(this);

ILVLiveManager.getInstance().quitRoom(null);

ILiveRoomManager.getInstance().onDestory();

ILVLiveManager.getInstance().onDestory();

}

}

java ilvmanagerview_Android 腾讯互动直播集成相关推荐

  1. 集成腾讯互动直播之大咖模式采坑记录

    最近在做互动直播,公司最后选择使用腾讯的直播sdk,而且为了增强用户体验,选择了腾讯互动直播的大咖模式.这里不得不吐槽一下,腾讯的sdk文档写的不是烂,而是非常烂,让人看的云里雾里的,结果就是大家都在 ...

  2. 腾讯直播与 JAVA整合_JAVA对接腾讯云直播如何实现 JAVA对接腾讯云直播实现代码...

    JAVA对接腾讯云直播如何实现?本篇文章小编给大家分享一下JAVA对接腾讯云直播实现代码,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. 签名授权 public static ...

  3. Java实现腾讯云直播生成推流地址和播放地址

    最近在做小程序直播,用到了腾讯云直播,下面为相关文档和程序 URL规则:https://cloud.tencent.com/document/product/267/13457 防盗链计算:https ...

  4. 腾讯互动直播php,【模块教程】腾讯云视频互动直播(tencentTrtcLiveRoom)教程

    ##**腾讯云视频互动直播** 腾讯云视频互动直播模块,提供互动直播的功能,包括直播.连麦PK.主播 PK.低延时观看.弹幕聊天等在互动直播场景下的相关能力. ##**技术支持** 原生模块本身使用复 ...

  5. java 采用腾讯云直播实现多方视频录制并每路画面添加相应的水印

    这是我第一篇文章,本人也是菜鸟,如果有什么不对,也请大神多多指点 话不多说,进入正题. 首先录制视频的前提是推流和拉流同时是连接上才能进行录制工作.否则腾讯云不会给你返回录制的视频地址. 如果你不知道 ...

  6. java实现腾讯云直播

    云直播官方文档:https://cloud.tencent.com/document/product/267 云直播在线生成api:https://console.cloud.tencent.com/ ...

  7. 腾讯云直播集成app时报错 Duplicate class com.xxx:

    报错信息: Duplicate class com.tencent.ugc.TXRecordCommon$TXUGCSimpleConfig found in modules jetified-Lit ...

  8. 腾讯云直播代码 java_JAVA 对接腾讯云直播的实现

    签名授权 public static T TecentDoPostJsonV3(String url,String key,String secretId, TecentPublicParams he ...

  9. 腾讯云互动直播SDK集成综述

    SDK构成 互动直播SDK由两部分构成,IMSDK和AVSDK 其中IMSDK为云通信SDK,为AVSDK提供账号登录.信令通道.IM弹幕消息.日志模块.日志上报等功能 AVSDK提供摄像头采集.编码 ...

最新文章

  1. 定制简单的Linux系统
  2. SWPU ACM2020 年预备队员选拔赛 题解
  3. AI 质检学习报告——学习篇——AI质检产生背景和发展过程
  4. vue a-sub-menu 添加点击事件报错_Vue+TS 使用的问题(持续更)
  5. Android内存泄漏
  6. java压缩linux上的文件,java linux下文件压缩
  7. freeradius mysql ad_freeradiusmysql简单配置一例
  8. Summits poj3503
  9. 答疑解惑 | 关于PMBOK第七版,常见问题合集【附电子版】
  10. 如何用 js 获取虚拟键盘高度?- 20170817 前端开发日报
  11. 【手写汉字识别】基于深度学习的脱机手写汉字识别技术研究
  12. python 条形图填充疏密_Python数据分析 4:图表绘制工具Matplotlib
  13. 蓝牙技术|了解蓝牙LE Audio的Auracast广播音频
  14. 微信小程序点击分享功能
  15. 7-2 分解素因子 (10 分)
  16. [算法]LeetCode第194场周赛202006021
  17. Bootstrap中的utilities(工具类)
  18. 应用是非正式发布版本, 当前设备不支持安装。
  19. 无人驾驶面临的伦理问题
  20. 仿牛客论坛项目(上)

热门文章

  1. tensorflow00:windows下训练并测试MNIST数字识别详细笔记
  2. scala类型系统之: 类型变量界定、视图界定、上下文界定
  3. 【论文译文】Few Shot Vid-to-Vid
  4. 用Mendeley在Word插入参考文献
  5. win10删除skype方法
  6. xuex 详解以及使用
  7. c++demo基础入门教学第一课(写标准框架)
  8. 匈牙利匹配算法_学习笔记_Python编程实现
  9. advanced mathematics 8
  10. ADS 如何切换不同PDK之间的版图?