permissions = new ArrayList<>();
+ if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+ permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ }
+ if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)) {
+ permissions.add(Manifest.permission.CAMERA);
+ }
+ if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)) {
+ permissions.add(Manifest.permission.RECORD_AUDIO);
+ }
+ if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ }
+ if (permissions.size() != 0) {
+ ActivityCompat.requestPermissions(TRTCBaseActivity.this,
+ permissions.toArray(new String[0]),
+ REQ_PERMISSION_CODE);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ switch (requestCode) {
+ case REQ_PERMISSION_CODE:
+ for (int ret : grantResults) {
+ if (PackageManager.PERMISSION_GRANTED == ret) {
+ mGrantedCount ++;
+ }
+ }
+ if (mGrantedCount == permissions.length) {
+ onPermissionGranted();
+ } else {
+ Toast.makeText(this, getString(getId("common_please_input_roomid_and_userid", "string")), Toast.LENGTH_SHORT).show();
+ }
+ mGrantedCount = 0;
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/android/java/com/tencent/trtc/debug/Constant.java b/src/android/java/com/tencent/trtc/debug/Constant.java
new file mode 100644
index 0000000..d5e4120
--- /dev/null
+++ b/src/android/java/com/tencent/trtc/debug/Constant.java
@@ -0,0 +1,27 @@
+package com.tencent.trtc.debug;
+
+public class Constant {
+
+ public static final String ROOM_ID = "room_id";
+ public static final String USER_ID = "user_id";
+ public static final String SDK_APP_ID = "sdk_app_id";
+ public static final String USER_SIG = "user_sig";
+ public static final String ROLE_TYPE = "role_type";
+ public static final String CUSTOM_VIDEO = "custom_video";
+
+ // 美颜风格.三种美颜风格:0 :光滑 1:自然 2:朦胧
+ public static final int BEAUTY_STYLE_SMOOTH = 0;
+ public static final int BEAUTY_STYLE_NATURE = 1;
+
+ public static final int VIDEO_FPS = 15;
+ // RTC 通话场景:640*360,15fps,550kbps
+ public static final int RTC_VIDEO_BITRATE = 550;
+
+ // 直播场景:连麦小主播:270*480, 15pfs, 400kbps
+ public static final int LIVE_270_480_VIDEO_BITRATE = 400;
+ public static final int LIVE_360_640_VIDEO_BITRATE = 800;
+ // 直播场景:大主播:默认 540*960, 15fps,1200kbps
+ public static final int LIVE_540_960_VIDEO_BITRATE = 900;
+ public static final int LIVE_720_1280_VIDEO_BITRATE = 1500;
+
+}
diff --git a/src/android/java/com/tencent/trtc/debug/GenerateTestUserSig.java b/src/android/java/com/tencent/trtc/debug/GenerateTestUserSig.java
new file mode 100644
index 0000000..0826965
--- /dev/null
+++ b/src/android/java/com/tencent/trtc/debug/GenerateTestUserSig.java
@@ -0,0 +1,280 @@
+package com.tencent.trtc.debug;
+
+
+import android.util.Base64;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.zip.Deflater;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Module: GenerateTestUserSig
+ *
+ * Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
+ * 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
+ *
+ * Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
+ *
+ * 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
+ * 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
+ * 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
+ *
+ * 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
+ * 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
+ *
+ * Reference:https://cloud.tencent.com/document/product/647/17275#Server
+ */
+
+/**
+ * Module: GenerateTestUserSig
+ *
+ * Description: generates UserSig for testing. UserSig is a security signature designed by Tencent Cloud for its cloud services.
+ * It is calculated based on `SDKAppID`, `UserID`, and `EXPIRETIME` using the HMAC-SHA256 encryption algorithm.
+ *
+ * Attention: do not use the code below in your commercial app. This is because:
+ *
+ * The code may be able to calculate UserSig correctly, but it is only for quick testing of the SDK’s basic features, not for commercial apps.
+ * `SECRETKEY` in client code can be easily decompiled and reversed, especially on web.
+ * Once your key is disclosed, attackers will be able to steal your Tencent Cloud traffic.
+ *
+ * The correct method is to deploy the `UserSig` calculation code and encryption key on your project server so that your app can request from your server a `UserSig` that is calculated whenever one is needed.
+ * Given that it is more difficult to hack a server than a client app, server-end calculation can better protect your key.
+ *
+ * Reference: https://cloud.tencent.com/document/product/647/17275#Server
+ */
+public class GenerateTestUserSig {
+
+ /**
+ * 配置为CDN发布、混流的域名
+ *
+ */
+
+ /**
+ * Domain name for CDN publishing and stream mixing
+ */
+ public static final String CDN_DOMAIN_NAME = "testuser";
+
+ /**
+ * CDN发布功能 混流bizId
+ */
+
+ /**
+ * `bizId` for CDN publishing and stream mixing
+ */
+ public static final int BIZID = 111;
+
+ /**
+ * CDN发布功能 混流appId
+ */
+
+ /**
+ * `appId` for CDN publishing and stream mixing
+ */
+ public static final int APPID = 111;
+
+ /**
+ * 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
+ *
+ * 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
+ * 它是腾讯云用于区分客户的唯一标识。
+ */
+
+ /**
+ * Tencent Cloud `SDKAppID`. Set it to the `SDKAppID` of your account.
+ *
+ * You can view your `SDKAppID` after creating an application in the [TRTC console](https://console.cloud.tencent.com/rav).
+ * `SDKAppID` uniquely identifies a Tencent Cloud account.
+ */
+ public static final int SDKAPPID = 1400593044;
+
+ /**
+ * 签名过期时间,建议不要设置的过短
+ *
+ * 时间单位:秒
+ * 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
+ */
+
+ /**
+ * Signature validity period, which should not be set too short
+ *
+ * Unit: second
+ * Default value: 604800 (7 days)
+ */
+ private static final int EXPIRETIME = 604800;
+
+
+ /**
+ * 计算签名用的加密密钥,获取步骤如下:
+ *
+ * step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
+ * step2. 单击应用信息,并进一步找到“快速上手”部分。
+ * step3. 点击“复制密钥”按钮,复制密钥,请将其拷贝并复制到如下的变量中
+ *
+ * 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
+ * 文档:https://cloud.tencent.com/document/product/647/17275#Server
+ */
+
+ /**
+ * Follow the steps below to obtain the key required for UserSig calculation.
+ *
+ * Step 1. Log in to the [TRTC console](https://console.cloud.tencent.com/rav), and create an application if you don’t have one.
+ * Step 2. Find your application, click “Application Info”, and click the “Quick Start” tab.
+ * Step 3. Copy and paste the key to the code, as shown below.
+ *
+ * Note: this method is for testing only. Before commercial launch, please migrate the UserSig calculation code and key to your backend server to prevent key disclosure and traffic stealing.
+ * Reference: https://cloud.tencent.com/document/product/647/17275#Server
+ */
+ public static final String SECRETKEY = "f814a0480cfa95a3ffffd8891844f3cd36888166dbef5347303043744f247c06";
+
+ /**
+ * 计算 UserSig 签名
+ *
+ * 函数内部使用 HMAC-SHA256 非对称加密算法,对 SDKAPPID、userId 和 EXPIRETIME 进行加密。
+ *
+ * @note: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
+ *
+ * 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
+ * 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
+ * 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
+ *
+ * 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
+ * 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
+ *
+ * 文档:https://cloud.tencent.com/document/product/647/17275#Server
+ */
+
+ /**
+ * Calculating UserSig
+ *
+ * The asymmetric encryption algorithm HMAC-SHA256 is used in the function to calculate UserSig based on `SDKAppID`, `UserID`, and `EXPIRETIME`.
+ *
+ * @note: do not use the code below in your commercial app. This is because:
+ *
+ * The code may be able to calculate UserSig correctly, but it is only for quick testing of the SDK’s basic features, not for commercial apps.
+ * `SECRETKEY` in client code can be easily decompiled and reversed, especially on web.
+ * Once your key is disclosed, attackers will be able to steal your Tencent Cloud traffic.
+ *
+ * The correct method is to deploy the `UserSig` calculation code on your project server so that your app can request from your server a `UserSig` that is calculated whenever one is needed.
+ * Given that it is more difficult to hack a server than a client app, server-end calculation can better protect your key.
+ *
+ * Reference: https://cloud.tencent.com/document/product/647/17275#Server
+ */
+ public static String genTestUserSig(String userId) {
+ return GenTLSSignature(SDKAPPID, userId, EXPIRETIME, null, SECRETKEY);
+ }
+
+ /**
+ * 生成 tls 票据
+ *
+ * @param sdkappid 应用的 appid
+ * @param userId 用户 id
+ * @param expire 有效期,单位是秒
+ * @param userbuf 默认填写null
+ * @param priKeyContent 生成 tls 票据使用的私钥内容
+ * @return 如果出错,会返回为空,或者有异常打印,成功返回有效的票据
+ */
+
+ /**
+ * Generating a TLS Ticket
+ *
+ * @param sdkappid `appid` of your application
+ * @param userId User ID
+ * @param expire Validity period, in seconds
+ * @param userbuf `null` by default
+ * @param priKeyContent Private key required for generating a TLS ticket
+ * @return If an error occurs, an empty string will be returned or exceptions printed. If the operation succeeds, a valid ticket will be returned.
+ */
+ private static String GenTLSSignature(long sdkappid, String userId, long expire, byte[] userbuf, String priKeyContent) {
+ long currTime = System.currentTimeMillis() / 1000;
+ JSONObject sigDoc = new JSONObject();
+ try {
+ sigDoc.put("TLS.ver", "2.0");
+ sigDoc.put("TLS.identifier", userId);
+ sigDoc.put("TLS.sdkappid", sdkappid);
+ sigDoc.put("TLS.expire", expire);
+ sigDoc.put("TLS.time", currTime);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ String base64UserBuf = null;
+ if (null != userbuf) {
+ base64UserBuf = Base64.encodeToString(userbuf, Base64.NO_WRAP);
+ try {
+ sigDoc.put("TLS.userbuf", base64UserBuf);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ String sig = hmacsha256(sdkappid, userId, currTime, expire, priKeyContent, base64UserBuf);
+ if (sig.length() == 0) {
+ return "";
+ }
+ try {
+ sigDoc.put("TLS.sig", sig);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ Deflater compressor = new Deflater();
+ compressor.setInput(sigDoc.toString().getBytes(Charset.forName("UTF-8")));
+ compressor.finish();
+ byte[] compressedBytes = new byte[2048];
+ int compressedBytesLength = compressor.deflate(compressedBytes);
+ compressor.end();
+ return new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)));
+ }
+
+
+ private static String hmacsha256(long sdkappid, String userId, long currTime, long expire, String priKeyContent, String base64Userbuf) {
+ String contentToBeSigned = "TLS.identifier:" + userId + "\n"
+ + "TLS.sdkappid:" + sdkappid + "\n"
+ + "TLS.time:" + currTime + "\n"
+ + "TLS.expire:" + expire + "\n";
+ if (null != base64Userbuf) {
+ contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
+ }
+ try {
+ byte[] byteKey = priKeyContent.getBytes("UTF-8");
+ Mac hmac = Mac.getInstance("HmacSHA256");
+ SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");
+ hmac.init(keySpec);
+ byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes("UTF-8"));
+ return new String(Base64.encode(byteSig, Base64.NO_WRAP));
+ } catch (UnsupportedEncodingException e) {
+ return "";
+ } catch (NoSuchAlgorithmException e) {
+ return "";
+ } catch (InvalidKeyException e) {
+ return "";
+ }
+ }
+
+ private static byte[] base64EncodeUrl(byte[] input) {
+ byte[] base64 = new String(Base64.encode(input, Base64.NO_WRAP)).getBytes();
+ for (int i = 0; i < base64.length; ++i)
+ switch (base64[i]) {
+ case '+':
+ base64[i] = '*';
+ break;
+ case '/':
+ base64[i] = '-';
+ break;
+ case '=':
+ base64[i] = '_';
+ break;
+ default:
+ break;
+ }
+ return base64;
+ }
+
+}
diff --git a/src/android/java/com/tencent/trtc/videocall/BuildConfig.java b/src/android/java/com/tencent/trtc/videocall/BuildConfig.java
new file mode 100644
index 0000000..1371d8a
--- /dev/null
+++ b/src/android/java/com/tencent/trtc/videocall/BuildConfig.java
@@ -0,0 +1,18 @@
+/**
+ * Automatically generated file. DO NOT MODIFY
+ */
+package com.tencent.trtc.videocall;
+
+public final class BuildConfig {
+ public static final boolean DEBUG = Boolean.parseBoolean("true");
+ public static final String LIBRARY_PACKAGE_NAME = "com.tencent.trtc.videocall";
+ /**
+ * @deprecated APPLICATION_ID is misleading in libraries. For the library package name use LIBRARY_PACKAGE_NAME
+ */
+ @Deprecated
+ public static final String APPLICATION_ID = "com.tencent.trtc.videocall";
+ public static final String BUILD_TYPE = "debug";
+ public static final String FLAVOR = "";
+ public static final int VERSION_CODE = 1;
+ public static final String VERSION_NAME = "1.0";
+}
diff --git a/src/android/java/com/tencent/trtc/videocall/FloatingView.java b/src/android/java/com/tencent/trtc/videocall/FloatingView.java
new file mode 100644
index 0000000..2259163
--- /dev/null
+++ b/src/android/java/com/tencent/trtc/videocall/FloatingView.java
@@ -0,0 +1,193 @@
+package com.tencent.trtc.videocall;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow;
+
+/**
+ * 悬浮球,点击可以弹出菜单
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class FloatingView extends FrameLayout implements GestureDetector.OnGestureListener {
+
+ private Context mContext;
+ private WindowManager mWindowManager;
+ private GestureDetector mGestureDetector;
+ private WindowManager.LayoutParams mLayoutParams;
+ private float mLastX;
+ private float mLastY;
+ private PopupWindow mPopupWindow;
+ private long mTapOutsideTime;
+ private boolean mIsShowing = false;
+
+ public FloatingView(Context context) {
+ super(context);
+ this.mContext = context;
+ this.mGestureDetector = new GestureDetector(context, this);
+ }
+
+ public FloatingView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.mContext = context;
+ this.mGestureDetector = new GestureDetector(context, this);
+ }
+
+ public FloatingView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ this.mContext = context;
+ this.mGestureDetector = new GestureDetector(context, this);
+ }
+
+ public FloatingView(Context context, int viewResId) {
+ super(context);
+ this.mContext = context;
+ View.inflate(context, viewResId, this);
+ this.mGestureDetector = new GestureDetector(context, this);
+ }
+
+ public void showView(View view) {
+ showView(view, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
+ }
+
+ public void showView(View view, int width, int height) {
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ int type = WindowManager.LayoutParams.TYPE_TOAST;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
+ type = WindowManager.LayoutParams.TYPE_PHONE;
+ }
+ mLayoutParams = new WindowManager.LayoutParams(type);
+ mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLayoutParams.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ mLayoutParams.width = width;
+ mLayoutParams.height = height;
+ mLayoutParams.format = PixelFormat.TRANSLUCENT;
+ mWindowManager.addView(view, mLayoutParams);
+ }
+
+ public void hideView() {
+ if (null != mWindowManager) {
+ mWindowManager.removeViewImmediate(this);
+ }
+ mWindowManager = null;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ mLastX = e.getRawX();
+ mLastY = e.getRawY();
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ float nowX, nowY, tranX, tranY;
+ nowX = e2.getRawX();
+ nowY = e2.getRawY();
+ tranX = nowX - mLastX;
+ tranY = nowY - mLastY;
+ mLayoutParams.x += tranX;
+ mLayoutParams.y += tranY;
+ mWindowManager.updateViewLayout(this, mLayoutParams);
+ mLastX = nowX;
+ mLastY = nowY;
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ return false;
+ }
+
+ public void setPopupWindow(int id) {
+ mPopupWindow = new PopupWindow(this);
+ mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
+ mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
+ mPopupWindow.setTouchable(true);
+ mPopupWindow.setOutsideTouchable(true);
+ mPopupWindow.setFocusable(false);
+ mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
+ mPopupWindow.setContentView(LayoutInflater.from(getContext()).inflate(id, null));
+ mPopupWindow.setTouchInterceptor(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mPopupWindow.dismiss();
+ mTapOutsideTime = System.currentTimeMillis();
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ public View getPopupView() {
+ return mPopupWindow.getContentView();
+ }
+
+ public void setOnPopupItemClickListener(OnClickListener listener) {
+ if (mPopupWindow == null)
+ return;
+
+ ViewGroup layout = (ViewGroup) mPopupWindow.getContentView();
+ for (int i = 0; i < layout.getChildCount(); i++) {
+ layout.getChildAt(i).setOnClickListener(listener);
+ }
+ }
+
+ public void show() {
+ if (!mIsShowing) {
+ showView(this);
+ }
+ mIsShowing = true;
+ }
+
+ public void dismiss() {
+ if (mIsShowing) {
+ hideView();
+ }
+ mIsShowing = false;
+ ViewGroup layout = (ViewGroup) mPopupWindow.getContentView();
+ for (int i = 0; i < layout.getChildCount(); i++) {
+ layout.getChildAt(i).setOnClickListener(null);
+ }
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ if (null != mPopupWindow)
+ mPopupWindow.dismiss();
+ if (!(System.currentTimeMillis() - mTapOutsideTime < 80)) {
+ mPopupWindow.showAtLocation(this, Gravity.NO_GRAVITY, 100, 0);
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/android/java/com/tencent/trtc/videocall/VideoCallingActivity.java b/src/android/java/com/tencent/trtc/videocall/VideoCallingActivity.java
new file mode 100644
index 0000000..4072c97
--- /dev/null
+++ b/src/android/java/com/tencent/trtc/videocall/VideoCallingActivity.java
@@ -0,0 +1,397 @@
+package com.tencent.trtc.videocall;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import android.java.com.example.basic.TRTCBaseActivity;
+
+import com.tencent.liteav.TXLiteAVCode;
+import com.tencent.liteav.device.TXDeviceManager;
+import com.tencent.rtmp.ui.TXCloudVideoView;
+import com.tencent.trtc.TRTCCloud;
+import com.tencent.trtc.TRTCCloudDef;
+import com.tencent.trtc.TRTCCloudListener;
+import com.tencent.trtc.debug.Constant;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TRTC视频通话的主页面
+ *
+ * 包含如下简单功能:
+ * - 进入视频通话房间{@link VideoCallingActivity#enterRoom()}
+ * - 退出视频通话房间{@link VideoCallingActivity#exitRoom()}
+ * - 切换前置/后置摄像头{@link VideoCallingActivity#switchCamera()}
+ * - 打开/关闭摄像头{@link VideoCallingActivity#muteVideo()}
+ * - 打开/关闭麦克风{@link VideoCallingActivity#muteAudio()}
+ * - 显示房间内其他用户的视频画面(当前示例最多可显示6个其他用户的视频画面){@link TRTCCloudImplListener#refreshRemoteVideoViews()}
+ *
+ * - 详见接入文档{https://cloud.tencent.com/document/product/647/42045}
+ */
+
+/**
+ * Video Call
+ *
+ * Features:
+ * - Enter a video call room: {@link VideoCallingActivity#enterRoom()}
+ * - Exit a video call room: {@link VideoCallingActivity#exitRoom()}
+ * - Switch between the front and rear cameras: {@link VideoCallingActivity#switchCamera()}
+ * - Turn on/off the camera: {@link VideoCallingActivity#muteVideo()}
+ * - Turn on/off the mic: {@link VideoCallingActivity#muteAudio()}
+ * - Display the video of other users (max. 6) in the room: {@link TRTCCloudImplListener#refreshRemoteVideoViews()}
+ *
+ * - For more information, please see the integration document {https://cloud.tencent.com/document/product/647/42045}.
+ */
+public class VideoCallingActivity extends TRTCBaseActivity implements View.OnClickListener {
+
+ private static final String TAG = "VideoCallingActivity";
+ private static final int OVERLAY_PERMISSION_REQ_CODE = 1234;
+
+ private TextView mTextTitle;
+ private TXCloudVideoView mTXCVVLocalPreviewView;
+ private ImageView mImageBack;
+ private ImageView mButtonMuteVideo;
+ private ImageView mButtonMuteAudio;
+ private ImageView mButtonSwitchCamera;
+ private ImageView mButtonAudioRoute;
+
+ private TRTCCloud mTRTCCloud;
+ private TXDeviceManager mTXDeviceManager;
+ private boolean mIsFrontCamera = true;
+ private List mRemoteUidList;
+ private List mRemoteViewList;
+ private int mUserCount = 0;
+ private String mRoomId;
+ private String mUserId;
+ private int sdkAppId;
+ private String userSig;
+ private boolean mAudioRouteFlag = true;
+ private FloatingView mFloatingView;
+
+ private int getId(String idName, String type) {
+ return getResources().getIdentifier(idName, type, getPackageName());
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+// R.layout.videocall_activity_calling
+ setContentView(getId("videocall_activity_calling", "layout"));
+ getSupportActionBar().hide();
+ handleIntent();
+
+ if (checkPermission()) {
+ initView();
+ enterRoom();
+ }
+ }
+
+ private void handleIntent() {
+ Intent intent = getIntent();
+ if (null != intent) {
+ if (intent.getStringExtra(Constant.USER_ID) != null) {
+ mUserId = intent.getStringExtra(Constant.USER_ID);
+ }
+ if (intent.getStringExtra(Constant.ROOM_ID) != null) {
+ mRoomId = intent.getStringExtra(Constant.ROOM_ID);
+ }
+
+ if (intent.getIntExtra(Constant.SDK_APP_ID, 0) != 0) {
+ sdkAppId = intent.getIntExtra(Constant.SDK_APP_ID, 0);
+ }
+
+ if (intent.getStringExtra(Constant.USER_SIG) != null) {
+ userSig = intent.getStringExtra(Constant.USER_SIG);
+ }
+ }
+ System.out.println("sdkAppId = " + sdkAppId);
+ System.out.println("userSig = " + userSig);
+ }
+
+ private void initView() {
+// R.id.tv_room_number
+ mTextTitle = findViewById(getId("tv_room_number", "id"));
+// R.id.iv_back
+ mImageBack = findViewById(getId("iv_back", "id"));
+// R.id.txcvv_main
+ mTXCVVLocalPreviewView = findViewById(getId("txcvv_main", "id"));
+// R.id.btn_mute_video
+ mButtonMuteVideo = findViewById(getId("btn_mute_video", "id"));
+// R.id.btn_mute_audio
+ mButtonMuteAudio = findViewById(getId("btn_mute_audio", "id"));
+// R.id.btn_switch_camera
+ mButtonSwitchCamera = findViewById(getId("btn_switch_camera", "id"));
+// R.id.btn_audio_route
+ mButtonAudioRoute = findViewById(getId("btn_audio_route", "id"));
+
+ if (!TextUtils.isEmpty(mRoomId)) {
+ mTextTitle.setText(getString(getId("videocall_roomid", "string")) + mRoomId);
+ }
+ mImageBack.setOnClickListener(this);
+ mButtonMuteVideo.setOnClickListener(this);
+ mButtonMuteAudio.setOnClickListener(this);
+ mButtonSwitchCamera.setOnClickListener(this);
+ mButtonAudioRoute.setOnClickListener(this);
+
+ mRemoteUidList = new ArrayList<>();
+ mRemoteViewList = new ArrayList<>();
+// R.id.trtc_view_1
+ mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_1", "id")));
+ mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_2", "id")));
+ mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_3", "id")));
+ mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_4", "id")));
+ mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_5", "id")));
+ mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_6", "id")));
+
+// R.layout.videocall_view_floating_default
+ mFloatingView = new FloatingView(getApplicationContext(), getId("videocall_view_floating_default", "layout"));
+// R.layout.videocall_popup_layout
+ mFloatingView.setPopupWindow(getId("videocall_popup_layout", "layout"));
+ mFloatingView.setOnPopupItemClickListener(this);
+ }
+
+ private void enterRoom() {
+ mTRTCCloud = TRTCCloud.sharedInstance(getApplicationContext());
+ mTRTCCloud.setListener(new TRTCCloudImplListener(VideoCallingActivity.this));
+ mTXDeviceManager = mTRTCCloud.getDeviceManager();
+
+ TRTCCloudDef.TRTCParams trtcParams = new TRTCCloudDef.TRTCParams();
+ trtcParams.sdkAppId = this.sdkAppId;
+ trtcParams.userId = mUserId;
+ trtcParams.roomId = Integer.parseInt(mRoomId);
+ trtcParams.userSig = this.userSig;
+
+ mTRTCCloud.startLocalPreview(mIsFrontCamera, mTXCVVLocalPreviewView);
+ mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
+ mTRTCCloud.enterRoom(trtcParams, TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ requestDrawOverLays();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mFloatingView != null && mFloatingView.isShown()) {
+ mFloatingView.dismiss();
+ }
+ exitRoom();
+ }
+
+ private void exitRoom() {
+ if (mTRTCCloud != null) {
+ mTRTCCloud.stopLocalAudio();
+ mTRTCCloud.stopLocalPreview();
+ mTRTCCloud.exitRoom();
+ mTRTCCloud.setListener(null);
+ }
+ mTRTCCloud = null;
+ TRTCCloud.destroySharedInstance();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mFloatingView != null && mFloatingView.isShown()) {
+ mFloatingView.dismiss();
+ }
+ }
+
+ @Override
+ protected void onPermissionGranted() {
+ initView();
+ enterRoom();
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == getId("iv_back", "id")) {
+ finish();
+ } else if (id == getId("btn_mute_video", "id")) {
+ muteVideo();
+ } else if (id == getId("btn_mute_audio", "id")) {
+ muteAudio();
+ } else if (id == getId("btn_switch_camera", "id")) {
+ switchCamera();
+ } else if (id == getId("btn_audio_route", "id")) {
+ audioRoute();
+ } else if (id == getId("iv_return", "id")) {
+ floatViewClick();
+ }
+ }
+
+ private void floatViewClick() {
+ Intent intent = new Intent(this, VideoCallingActivity.class);
+ if (intent != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
+ try {
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void muteVideo() {
+ boolean isSelected = mButtonMuteVideo.isSelected();
+ if (!isSelected) {
+ mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_open", "mipmap")));
+ closeRemoteVideoViews();
+ } else {
+ mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_close", "mipmap")));
+ refreshRemoteVideoViews();
+ }
+ mButtonMuteVideo.setSelected(!isSelected);
+ }
+
+
+ private void closeRemoteVideoViews() {
+ for (int i = 0; i < mRemoteViewList.size(); i++) {
+ if (i < mRemoteUidList.size()) {
+ mRemoteViewList.get(i).setVisibility(View.INVISIBLE);
+ } else {
+ mRemoteViewList.get(i).setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private void refreshRemoteVideoViews() {
+ for (int i = 0; i < mRemoteViewList.size(); i++) {
+ if (i < mRemoteUidList.size()) {
+ String remoteUid = mRemoteUidList.get(i);
+ mRemoteViewList.get(i).setVisibility(View.VISIBLE);
+ } else {
+ mRemoteViewList.get(i).setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private void muteAudio() {
+ boolean isSelected = mButtonMuteAudio.isSelected();
+ if (!isSelected) {
+ mTRTCCloud.muteLocalAudio(true);
+ mButtonMuteAudio.setImageDrawable(getDrawable(getId("mac_on", "mipmap")));
+ } else {
+ mTRTCCloud.muteLocalAudio(false);
+ mButtonMuteAudio.setImageDrawable(getDrawable(getId("mac_off", "mipmap")));
+ }
+ mButtonMuteAudio.setSelected(!isSelected);
+ }
+
+ private void switchCamera() {
+ mIsFrontCamera = !mIsFrontCamera;
+ mTXDeviceManager.switchCamera(mIsFrontCamera);
+ }
+
+ private void audioRoute() {
+ if (mAudioRouteFlag) {
+ mAudioRouteFlag = false;
+ mTXDeviceManager.setAudioRoute(TXDeviceManager.TXAudioRoute.TXAudioRouteEarpiece);
+ mButtonAudioRoute.setImageDrawable(getDrawable(getId("earpiece", "mipmap")));
+ } else {
+ mAudioRouteFlag = true;
+ mTXDeviceManager.setAudioRoute(TXDeviceManager.TXAudioRoute.TXAudioRouteSpeakerphone);
+ mButtonAudioRoute.setImageDrawable(getDrawable(getId("speaker", "mipmap")));
+ }
+ }
+
+ private class TRTCCloudImplListener extends TRTCCloudListener {
+
+ private WeakReference mContext;
+
+ public TRTCCloudImplListener(VideoCallingActivity activity) {
+ super();
+ mContext = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void onUserVideoAvailable(String userId, boolean available) {
+ Log.d(TAG, "onUserVideoAvailable userId " + userId + ", mUserCount " + mUserCount + ",available " + available);
+ int index = mRemoteUidList.indexOf(userId);
+ if (available) {
+ if (index != -1) {
+ return;
+ }
+ mRemoteUidList.add(userId);
+ refreshRemoteVideoViews();
+ } else {
+ if (index == -1) {
+ return;
+ }
+ mTRTCCloud.stopRemoteView(userId);
+ mRemoteUidList.remove(index);
+ refreshRemoteVideoViews();
+ }
+
+ }
+
+ private void refreshRemoteVideoViews() {
+ for (int i = 0; i < mRemoteViewList.size(); i++) {
+ if (i < mRemoteUidList.size()) {
+ String remoteUid = mRemoteUidList.get(i);
+ mRemoteViewList.get(i).setVisibility(View.VISIBLE);
+ mTRTCCloud.startRemoteView(remoteUid, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SMALL, mRemoteViewList.get(i));
+ } else {
+ mRemoteViewList.get(i).setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ public void onError(int errCode, String errMsg, Bundle extraInfo) {
+ Log.d(TAG, "sdk callback onError");
+ VideoCallingActivity activity = mContext.get();
+ if (activity != null) {
+ Toast.makeText(activity, "onError: " + errMsg + "[" + errCode + "]", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ }
+
+ @Override
+ public void onExitRoom(int reason) {
+ super.onExitRoom(reason);
+ if (reason == 2) {
+ VideoCallingActivity activity = mContext.get();
+ Toast.makeText(activity, "远程协助已结束", Toast.LENGTH_LONG).show();
+ }
+ finish();
+ }
+
+ }
+
+ public void requestDrawOverLays() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N && !Settings.canDrawOverlays(VideoCallingActivity.this)) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + VideoCallingActivity.this.getPackageName()));
+ startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
+ } else {
+ showFloatingView();
+ }
+ }
+
+ private void showFloatingView() {
+ if (mFloatingView != null && !mFloatingView.isShown()) {
+ if ((null != mTRTCCloud)) {
+ mFloatingView.show();
+ mFloatingView.setOnPopupItemClickListener(this);
+ }
+ }
+ }
+}
diff --git a/src/android/java/com/tencent/trtc/videocall/VideoCallingEnterActivity.java b/src/android/java/com/tencent/trtc/videocall/VideoCallingEnterActivity.java
new file mode 100644
index 0000000..709329f
--- /dev/null
+++ b/src/android/java/com/tencent/trtc/videocall/VideoCallingEnterActivity.java
@@ -0,0 +1,93 @@
+package com.tencent.trtc.videocall;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.tencent.trtc.debug.Constant;
+
+/**
+ * TRTC视频通话的入口页面(可以设置房间id和用户id)
+ *
+ * - 可跳转TRTC视频通话页面{@link VideoCallingActivity}
+ */
+
+/**
+ * Video Call Entrance View (set room ID and user ID)
+ *
+ * - Direct to the video call view: {@link VideoCallingActivity}
+ */
+public class VideoCallingEnterActivity extends AppCompatActivity {
+
+ private EditText mEditInputUserId;
+ private EditText mEditInputRoomId;
+
+ private int getId(String idName, String type) {
+ return getResources().getIdentifier(idName, type, getPackageName());
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+// setContentView(R.layout.videocall_activit_enter);
+ setContentView(getId("videocall_activit_enter", "layout"));
+ getSupportActionBar().hide();
+// mEditInputUserId = findViewById(R.id.et_input_username);
+// mEditInputRoomId = findViewById(R.id.et_input_room_id);
+ mEditInputUserId = findViewById(getId("et_input_username", "id"));
+ mEditInputRoomId = findViewById(getId("et_input_room_id", "id"));
+// R.id.btn_enter_room
+ findViewById(getId("btn_enter_room", "id")).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startEnterRoom();
+ }
+ });
+// R.id.rl_entrance_main
+ findViewById(getId("rl_entrance_main", "id")).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideInput();
+ }
+ });
+// R.id.iv_back
+ findViewById(getId("iv_back", "id")).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+ mEditInputRoomId.setText("1256732");
+ String time = String.valueOf(System.currentTimeMillis());
+ String userId = time.substring(time.length() - 8);
+ mEditInputUserId.setText(userId);
+ }
+
+ private void startEnterRoom() {
+ if (TextUtils.isEmpty(mEditInputUserId.getText().toString().trim())
+ || TextUtils.isEmpty(mEditInputRoomId.getText().toString().trim())) {
+// R.string.videocall_room_input_error_tip
+ Toast.makeText(VideoCallingEnterActivity.this, getString(getId("videocall_room_input_error_tip", "string")), Toast.LENGTH_LONG).show();
+ return;
+ }
+ Intent intent = new Intent(VideoCallingEnterActivity.this, VideoCallingActivity.class);
+ intent.putExtra(Constant.ROOM_ID, mEditInputRoomId.getText().toString().trim());
+ intent.putExtra(Constant.USER_ID, mEditInputUserId.getText().toString().trim());
+ startActivity(intent);
+ }
+
+ protected void hideInput() {
+ InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+ View v = getWindow().peekDecorView();
+ if (null != v) {
+ imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+ }
+
+}
diff --git a/src/android/libs/trtc.gradle b/src/android/libs/trtc.gradle
new file mode 100644
index 0000000..39a6773
--- /dev/null
+++ b/src/android/libs/trtc.gradle
@@ -0,0 +1,10 @@
+dependencies {
+ compile fileTree(dir: "libs", include: ["*.jar"])
+ compile 'androidx.appcompat:appcompat:1.1.0'
+ compile 'androidx.constraintlayout:constraintlayout:1.1.3'
+ compile 'com.android.support:multidex:1.0.1'
+ compile 'com.tencent.ilivesdk:ilivesdk:1.9.3'
+ compile 'com.tencent.ilivefilter:liteav_normal:1.1.21'
+ compile 'com.google.android.material:material:1.3.0'
+ compile 'com.tencent.liteav:LiteAVSDK_TRTC:latest.release'
+}
diff --git a/src/android/liteavsdk.jar b/src/android/liteavsdk.jar
new file mode 100644
index 0000000..ad86eb4
Binary files /dev/null and b/src/android/liteavsdk.jar differ
diff --git a/src/android/res/drawable/common_button_bg.xml b/src/android/res/drawable/common_button_bg.xml
new file mode 100644
index 0000000..afd55ca
--- /dev/null
+++ b/src/android/res/drawable/common_button_bg.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/src/android/res/drawable/common_edit_bg.xml b/src/android/res/drawable/common_edit_bg.xml
new file mode 100644
index 0000000..e72ac4d
--- /dev/null
+++ b/src/android/res/drawable/common_edit_bg.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/android/res/drawable/common_seekbar_style.xml b/src/android/res/drawable/common_seekbar_style.xml
new file mode 100644
index 0000000..2407a4d
--- /dev/null
+++ b/src/android/res/drawable/common_seekbar_style.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
diff --git a/src/android/res/drawable/common_seekbar_thumb.xml b/src/android/res/drawable/common_seekbar_thumb.xml
new file mode 100644
index 0000000..070b130
--- /dev/null
+++ b/src/android/res/drawable/common_seekbar_thumb.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/res/drawable/common_selector_radio_bg.xml b/src/android/res/drawable/common_selector_radio_bg.xml
new file mode 100644
index 0000000..6512454
--- /dev/null
+++ b/src/android/res/drawable/common_selector_radio_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/res/drawable/videocall_background.xml b/src/android/res/drawable/videocall_background.xml
new file mode 100644
index 0000000..cb7f0ee
--- /dev/null
+++ b/src/android/res/drawable/videocall_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/res/layout/videocall_activit_enter.xml b/src/android/res/layout/videocall_activit_enter.xml
new file mode 100644
index 0000000..63ad531
--- /dev/null
+++ b/src/android/res/layout/videocall_activit_enter.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/res/layout/videocall_activity_calling.xml b/src/android/res/layout/videocall_activity_calling.xml
new file mode 100644
index 0000000..d921b0b
--- /dev/null
+++ b/src/android/res/layout/videocall_activity_calling.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/res/layout/videocall_popup_layout.xml b/src/android/res/layout/videocall_popup_layout.xml
new file mode 100644
index 0000000..6a0cb83
--- /dev/null
+++ b/src/android/res/layout/videocall_popup_layout.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/res/layout/videocall_view_floating_default.xml b/src/android/res/layout/videocall_view_floating_default.xml
new file mode 100644
index 0000000..d97171d
--- /dev/null
+++ b/src/android/res/layout/videocall_view_floating_default.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/res/mipmap-xxhdpi/common_ic_back.png b/src/android/res/mipmap-xxhdpi/common_ic_back.png
new file mode 100644
index 0000000..0297f82
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/common_ic_back.png differ
diff --git a/src/android/res/mipmap-xxhdpi/common_user_portrait.png b/src/android/res/mipmap-xxhdpi/common_user_portrait.png
new file mode 100644
index 0000000..bd5472c
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/common_user_portrait.png differ
diff --git a/src/android/res/mipmap-xxhdpi/earpiece.png b/src/android/res/mipmap-xxhdpi/earpiece.png
new file mode 100644
index 0000000..417c4e1
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/earpiece.png differ
diff --git a/src/android/res/mipmap-xxhdpi/mac_off.png b/src/android/res/mipmap-xxhdpi/mac_off.png
new file mode 100644
index 0000000..fd77c4f
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/mac_off.png differ
diff --git a/src/android/res/mipmap-xxhdpi/mac_on.png b/src/android/res/mipmap-xxhdpi/mac_on.png
new file mode 100644
index 0000000..b2d03ed
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/mac_on.png differ
diff --git a/src/android/res/mipmap-xxhdpi/speaker.png b/src/android/res/mipmap-xxhdpi/speaker.png
new file mode 100644
index 0000000..3112b60
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/speaker.png differ
diff --git a/src/android/res/mipmap-xxhdpi/swap_camera.png b/src/android/res/mipmap-xxhdpi/swap_camera.png
new file mode 100644
index 0000000..a6c2630
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/swap_camera.png differ
diff --git a/src/android/res/mipmap-xxhdpi/videocall_float_logo.png b/src/android/res/mipmap-xxhdpi/videocall_float_logo.png
new file mode 100644
index 0000000..8c71ffd
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/videocall_float_logo.png differ
diff --git a/src/android/res/mipmap-xxhdpi/videocall_home.png b/src/android/res/mipmap-xxhdpi/videocall_home.png
new file mode 100644
index 0000000..94085a8
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/videocall_home.png differ
diff --git a/src/android/res/mipmap-xxhdpi/view_close.png b/src/android/res/mipmap-xxhdpi/view_close.png
new file mode 100644
index 0000000..904914c
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/view_close.png differ
diff --git a/src/android/res/mipmap-xxhdpi/view_open.png b/src/android/res/mipmap-xxhdpi/view_open.png
new file mode 100644
index 0000000..a280075
Binary files /dev/null and b/src/android/res/mipmap-xxhdpi/view_open.png differ
diff --git a/src/android/res/values-en/strings.xml b/src/android/res/values-en/strings.xml
new file mode 100644
index 0000000..e0f744a
--- /dev/null
+++ b/src/android/res/values-en/strings.xml
@@ -0,0 +1,55 @@
+
+
+ TRTC-API-Example
+ test
+ testtest
+ TRTC API Example
+
+ Failed to join call as the user did not grant the required permission.
+
+ Basic Features
+ Audio Call
+ One-to-one/Group audio call, which supports muting, hands-free mode, etc.
+ Video Call
+ One-to-one/Group video call, which supports muting, hands-free mode, etc.
+ Interactive Live Video Streaming
+ Interactive Live Audio Streaming
+ Screen Recording Live Streaming
+ Share the screen during live streaming, designed for online education, game streaming, etc.
+
+ Advanced Features
+ String-type Room ID
+ Video Quality Setting
+ Audio Quality Setting
+ Render Control
+ Network Speed Testing
+ Publish via CDN
+ Custom Video Capturing & Rendering
+ Audio Effect Setting
+ Background Music Setting
+ Local Video Sharing
+ Local Recording
+ Enter Multiple Rooms
+ Receive/Send SEI Message
+ Switch Room
+ Cross-room Competition
+ Third-party Beauty Filter
+
+
+ Video Call
+ Enter a room ID and username
+ Video Settings
+ Audio Settings
+ Use Rear Camera
+ Use Front Camera
+ Camera On
+ Camera Off
+ Mic On
+ Mic Off
+ Use Speaker
+ Use Receiver
+ Enter Room
+ User ID (Required)
+ Room ID (Required)
+ Room ID:
+
\ No newline at end of file
diff --git a/src/android/res/values-en/styles.xml b/src/android/res/values-en/styles.xml
new file mode 100644
index 0000000..1195356
--- /dev/null
+++ b/src/android/res/values-en/styles.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/src/android/res/values/colors.xml b/src/android/res/values/colors.xml
new file mode 100644
index 0000000..e4af778
--- /dev/null
+++ b/src/android/res/values/colors.xml
@@ -0,0 +1,14 @@
+
+
+ #1B1B1B
+ #00A66B
+ #B7B7B7
+ #34c759
+ #EFEFEF
+ #FFFFFF
+ #34c759
+ #999999
+ #008577
+ #00574B
+ #D81B60
+
diff --git a/src/android/res/values/strings.xml b/src/android/res/values/strings.xml
new file mode 100644
index 0000000..ccad510
--- /dev/null
+++ b/src/android/res/values/strings.xml
@@ -0,0 +1,55 @@
+
+ TRTC-API-Example
+ 测试
+ 测试测试
+ 腾讯云TRTC API Example
+
+ 用户没有允许需要的权限,加入通话失败
+
+
+ 基础功能
+ 语音通话
+ 双人/多人语音通话、包含静音/免提等功能
+ 视频通话
+ 双人/多人视频通话、包含静音/免提等功能
+ 视频互动直播
+ 语音互动直播
+ 录屏直播
+ 直播过程中分享屏幕,适用于在线教育,游戏直播等场景
+
+ 进阶功能
+ 字符串房间号
+ 画质设定
+ 音质设定
+ 渲染控制
+ 网络测速
+ CDN发布
+ 自定义视频采集&渲染
+ 设置音效
+ 设置背景音乐
+ 本地视频分享
+ 本地媒体录制
+ 加入多个房间
+ 收发SEI消息
+ 快速切换房间
+ 跨房PK
+ 第三方美颜
+
+
+ TRTC 视频通话示例
+ "房间号和用户名不能为空"
+ 视频选项
+ 音频选项
+ 使用后置摄像头
+ 使用前置摄像头
+ 打开摄像头
+ 关闭摄像头
+ 打开麦克风
+ 关闭麦克风
+ 使用扬声器
+ 使用听筒
+ 进入房间
+ 请输入用户名(必填项)
+ 请输入房间号(必填项)
+ 房间号:
+
\ No newline at end of file
diff --git a/src/android/res/values/styles.xml b/src/android/res/values/styles.xml
new file mode 100644
index 0000000..1195356
--- /dev/null
+++ b/src/android/res/values/styles.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/src/ios/Trtc/Info.plist b/src/ios/Trtc/Info.plist
new file mode 100644
index 0000000..49a2d21
--- /dev/null
+++ b/src/ios/Trtc/Info.plist
@@ -0,0 +1,73 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ trtcdayisheng
+ CFBundleURLSchemes
+
+ trtcdayisheng
+
+
+
+ CFBundleVersion
+ 1
+ LSApplicationCategoryType
+
+ LSRequiresIPhoneOS
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+ NSCameraUsageDescription
+ 请点击“好”以允许访问。
+
+ NSMicrophoneUsageDescription
+ 请点击“好”以允许访问。
+
+ UIBackgroundModes
+
+ audio
+
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/src/ios/Trtc/TCLiveConfigDefine.h b/src/ios/Trtc/TCLiveConfigDefine.h
new file mode 100644
index 0000000..0b5a783
--- /dev/null
+++ b/src/ios/Trtc/TCLiveConfigDefine.h
@@ -0,0 +1,15 @@
+//
+// TCLiveConfigDefine.h
+// TRTC
+//
+// Created by xiaowei li on 2018/6/22.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#ifndef TCLiveConfigDefine_h
+#define TCLiveConfigDefine_h
+
+#define Login_Info_Url @"https://xzb.qcloud.com/webrtc/weapp/webrtc_room/get_login_info" //业务后台登录信息拉取,可替换为自己的业务后台
+#define AuthBuffer_Info_Url @"https://xzb.qcloud.com/webrtc/weapp/webrtc_room/get_privatemapkey"
+#define Default_Role @"ed640" //用户角色配置画面参数,可在控制台进行配置 https://cloud.tencent.com/document/product/647/17308
+#endif /* TCLiveConfigDefine_h */
diff --git a/src/ios/Trtc/TCLiveJoinRoomViewController.h b/src/ios/Trtc/TCLiveJoinRoomViewController.h
new file mode 100644
index 0000000..b5a1a98
--- /dev/null
+++ b/src/ios/Trtc/TCLiveJoinRoomViewController.h
@@ -0,0 +1,17 @@
+//
+// ViewController.h
+// TRTC
+//
+// Created by Tencent on 2018/5/31.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+
+@interface TCLiveJoinRoomViewController : UIViewController
+
+@property (nonatomic) NSString *defaultRoomId;
+
+@end
+
+
diff --git a/src/ios/Trtc/TCLiveJoinRoomViewController.m b/src/ios/Trtc/TCLiveJoinRoomViewController.m
new file mode 100644
index 0000000..d5ec8ee
--- /dev/null
+++ b/src/ios/Trtc/TCLiveJoinRoomViewController.m
@@ -0,0 +1,112 @@
+//
+// ViewController.m
+// TRTC
+//
+// Created by Tencent on 2018/5/31.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import "TCLiveJoinRoomViewController.h"
+#import "UIColorEX.h"
+#import "TCLiveRoomViewController.h"
+#import "UIToastView.h"
+#import "TCLiveConfigDefine.h"
+
+@interface TCLiveJoinRoomViewController ()
+@property(nonatomic,strong)UITextField *inputTextField;
+@property(nonatomic,strong)UIButton *joinRoomBtn;
+@property(nonatomic,strong) UIView *botoomLine;
+@property(nonatomic,strong) UIImageView *bgImageView;
+@end
+
+@implementation TCLiveJoinRoomViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view.
+ self.title = @"视频通话";
+
+ [self.navigationController.navigationBar setTranslucent:NO];
+
+ [self.view addSubview:self.bgImageView];
+
+ [self.view addSubview:self.inputTextField];
+
+ [self.view addSubview:self.joinRoomBtn];
+
+}
+-(void)viewWillAppear:(BOOL)animated{
+ if (self.defaultRoomId) {
+ self.inputTextField.text = self.defaultRoomId;
+ }
+ [super viewWillAppear:animated];
+ [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor]}];
+}
+-(UIImageView *)bgImageView{
+ if (!_bgImageView) {
+ _bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
+ [_bgImageView setImage:[UIImage imageNamed:@"bg.png"]];
+ _bgImageView.userInteractionEnabled = YES;
+ }
+ return _bgImageView;
+}
+
+- (UIButton *)joinRoomBtn{
+ if (!_joinRoomBtn) {
+ _joinRoomBtn = [[UIButton alloc] initWithFrame:CGRectMake(20, _inputTextField.frame.size.height + _inputTextField.frame.origin.y + 50, self.view.frame.size.width - 40, 50)];
+ _joinRoomBtn.layer.cornerRadius = 25;
+ [_joinRoomBtn setTitle:@"加入房间" forState:UIControlStateNormal];
+ _joinRoomBtn.backgroundColor = [UIColor colorWithRGBHex:0x1472fc];
+ [_joinRoomBtn addTarget:self action:@selector(joinRoomBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ [_joinRoomBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
+ }
+ return _joinRoomBtn;
+}
+- (UITextField *)inputTextField{
+ if (!_inputTextField) {
+ _inputTextField = [[UITextField alloc] initWithFrame:CGRectMake(5, 20 , self.view.frame.size.width-10, 40)];
+ _inputTextField.delegate = self;
+ _inputTextField.backgroundColor= [UIColor clearColor];
+ NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"请输入房间号码" attributes:@{NSForegroundColorAttributeName:[UIColor grayColor]}];
+ _inputTextField.attributedPlaceholder = str;
+ _inputTextField.textColor = [UIColor grayColor];
+ _inputTextField.returnKeyType = UIReturnKeyDone;
+ _inputTextField.keyboardType = UIKeyboardTypeNumberPad;
+
+ _botoomLine = [[UIView alloc] initWithFrame:CGRectMake(0, 40-1, _inputTextField.frame.size.width, 1)];
+ _botoomLine.backgroundColor = [UIColor colorWithRGBHex:0x1472fc];
+ [_inputTextField addSubview:_botoomLine];
+ }
+ return _inputTextField;
+}
+
+- (void)joinRoomBtnClick:(UIButton *)sender{
+ if (self.inputTextField.text.length > 0) {
+ TCLiveRoomViewController *vc = [[TCLiveRoomViewController alloc] initWithRoomID:self.inputTextField.text role:Default_Role];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else{
+ [[UIToastView getInstance] showToastWithMessage:@"请输入房间号" toastMode:UIToastShowMode_fail];
+ }
+}
+
+// 自动跳转
+//- (void)autoNav:(NSString *)roomId {
+// TCLiveRoomViewController *vc = [[TCLiveRoomViewController alloc] initWithRoomID:roomId role:Default_Role];
+// [self.navigationController pushViewController:vc animated:YES];
+//}
+
+
+#pragma mark - UITextFieldDelegate
+- (BOOL)textFieldShouldReturn:(UITextField *)textField{
+ [textField resignFirstResponder];
+ return YES;
+}
+
+- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
+{
+ NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
+ NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
+ return [string isEqualToString:filtered];
+}
+@end
diff --git a/src/ios/Trtc/TCLiveRequestManager.h b/src/ios/Trtc/TCLiveRequestManager.h
new file mode 100644
index 0000000..47ebda8
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRequestManager.h
@@ -0,0 +1,21 @@
+//
+// TCLiveRequestManager.h
+// TRTC
+//
+// Created by Tencent on 2018/5/31.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+
+typedef void (^LiveLoginInfoBlock)(int code);
+typedef void (^LiveAuthBufferBlock)(NSDictionary *info);
+@interface TCLiveRequestManager : NSObject
+@property(nonatomic,assign)int sdkAppID; //app标识,可在实时音视频控制台(https://console.cloud.tencent.com/rav)创建自己的应用生成
+@property(nonatomic,assign)int accountType; //登录实时音视频应用的帐号类型,在控制台创建应用后分配
+@property(nonatomic,strong)NSString *userID; //用户id标识(可由业务后台自己管理)
+@property(nonatomic,strong)NSString *userSig; //用于用户鉴权,生成方法https://cloud.tencent.com/document/product/647/17275 (可由业务后台自己管理)
++ (TCLiveRequestManager *)getInstance;
+- (void)requestLoginInfo:(LiveLoginInfoBlock)block;
+- (void)reqGetAuthBufferInfoWithParams:(NSDictionary *)params block:(LiveAuthBufferBlock)block;
+@end
diff --git a/src/ios/Trtc/TCLiveRequestManager.m b/src/ios/Trtc/TCLiveRequestManager.m
new file mode 100644
index 0000000..51b7489
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRequestManager.m
@@ -0,0 +1,98 @@
+//
+// TCLiveRequestManager.m
+// TRTC
+//
+// Created by Tencent on 2018/5/31.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import "TCLiveRequestManager.h"
+#import "UIToastView.h"
+#import "TCLiveConfigDefine.h"
+@implementation TCLiveRequestManager
++ (TCLiveRequestManager *)getInstance{
+ static TCLiveRequestManager *singleTon = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ singleTon = [TCLiveRequestManager new];
+ });
+ return singleTon;
+}
+
+- (void)requestLoginInfo:(LiveLoginInfoBlock)block{
+ NSString *user = [[NSUserDefaults standardUserDefaults] objectForKey:@"TCLIVE_USER"];
+ if (user.length == 0) {
+ user = @"";
+ }
+ NSDictionary *params = @{@"userID":user};
+ NSMutableURLRequest *request = [self getSendPostRequest:Login_Info_Url body:params];//加备注
+
+ NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
+ [sessionConfig setTimeoutIntervalForRequest:30];
+
+ __weak TCLiveRequestManager *weakself = self;
+ NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
+ NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
+ if (error|| data == nil) {
+ block(-1);
+ [[UIToastView getInstance] showToastWithMessage:@"登录请求失败" toastMode:UIToastShowMode_fail];
+ }
+ else{
+ //无error data解不出
+ NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ if (info) {
+ weakself.sdkAppID = [info[@"sdkAppID"] intValue];
+ weakself.accountType = [info[@"accountType"]intValue];
+ weakself.userID = info[@"userID"];
+ weakself.userSig = info[@"userSig"];
+ [[NSUserDefaults standardUserDefaults] setObject:info[@"userID"] forKey:@"TCLIVE_USER"];
+ block(0);
+ }
+ else{
+ block(-1);
+ [[UIToastView getInstance] showToastWithMessage:@"登录信息解包失败" toastMode:UIToastShowMode_fail];
+ }
+ }
+ }];
+ [task resume];
+}
+
+-(void)reqGetAuthBufferInfoWithParams:(NSDictionary *)params block:(LiveAuthBufferBlock)block{
+ NSMutableURLRequest *request = [self getSendPostRequest:AuthBuffer_Info_Url body:params];
+
+ NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
+ [sessionConfig setTimeoutIntervalForRequest:30];
+
+ NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
+ NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
+ if (error|| data == nil) {
+ [[UIToastView getInstance] showToastWithMessage:@"获取authBuffer请求失败" toastMode:UIToastShowMode_fail];
+ }
+ else{
+ //无error data解不出
+ NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ if (info) {
+ block(info);
+ }
+ else{
+ [[UIToastView getInstance] showToastWithMessage:@"获取authBuffer解包失败" toastMode:UIToastShowMode_fail];
+ }
+ }
+ }];
+ [task resume];
+}
+
+- (NSMutableURLRequest *)getSendPostRequest:(NSString *)url body:(NSDictionary *)body{
+
+ NSData *dataBody = [NSJSONSerialization dataWithJSONObject:body options:NSJSONWritingPrettyPrinted error:nil];
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
+ [request setValue:[NSString stringWithFormat:@"%ld", (long) [dataBody length]] forHTTPHeaderField:@"Content-Length"];
+ [request setHTTPMethod:@"POST"];
+ [request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
+ [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
+ [request setHTTPBody:dataBody];
+
+ return request;
+}
+@end
+
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.h
new file mode 100644
index 0000000..383bdb1
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.h
@@ -0,0 +1,17 @@
+//
+// TCLiveChatTableView.h
+// TRTC
+//
+// Created by Tencent on 2018/6/3.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+#import
+
+@interface TCLiveChatTableView : UITableView
+//添加信息到聊天列表
+- (void)addChatMessage:(NSArray *)msgList withContentColor:(UIColor *)contentColor nickColor:(UIColor *)nickColor;
+//发送信息
+- (void)sendMessage:(NSString *)message;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.m
new file mode 100644
index 0000000..eb15ce5
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.m
@@ -0,0 +1,177 @@
+//
+// TCLiveChatTableView.m
+// TRTC
+//
+// Created by Tencent on 2018/6/3.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+#import
+#import
+#import "TCLiveChatTableView.h"
+#import "TCLiveChatTableViewCell.h"
+#import "UIColorEX.h"
+#import "UIToastView.h"
+#import "TCLiveRequestManager.h"
+
+@interface TCLiveChatTableView ()
+@property(nonatomic,strong)NSMutableArray *chatMessageList;
+@property(nonatomic,strong)UIColor *contentColor;
+@property(nonatomic,strong)UIColor *nickColor;
+@end
+
+@implementation TCLiveChatTableView
+
+- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{
+ if (self = [super initWithFrame:frame style:style]) {
+ _chatMessageList = [[NSMutableArray alloc] initWithCapacity:1];
+ self.delegate = self;
+ self.dataSource = self;
+ self.separatorStyle = UITableViewCellSeparatorStyleNone;
+ self.transform = CGAffineTransformMakeScale(1, -1);
+ self.layer.cornerRadius = 4;
+ self.layer.masksToBounds = YES;
+ self.scrollEnabled = NO;
+ //设置消息监听
+ [[[ILiveSDK getInstance] getTIMManager] setMessageListener:self];
+ }
+ return self;
+}
+
+//接收消息
+-(void)onNewMessage:(NSArray *)msgs{
+ [self addChatMessage:msgs withContentColor:nil nickColor:nil];
+}
+//消息处理
+- (void)addChatMessage:(NSArray *)msgList withContentColor:(UIColor *)contentColor nickColor:(UIColor *)nickColor{
+ self.contentColor = contentColor;
+ self.nickColor = nickColor;
+ for (id item in msgList) {
+ [self.chatMessageList insertObject:item atIndex:0];
+ }
+ //过滤非文本消息
+ NSMutableArray *tempArr = [NSMutableArray array];
+ for (int i = 0; i < msgList.count;i++) {
+ TIMMessage *msg = msgList[i];
+ if (![self isTextMsg:msg]) {
+ [tempArr addObject:msg];
+ }
+ }
+ [self.chatMessageList removeObjectsInArray:tempArr];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self reloadData];
+ });
+}
+- (BOOL)isTextMsg:(TIMMessage *)msg {
+ TIMOfflinePushInfo *info = msg.getOfflinePushInfo;
+ if ([info.ext hasPrefix:@"TEXT"]) {
+ return YES;
+ }
+ int count = [msg elemCount];
+ for(int i = 0; i < count; i++) {
+ TIMElem *elem = [msg getElem:i];
+ if ([elem isKindOfClass:[TIMCustomElem class]]){
+ if ([((TIMCustomElem*)elem).ext hasPrefix:@"TEXT"]) {
+ return YES;
+ }
+ }
+ else if ([elem isKindOfClass:[TIMTextElem class]]){
+ return YES;
+ }
+ }
+ return NO;
+}
+//发送消息
+- (void)sendMessage:(NSString *)message{
+ //消息组装
+ TIMMessage *msge = [[TIMMessage alloc] init];
+ TIMCustomElem *textElem = [[TIMCustomElem alloc] init];
+ textElem.data = [message dataUsingEncoding:NSUTF8StringEncoding];
+ NSDictionary *descDic = @{@"nickName":[TCLiveRequestManager getInstance].userID};
+ NSString *desc = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:descDic options:NSJSONReadingAllowFragments error:nil] encoding:NSUTF8StringEncoding];
+ textElem.desc = desc;
+ textElem.ext = @"TEXT";
+ [msge addElem:textElem];
+ //调用发送接口
+ [[ILiveRoomManager getInstance] sendGroupMessage:msge succ:^{
+ NSLog(@"send message succ");
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"send message fail");
+ [[UIToastView getInstance] showToastWithMessage:@"发送消息失败" toastMode:UIToastShowMode_fail];
+ }];
+ [self addChatMessage:@[msge] withContentColor:nil nickColor:nil];
+}
+#pragma mark - UITableViewDelegate
+
+-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
+ TCLiveChatTableViewCell *cell = [self createChatTableViewCell:tableView withIndexPath:indexPath];
+ return cell.cellHeight;
+}
+#pragma mark - UITableViewDataSource
+
+-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
+ return self.chatMessageList.count;
+}
+
+-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
+ TCLiveChatTableViewCell *cell = [self createChatTableViewCell:tableView withIndexPath:indexPath];
+ return cell;
+}
+//创建消息cell
+- (TCLiveChatTableViewCell *)createChatTableViewCell:(UITableView *)tableView withIndexPath:(NSIndexPath *)indexPath{
+ TCLiveChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ChatTableviewCell"];
+ if (!cell) {
+ cell = [[TCLiveChatTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ChatTableviewCell"];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ cell.backgroundColor = [UIColor clearColor];
+ cell.contentView.transform = CGAffineTransformMakeScale (1,-1);
+ }
+ TIMMessage *msg = self.chatMessageList[indexPath.row];
+ int count = [msg elemCount];
+ for(int i = 0; i < count; i++) {
+ TIMElem *elem = [msg getElem:i];
+ //收到消息展示
+ NSMutableAttributedString *msgInfo = [[NSMutableAttributedString alloc] initWithString:@""];
+ if([elem isKindOfClass:[TIMTextElem class]]){
+ msgInfo = [self getContentWithNick:msg.sender andContentTex:((TIMTextElem *)elem).text];
+ [cell setModel:msgInfo];
+ break;
+ }
+ else if ([elem isKindOfClass:[TIMCustomElem class]]){
+ NSString *nick = msg.sender;
+ NSString *dataStr = [[NSString alloc] initWithData:((TIMCustomElem *)elem).data encoding:NSUTF8StringEncoding];
+ NSDictionary *descDic = [NSJSONSerialization JSONObjectWithData:[((TIMCustomElem *)elem).desc dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
+ NSString *nickNmae = descDic[@"nickName"];
+ if (nickNmae.length > 0) {
+ nick = nickNmae;
+ }
+ msgInfo = [self getContentWithNick:nick andContentTex:dataStr];
+ [cell setModel:msgInfo];
+ break;
+ }
+ }
+ return cell;
+}
+- (NSMutableAttributedString *)getContentWithNick:(NSString *)nick andContentTex:(NSString *)contentText{
+ NSString *content = [NSString stringWithFormat:@"%@:%@",nick, contentText];
+ NSMutableAttributedString *msgInfo = [[NSMutableAttributedString alloc] initWithString:content];
+ UIColor *contentColor = [UIColor whiteColor];
+ UIColor *nickColor = [UIColor colorWithRGBHex:0xFF4081];
+ if (self.contentColor) {
+ contentColor = self.contentColor;
+ }
+ if(self.nickColor){
+ nickColor = self.nickColor;
+ }
+ [msgInfo addAttribute:NSForegroundColorAttributeName value:contentColor range:[content rangeOfString:contentText]];
+ [msgInfo addAttribute:NSForegroundColorAttributeName value:nickColor range:[content rangeOfString:nick]];
+
+ return msgInfo;
+}
+//点击事件透传
+-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
+ return nil;
+}
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.h
new file mode 100644
index 0000000..8140059
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.h
@@ -0,0 +1,14 @@
+//
+// TCLiveChatTableViewCell.h
+// TRTC
+//
+// Created by Tencent on 2018/6/8.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+
+@interface TCLiveChatTableViewCell : UITableViewCell
+@property(nonatomic,assign)CGFloat cellHeight;
+-(void)setModel:(NSMutableAttributedString *)model;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.m
new file mode 100644
index 0000000..79bebde
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.m
@@ -0,0 +1,59 @@
+//
+// TCLiveChatTableViewCell.m
+// TRTC
+//
+// Created by Tencent on 2018/6/8.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+
+#import "TCLiveChatTableViewCell.h"
+
+@interface TCLiveChatTableViewCell ()
+@property(nonatomic,strong) UILabel *contentLabel;
+@property(nonatomic,strong) UIView *backMaskView;
+@end
+
+@implementation TCLiveChatTableViewCell
+
+- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
+ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
+
+ [self.contentView addSubview:self.backMaskView];
+ [_backMaskView addSubview:self.contentLabel];
+ }
+ return self;
+}
+
+-(UILabel *)contentLabel{
+ if (!_contentLabel) {
+ _contentLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ _contentLabel.backgroundColor = [UIColor clearColor];
+ _contentLabel.textAlignment = NSTextAlignmentLeft;
+ _contentLabel.font = [UIFont systemFontOfSize:13];
+ _contentLabel.textColor = [UIColor whiteColor];
+ _contentLabel.numberOfLines = 0;
+ }
+ return _contentLabel;
+}
+
+-(UIView *)backMaskView{
+ if (!_backMaskView) {
+ _backMaskView = [[UIView alloc] initWithFrame:CGRectZero];
+ _backMaskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
+ _backMaskView.layer.cornerRadius = 10;
+ _backMaskView.layer.masksToBounds = YES;
+ }
+ return _backMaskView;
+}
+
+-(void)setModel:(NSMutableAttributedString *)model{
+ _contentLabel.attributedText = model;
+ CGSize size = [_contentLabel sizeThatFits:CGSizeMake(230, 10000)];
+
+ _contentLabel.frame = CGRectMake(5, 5, size.width, size.height );
+ _backMaskView.frame = CGRectMake(5, 5, size.width + 10, size.height + 10);
+ self.cellHeight = size.height+20;
+}
+
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.h
new file mode 100644
index 0000000..4e33f53
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.h
@@ -0,0 +1,15 @@
+//
+// TCLiveRoomViewController.h
+// TRTC
+//
+// Created by Tencent on 2018/5/31.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+#import "TCLiveVideoLayoutView.h"
+
+@interface TCLiveRoomViewController : UIViewController
+//传入roomid(房间号)进入指定房间 设置role配置画面参数
+-(instancetype)initWithRoomID:(NSString *)roomid role:(NSString *)role;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.m
new file mode 100644
index 0000000..ccd07de
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.m
@@ -0,0 +1,275 @@
+//
+// TCLiveRoomViewController.m
+// TRTC
+//
+// Created by Tencent on 2018/5/31.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import "TCLiveRoomViewController.h"
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import "TCLiveRequestManager.h"
+#import "TCLiveVideoControlBar.h"
+#import "TCLiveChatTableView.h"
+#import "UIToastView.h"
+
+#define LIVE_VIEW_HEIGHT 370
+#define LIVE_CONTROL_BAR_HEIGHT 70
+#define LIVE_INPUTTEXTFIELD_HEIGHT 40
+
+@interface TCLiveRoomViewController ()
+@property(nonatomic,strong) TCLiveVideoLayoutView *videoLayoutView;
+@property(nonatomic,strong) TCLiveVideoControlBar *controlBar;
+@property(nonatomic,strong) TCLiveChatTableView *chatTableView;
+@property(nonatomic,strong) UITextField *inputTextField;
+@property(nonatomic,strong) NSString *roomID;
+@property(nonatomic,strong) UIImageView *bgImageView;
+@property(nonatomic,strong) NSTimer *logTimer;
+@property(nonatomic,strong) NSTimer *heartBeatTimer;
+@property(nonatomic,assign) CGRect origInputTextFieldFrame;
+@property(nonatomic,assign) CGRect origChatTableViewFrame;
+@property(nonatomic,strong) NSString *role;
+@end
+
+@implementation TCLiveRoomViewController
+
+-(instancetype)initWithRoomID:(NSString *)roomid role:(NSString *)role{
+ if (self = [super init]) {
+ self.roomID = roomid;
+ self.role = role;
+ }
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view.
+ self.title = self.roomID;
+ [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
+
+ [self enterRoom];
+
+ [self customLeftButton];
+
+ [self.view addSubview:self.bgImageView];
+
+ [_bgImageView addSubview:self.videoLayoutView];
+
+ [_bgImageView addSubview:self.chatTableView];
+
+ [_bgImageView addSubview:self.controlBar];
+
+ [_bgImageView addSubview:self.inputTextField];
+
+ //监听键盘变化
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
+}
+-(void)viewWillAppear:(BOOL)animated{
+ [super viewWillAppear:animated];
+ //导航栏透明化
+ [self setNavigationBarTransparent];
+ [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
+}
+
+- (void)viewWillDisappear:(BOOL)animated{
+ [super viewWillDisappear:animated];
+ [self.navigationController.navigationBar setTranslucent:NO];
+ [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
+}
+- (void)setNavigationBarTransparent{
+ [self.navigationController.navigationBar setTranslucent:YES];
+ [self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc]init] forBarMetrics:UIBarMetricsDefault];
+ [self.navigationController.navigationBar setShadowImage:[[UIImage alloc]init]];
+ [self.navigationController.navigationBar setBackgroundColor:[UIColor clearColor]];
+}
+
+- (void)enterRoom{
+ [[TCLiveRequestManager getInstance] reqGetAuthBufferInfoWithParams:@{@"roomID":self.roomID,@"userID":[TCLiveRequestManager getInstance].userID} block:^(NSDictionary *info) {
+ ILiveRoomOption *option = [ILiveRoomOption defaultHostLiveOption];
+ option.imOption.imSupport = YES;
+ option.memberStatusListener = self.videoLayoutView;
+ option.roomDisconnectListener = self;
+ option.controlRole = self.role;
+ option.avOption.privateMapKey = [info[@"privateMapKey"] dataUsingEncoding:NSUTF8StringEncoding];
+
+ [[ILiveRoomManager getInstance] createRoom:[self.roomID intValue] option:option succ:^{
+ NSLog(@"-----> create room succ");
+ [[UIToastView getInstance] showToastWithMessage:@"创建房间成功" toastMode:UIToastShowMode_Succ];
+ [self.controlBar enableBeauty:YES];//进入房间默认开启美颜
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ if(errId == 10021){
+ //表示房间已存在直接加入房间
+ [[ILiveRoomManager getInstance] joinRoom:[self.roomID intValue] option:option succ:^{
+ NSLog(@"-----> join room succ");
+ [[UIToastView getInstance] showToastWithMessage:@"加入房间成功" toastMode:UIToastShowMode_Succ];
+ [self.controlBar enableBeauty:YES];//进入房间默认开启美颜
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"-----> join room fail,%@ %d %@",module, errId, errMsg);
+ [[UIToastView getInstance] showToastWithMessage:errMsg toastMode:UIToastShowMode_fail];
+ }];
+ }
+ else{
+ NSLog(@"-----> create room fail,%@ %d %@",module, errId, errMsg);
+ [[UIToastView getInstance] showToastWithMessage:errMsg toastMode:UIToastShowMode_fail];
+ }
+ }];
+ }];
+
+}
+
+- (void)customLeftButton{
+ UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
+ backBtn.frame = CGRectMake(0, 0, 44, 44);
+ [backBtn setImage:[UIImage imageNamed:@"ui_title_arrow_left.png"] forState:UIControlStateNormal];
+ [backBtn addTarget:self action:@selector(backBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
+ backBtn.frame = CGRectMake(0, 0, 44, 44);
+ [backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
+ backBtn.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 20);
+ UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithCustomView:backBtn];
+ self.navigationItem.leftBarButtonItem = item;
+}
+
+- (UIImageView *)bgImageView{
+ if (!_bgImageView) {
+ _bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
+ [_bgImageView setImage:[UIImage imageNamed:@"bg.png"]];
+ _bgImageView.userInteractionEnabled = YES;
+ }
+ return _bgImageView;
+}
+
+//视频区域
+-(TCLiveVideoLayoutView *)videoLayoutView{
+ if (!_videoLayoutView) {
+ _videoLayoutView = [[TCLiveVideoLayoutView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
+ }
+ return _videoLayoutView;
+}
+//控制bar
+- (TCLiveVideoControlBar *)controlBar{
+ if (!_controlBar) {
+ _controlBar = [[TCLiveVideoControlBar alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - LIVE_CONTROL_BAR_HEIGHT, self.view.frame.size.width, LIVE_CONTROL_BAR_HEIGHT)];
+ _controlBar.delegate = self;
+ }
+ return _controlBar;
+}
+//消息列表
+- (UITableView *)chatTableView{
+ if (!_chatTableView) {
+ _chatTableView = [[TCLiveChatTableView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - LIVE_CONTROL_BAR_HEIGHT - 400, 250, 400) style:UITableViewStylePlain];
+ _chatTableView.backgroundColor = [UIColor clearColor];
+ self.origChatTableViewFrame = self.chatTableView.frame;
+ }
+ return _chatTableView;
+}
+//输入框
+- (UITextField *)inputTextField{
+ if (!_inputTextField) {
+ _inputTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height , self.view.frame.size.width, LIVE_INPUTTEXTFIELD_HEIGHT)];
+ _inputTextField.delegate = self;
+ _inputTextField.backgroundColor= [[UIColor whiteColor] colorWithAlphaComponent:0.9];
+ _inputTextField.placeholder = @"请输入内容";
+ _inputTextField.returnKeyType = UIReturnKeySend;
+ self.origInputTextFieldFrame = self.inputTextField.frame;
+ }
+ return _inputTextField;
+}
+//关闭界面退出房间
+- (void)backBtnClicked:(UIButton *)sender{
+ [self.navigationController popViewControllerAnimated:YES];
+ [[ILiveRoomManager getInstance] quitRoom:^{
+ NSLog(@"-----> quit room succ");
+ [[UIToastView getInstance] showToastWithMessage:@"退出房间成功" toastMode:UIToastShowMode_Succ];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"-----> quit room fail,%@ %d %@",module, errId, errMsg);
+ [[UIToastView getInstance] showToastWithMessage:@"退出房间失败" toastMode:UIToastShowMode_fail];
+ }];
+ [_logTimer invalidate];
+ _logTimer = nil;
+
+}
+#pragma mark - ILiveRoomDisconnectListener
+- (BOOL)onRoomDisconnect:(int)reason;{
+ [self backBtnClicked:nil];
+ return YES;
+}
+#pragma mark - UITextFieldDelegate
+
+- (BOOL)textFieldShouldReturn:(UITextField *)textField;{
+ [textField resignFirstResponder];
+ NSString *text = textField.text;
+ if (text.length > 0) {
+ [self.chatTableView sendMessage:text];
+ textField.text = nil;
+ }
+ else{
+ return NO;
+ }
+ return YES;
+}
+#pragma mark - TCLiveVideoControlBarDelegate
+-(void)logBtnClick:(UIButton *)sender{
+ if (sender.selected) {
+ if (!_logTimer) {
+ [self logUpdate];
+ _logTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(logUpdate) userInfo:nil repeats:YES];
+ }
+ }
+ else{
+ [_logTimer invalidate];
+ _logTimer = nil;
+ [_videoLayoutView closeLogView];
+ }
+}
+- (void)chatBtnClick:(UIButton *)sender{
+ [self.inputTextField becomeFirstResponder];
+}
+- (void)logUpdate{
+ QAVContext *avContext = [[ILiveSDK getInstance] getAVContext];
+ NSString *qualityStr = [avContext.room getQualityTips];
+ [_videoLayoutView showLogView:qualityStr];
+}
+
+- (void)beautyBtnClick:(UIButton *)sender{
+ if (sender.selected) {
+ [self.videoLayoutView setBeautyLevel:9];
+ }
+ else{
+ [self.videoLayoutView setBeautyLevel:0];
+ }
+}
+#pragma mark - 键盘事件
+- (void)keybaordAnimationWithDuration:(CGFloat)duration keyboardOriginY:(CGFloat)keyboardOriginY{
+
+ __block TCLiveRoomViewController *blockSelf = self;
+ //作为视图的键盘,弹出动画也是UIViewAnimationOptionCurveEaseIn的方式
+ [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
+ //text field
+ if(keyboardOriginY == blockSelf.view.frame.size.height){
+ blockSelf.inputTextField.frame = blockSelf.origInputTextFieldFrame;
+ blockSelf.chatTableView.frame = blockSelf.origChatTableViewFrame;
+ }
+ else{
+ blockSelf.inputTextField.frame = CGRectMake(blockSelf.inputTextField.frame.origin.x,keyboardOriginY - blockSelf.inputTextField.frame.size.height, blockSelf.inputTextField.frame.size.width, blockSelf.inputTextField.frame.size.height);
+ blockSelf.chatTableView.frame = CGRectMake(blockSelf.chatTableView.frame.origin.x,keyboardOriginY - blockSelf.chatTableView.frame.size.height - LIVE_INPUTTEXTFIELD_HEIGHT, blockSelf.chatTableView.frame.size.width, blockSelf.chatTableView.frame.size.height);
+ }
+
+ } completion:nil];
+}
+- (void)keyboardWillChangeFrame:(NSNotification *)notify{
+ NSDictionary * info = notify.userInfo;
+ //动画时间
+ CGFloat animationDuration = [info[UIKeyboardAnimationDurationUserInfoKey] floatValue];
+ //键盘目标位置
+ CGRect keyboardAimFrame = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+ if ([self.inputTextField isFirstResponder]) {
+ [self keybaordAnimationWithDuration:animationDuration keyboardOriginY:keyboardAimFrame.origin.y];
+ }
+}
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.h
new file mode 100644
index 0000000..b3a8650
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.h
@@ -0,0 +1,13 @@
+//
+// UIColorEX.h
+// TRTC
+//
+// Created by Tencent on 2018/6/5.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+
+@interface UIColor (EX)
++ (UIColor *)colorWithRGBHex: (unsigned int)hex;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.m
new file mode 100644
index 0000000..458f5fc
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.m
@@ -0,0 +1,25 @@
+//
+// UIColorEX.m
+// TRTC
+//
+// Created by Tencent on 2018/6/5.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import "UIColorEX.h"
+
+@implementation UIColor (EX)
+
++ (UIColor *)colorWithRGBHex: (unsigned int)hex
+{
+ int r = (hex >> 16) & 0xFF;
+ int g = (hex >> 8) & 0xFF;
+ int b = (hex) & 0xFF;
+
+ return [UIColor colorWithRed:r / 255.0f
+ green:g / 255.0f
+ blue:b / 255.0f
+ alpha:1.0f];
+}
+
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.h
new file mode 100644
index 0000000..4103160
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.h
@@ -0,0 +1,20 @@
+//
+// UIToastView.h
+// TRTC
+//
+// Created by Tencent on 2018/6/9.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+
+typedef enum {
+ UIToastShowMode_Default = 1,
+ UIToastShowMode_Succ,
+ UIToastShowMode_fail,
+}UIToastShowMode;
+
+@interface UIToastView : UIView
++ (UIToastView *)getInstance;
+- (void)showToastWithMessage:(NSString *)text toastMode:(UIToastShowMode )mode;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.m
new file mode 100644
index 0000000..e5d631b
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.m
@@ -0,0 +1,96 @@
+//
+// UIToastView.m
+// TRTC
+//
+// Created by Tencent on 2018/6/9.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import "UIToastView.h"
+
+#define UITOAST_HEIGHT 30
+#define UITOAST_IMAGE_HEIGHT 15
+@interface UIToastView ()
+@property(nonatomic,strong)UILabel *toastLabel;
+@property(nonatomic,strong)UIImageView *toastImageView;
+@end
+
+@implementation UIToastView
+
++ (UIToastView *)getInstance{
+ static UIToastView *singleTon = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ singleTon = [UIToastView new];
+ });
+ return singleTon;
+}
+
+-(instancetype)initWithFrame:(CGRect)frame{
+ if (self = [super initWithFrame:frame]) {
+ self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
+ self.layer.cornerRadius = 4;
+ self.layer.masksToBounds = YES;
+
+ [self addSubview:self.toastImageView];
+ [self addSubview:self.toastLabel];
+ }
+ return self;
+}
+- (UILabel *)toastLabel{
+ if (!_toastLabel) {
+ _toastLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ _toastLabel.numberOfLines = 1;
+ _toastLabel.textAlignment = NSTextAlignmentCenter;
+ _toastLabel.textColor = [UIColor whiteColor];
+ _toastLabel.font = [UIFont systemFontOfSize:16];
+ }
+ return _toastLabel;
+}
+
+-(UIImageView *)toastImageView{
+ if (!_toastImageView) {
+ _toastImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
+
+ }
+ return _toastImageView;
+}
+
+- (void)showToastWithMessage:(NSString *)text toastMode:(UIToastShowMode )mode{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIToastView *view = [UIToastView new];
+ CGRect rect = [text boundingRectWithSize:CGSizeMake([UIScreen mainScreen].bounds.size.width - 80, 20) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil];
+ view.frame = CGRectMake(0, 0, rect.size.width + 10 + 20, UITOAST_HEIGHT);
+ view.toastImageView.frame = CGRectMake(5, 0, UITOAST_IMAGE_HEIGHT, UITOAST_IMAGE_HEIGHT);
+ view.toastImageView.center = CGPointMake(view.toastImageView.center.x, view.frame.size.height/2);
+ view.toastLabel.frame = CGRectMake(25, 0, rect.size.width, UITOAST_HEIGHT);
+ view.toastLabel.text = text;
+ if (UIToastShowMode_Succ == mode){
+ view.toastImageView.image = [UIImage imageNamed:@"ic_toast_success@2x"];
+ }
+ else if(UIToastShowMode_fail == mode){
+ view.toastImageView.image = [UIImage imageNamed:@"icon_sign@2x"];
+ }
+ else{
+ view.toastImageView.frame = CGRectZero;
+ view.frame = CGRectMake(0, 0, rect.size.width + 10 , UITOAST_HEIGHT);
+ view.toastLabel.frame = CGRectMake(5, 0, rect.size.width, UITOAST_HEIGHT);
+ }
+ view.center = [[UIApplication sharedApplication] keyWindow].center;
+ [[[UIApplication sharedApplication] keyWindow] addSubview:view];
+ [UIView animateWithDuration:0.5 animations:^{
+ view.alpha = 1;
+ } completion:^(BOOL finished) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ [UIView animateWithDuration:0.5 animations:^{
+ view.alpha = 0;
+ } completion:^(BOOL finished) {
+ [view removeFromSuperview];
+ }];
+ });
+ }];
+ });
+
+}
+
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.h
new file mode 100644
index 0000000..93f1a42
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.h
@@ -0,0 +1,34 @@
+//
+// TCLiveVideoControlBar.h
+// TRTC
+//
+// Created by Tencent on 2018/6/3.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+
+@protocol TCLiveVideoControlBarDelegate
+@optional
+//切换摄像头
+- (void)switchCameraBtnClick:(UIButton *)sender;
+//开关美颜
+- (void)beautyBtnClick:(UIButton *)sender;
+//开关麦克风
+- (void)voiceBtnClick:(UIButton *)sender;
+//展示日志
+- (void)logBtnClick:(UIButton *)sender;
+//反馈
+- (void)feedBackBtnClick:(UIButton *)sender;
+//切换配置
+- (void)changeRoleBtnClick:(UIButton *)sender;
+//聊天
+- (void)chatBtnClick:(UIButton *)sender;
+@end
+
+@interface TCLiveVideoControlBar : UIView
+@property(nonatomic,weak)id delegate;
+@property(nonatomic,strong) UIButton *logBtn;
+- (void)enableLog:(BOOL)endable;
+- (void)enableBeauty:(BOOL)enable;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.m
new file mode 100644
index 0000000..0eb7c7d
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.m
@@ -0,0 +1,298 @@
+//
+// TCLiveVideoControlBar.m
+// TRTC
+//
+// Created by Tencent on 2018/6/3.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+
+
+#import "TCLiveVideoControlBar.h"
+#import
+#import
+#import
+#import "TCLiveRequestManager.h"
+#import "UIToastView.h"
+#import "TCLiveRoomViewController.h"
+
+#define CONTROLBAR_BUTTON_WIDTH 50
+
+@interface TCLiveVideoControlBar ()
+@property(nonatomic,strong) UIButton *chatBtn;
+@property(nonatomic,strong) UIButton *switchCamera;
+@property(nonatomic,strong) UIButton *beautyBtn;
+@property(nonatomic,strong) UIButton *voiceBtn;
+@property(nonatomic,strong) UIButton *changeRoleBtn;
+@property(nonatomic,strong) UIButton *feedBackBtn;
+@end
+
+@implementation TCLiveVideoControlBar
+
+-(instancetype)initWithFrame:(CGRect)frame
+{
+ if (self = [super initWithFrame:frame]) {
+// [self addSubview:self.chatBtn];
+// [self addSubview:self.beautyBtn];
+ [self addSubview:self.voiceBtn];
+// [self addSubview:self.logBtn];
+ [self addSubview:self.switchCamera];
+// [self addSubview:self.feedBackBtn];
+// [self addSubview:self.changeRoleBtn];
+ self.backgroundColor = [UIColor clearColor];
+ }
+ return self;
+}
+
+-(UIButton *)chatBtn{
+ if(!_chatBtn){
+ _chatBtn = [self createCustomControlBtn:@"聊天" withImage:[UIImage imageNamed:@"chat.png"] selectedImage:nil];
+ [_chatBtn addTarget:self action:@selector(chatBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+ _chatBtn.frame = CGRectMake(self.frame.size.width / 2 - CONTROLBAR_BUTTON_WIDTH * 3.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ return _chatBtn;
+}
+-(UIButton *)switchCamera{
+ if (!_switchCamera) {
+ _switchCamera = [self createCustomControlBtn:@"翻转" withImage:[UIImage imageNamed:@"camera.png"] selectedImage:[UIImage imageNamed:@"camera-gray.png"]];
+ [_switchCamera addTarget:self action:@selector(switchCameraClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+// _switchCamera.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH * 2.5,0,CONTROLBAR_BUTTON_WIDTH,self.frame.size.height);
+ _switchCamera.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH * 1.5,0,CONTROLBAR_BUTTON_WIDTH,self.frame.size.height);
+ return _switchCamera;
+}
+
+- (UIButton *)beautyBtn{
+ if (!_beautyBtn) {
+ _beautyBtn = [self createCustomControlBtn:@"美颜" withImage:[UIImage imageNamed:@"beauty.png"] selectedImage:[UIImage imageNamed:@"beauty-dis.png"]];
+ [_beautyBtn addTarget:self action:@selector(beautyBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+ _beautyBtn.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH *1.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ return _beautyBtn;
+}
+
+- (UIButton *)voiceBtn{
+ if (!_voiceBtn) {
+ _voiceBtn = [self createCustomControlBtn:@"声音" withImage:[UIImage imageNamed:@"mic-dis.png"] selectedImage:[UIImage imageNamed:@"mic.png"]];
+ [_voiceBtn addTarget:self action:@selector(voiceBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+// _voiceBtn.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH * 0.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ _voiceBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 0.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ return _voiceBtn;
+}
+
+-(UIButton *)changeRoleBtn{
+ if (!_changeRoleBtn) {
+ _changeRoleBtn = [self createCustomControlBtn:@"配置" withImage:[UIImage imageNamed:@"role.png"] selectedImage:[UIImage imageNamed:@"role.png"]];
+ [_changeRoleBtn addTarget:self action:@selector(changeRoleBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+ _changeRoleBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 0.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ return _changeRoleBtn;
+}
+
+- (UIButton *)feedBackBtn{
+ if (!_feedBackBtn) {
+ _feedBackBtn = [self createCustomControlBtn:@"反馈" withImage:[UIImage imageNamed:@"feedback.png"] selectedImage:[UIImage imageNamed:@"feedback.png"]];
+ [_feedBackBtn addTarget:self action:@selector(feedBackBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+ _feedBackBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 1.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ return _feedBackBtn;
+}
+
+- (UIButton *)logBtn{
+ if (!_logBtn) {
+ _logBtn = [self createCustomControlBtn:@"信息" withImage:[UIImage imageNamed:@"log.png"] selectedImage:[UIImage imageNamed:@"log2.png"]];
+ [_logBtn addTarget:self action:@selector(logBtnClick:) forControlEvents:UIControlEventTouchUpInside];
+ }
+ _logBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 2.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
+ return _logBtn;
+}
+
+- (UIButton *)createCustomControlBtn:(NSString *)wording withImage:(UIImage *)image selectedImage:(UIImage *)highlightImage{
+ UIButton *customButton = [[UIButton alloc] initWithFrame:CGRectZero];
+ [customButton setTitle:wording forState:UIControlStateNormal];
+ customButton.titleLabel.font = [UIFont systemFontOfSize:11];
+ [customButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
+ [customButton setImage:image forState:UIControlStateNormal];
+ [customButton setImage:highlightImage forState:UIControlStateSelected];
+ CGFloat imageHeight = image.size.height;
+ CGFloat imageWidth = image.size.width;
+ [customButton setTitleEdgeInsets:UIEdgeInsetsMake(15, -60, -15, 0)];
+ [customButton setImageEdgeInsets:UIEdgeInsetsMake(10, 10, self.frame.size.height - (CONTROLBAR_BUTTON_WIDTH - 20)/imageWidth *imageHeight -10, 10)];
+ return customButton;
+}
+#pragma mark - Handle Event
+//聊天按钮
+- (void)chatBtnClick:(UIButton *)sender{
+ if (!sender.selected) {
+ sender.selected = YES;
+ }
+ else{
+ sender.selected = NO;
+ }
+ if ([_delegate respondsToSelector:@selector(chatBtnClick:)]) {
+ [_delegate chatBtnClick:sender];
+ }
+}
+//切换摄像头
+- (void)switchCameraClick:(UIButton *)sender{
+ ILiveFrameDispatcher *frameDispatcher = [[ILiveRoomManager getInstance] getFrameDispatcher];
+ ILiveRenderView *renderView = [frameDispatcher getRenderView:[TCLiveRequestManager getInstance].userID srcType:QAVVIDEO_SRC_TYPE_CAMERA];
+ if (!sender.selected) {
+ sender.selected = YES;
+ renderView.isMirror = NO;
+ }
+ else{
+ sender.selected = NO;
+ renderView.isMirror = YES;
+ }
+ [[ILiveRoomManager getInstance] switchCamera:^{
+ NSLog(@"switch camera succ");
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"switch camera fail");
+ [[UIToastView getInstance] showToastWithMessage:@"切换摄像头失败" toastMode:UIToastShowMode_fail];
+ }];
+ if ([_delegate respondsToSelector:@selector(switchCameraClick:)]) {
+ [_delegate switchCameraBtnClick:sender];
+ }
+}
+//美颜
+- (void)beautyBtnClick:(UIButton *)sender{
+ if (!sender.selected) {
+ sender.selected = YES;
+ }
+ else{
+ sender.selected = NO;
+ }
+ if ([_delegate respondsToSelector:@selector(beautyBtnClick:)]) {
+ [_delegate beautyBtnClick:sender];
+ }
+}
+- (void)enableBeauty:(BOOL)enable{
+ if (enable) {
+ _beautyBtn.selected = YES;
+ }
+ else{
+ _beautyBtn.selected = NO;
+ }
+ if ([_delegate respondsToSelector:@selector(beautyBtnClick:)]) {
+ [_delegate beautyBtnClick:_beautyBtn];
+ }
+}
+//语音开关
+- (void)voiceBtnClick:(UIButton *)sender{
+ if (!sender.selected) {
+ sender.selected = YES;
+ }
+ else{
+ sender.selected = NO;
+ }
+ [[ILiveRoomManager getInstance] enableMic:!sender.selected succ:^{
+ NSLog(@"enable mic succ");
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"enable mic fail");
+ [[UIToastView getInstance] showToastWithMessage:@"关麦失败" toastMode:UIToastShowMode_fail];
+ }];
+ if ([_delegate respondsToSelector:@selector(voiceBtnClick:)]) {
+ [_delegate voiceBtnClick:sender];
+ }
+
+}
+//配置角色
+- (void)changeRoleBtnClick:(UIButton *)sender{
+ if (!sender.selected) {
+ sender.selected = YES;
+ }
+ else{
+ sender.selected = NO;
+ }
+ if ([_delegate respondsToSelector:@selector(changeRoleBtnClick:)]) {
+ [_delegate changeRoleBtnClick:sender];
+ }
+ if ([_delegate isKindOfClass:[UIViewController class]]) {
+ [self showChangeRoleMenuOnVC:(UIViewController *)_delegate];
+ }
+}
+//日志
+- (void)logBtnClick:(UIButton *)sender{
+ if (!sender.selected) {
+ sender.selected = YES;
+ }
+ else{
+ sender.selected = NO;
+ }
+ if ([_delegate respondsToSelector:@selector(logBtnClick:)]) {
+ [_delegate logBtnClick:sender];
+ }
+}
+//问题反馈
+- (void)feedBackBtnClick:(UIButton *)sender{
+ if (!sender.selected) {
+ sender.selected = YES;
+ }
+ else{
+ sender.selected = NO;
+ }
+ if ([_delegate respondsToSelector:@selector(feedBackBtnClick:)]) {
+ [_delegate feedBackBtnClick:sender];
+ }
+ if ([_delegate isKindOfClass:[UIViewController class]]) {
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"若您在接入过程中有疑问可直接反馈给我们" message:@"邮箱联系地址:trtcfb@qq.com" preferredStyle:UIAlertControllerStyleAlert];
+ [alert addAction:[UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:nil]];
+ [((UIViewController *)self.delegate) presentViewController:alert animated:YES completion:nil];
+ }
+}
+//log展示开关
+- (void)enableLog:(BOOL)endable{
+ self.logBtn.selected = endable;
+ if ([_delegate respondsToSelector:@selector(logBtnClick:)]) {
+ [_delegate logBtnClick:self.logBtn];
+ }
+}
+//弹出配置菜单
+-(void)showChangeRoleMenuOnVC:(UIViewController *)vc{
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请选择配置分辨率" message:nil preferredStyle:UIAlertControllerStyleAlert];
+ [alertController addAction:[UIAlertAction actionWithTitle:@"1280x720 1000-1800kbps 20fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+ [[ILiveRoomManager getInstance] changeRole:@"ed1280" succ:^{
+ NSLog(@"更换ed1280 succ");
+ [self enableLog:YES];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"更换ed1280 fail");
+ }];
+ }]];
+ [alertController addAction:[UIAlertAction actionWithTitle:@"960x540 500-800kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+ [[ILiveRoomManager getInstance] changeRole:@"ed960" succ:^{
+ NSLog(@"更换ed960 succ");
+ [self enableLog:YES];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"更换ed960 fail");
+ }];
+ }]];
+ [alertController addAction:[UIAlertAction actionWithTitle:@"640x480 400-800kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+ [[ILiveRoomManager getInstance] changeRole:@"ed640" succ:^{
+ NSLog(@"更换ed640 succ");
+ [self enableLog:YES];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"更换ed640 fail");
+ }];
+ }]];
+ [alertController addAction:[UIAlertAction actionWithTitle:@"480x360 300-600kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+ [[ILiveRoomManager getInstance] changeRole:@"ed480" succ:^{
+ NSLog(@"更换ed480 succ");
+ [self enableLog:YES];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"更换ed480 fail");
+ }];
+ }]];
+ [alertController addAction:[UIAlertAction actionWithTitle:@"320x240 200-400kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+ [[ILiveRoomManager getInstance] changeRole:@"ed320" succ:^{
+ NSLog(@"更换ed320 succ");
+ [self enableLog:YES];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"更换ed320 fail");
+ }];
+ }]];
+ [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
+ [vc presentViewController:alertController animated:YES completion:nil];
+}
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.h b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.h
new file mode 100644
index 0000000..1898d10
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.h
@@ -0,0 +1,42 @@
+//
+// TCLiveVideoLayoutView.h
+// TRTC
+//
+// Created by Tencent on 2018/6/3.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import
+#import
+
+@class TCLiveVideoElementView;
+
+typedef enum {
+ TCLiveRoomVideoLayoutStyle_1v3,
+ TCLiveRoomVideoLayoutStyle_4_geizi,
+}TCLiveRoomVideoLayoutStyle;
+
+@protocol TCLiveVideoElementViewDelegate
+- (void)tapHandle:(TCLiveVideoElementView *)view;
+@end
+
+@interface TCLiveVideoLayoutView : UIView
+//画面布局样式
+@property(nonatomic,assign) TCLiveRoomVideoLayoutStyle layoutStyle;
+//显示日志信息
+- (void)showLogView:(NSString *)qualityParams;
+//关闭日志信息
+- (void)closeLogView;
+//设置美颜
+- (void)setBeautyLevel:(NSInteger)level;
+@end
+
+@interface TCLiveVideoElementView: UIView
+//展示userID
+@property(nonatomic,strong)UILabel *userIdLabel;
+//展示视频分辨率、帧率等信息
+@property(nonatomic,strong)UILabel *videoInfoLable;
+@property(nonatomic,weak)TCLiveVideoLayoutView *delegate;
+//可拖动开关
+- (void)ennableDraggable:(BOOL)draggable;
+@end
diff --git a/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.m b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.m
new file mode 100644
index 0000000..b13299e
--- /dev/null
+++ b/src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.m
@@ -0,0 +1,403 @@
+//
+// TCLiveVideoLayoutView.m
+// TRTC
+//
+// Created by Tencent on 2018/6/3.
+// Copyright © 2018年 Tencent. All rights reserved.
+//
+
+#import "TCLiveVideoLayoutView.h"
+#import
+#import
+#import
+#import "TCLiveRequestManager.h"
+#import "UIColorEX.h"
+#import "TXCVideoPreprocessor.h"
+#define LIVE_VIDEO_NUM 4
+
+@interface TCLiveVideoLayoutView ()
+@property(nonatomic,strong)NSMutableArray *liveVideos;
+@property(nonatomic,strong)NSMutableArray *liveRnederView;
+@property(nonatomic,strong)UITextView *logView;
+@property(nonatomic,assign) BOOL isShowLogInfo;
+@property(nonatomic,assign) NSDate *startTime;
+@property (nonatomic, strong) TXCVideoPreprocessor *preProcessor;
+@property (nonatomic, assign) Byte *processorBytes;
+@end
+
+@implementation TCLiveVideoLayoutView
+
+-(instancetype)initWithFrame:(CGRect)frame{
+ if (self = [super initWithFrame:frame]) {
+ self.backgroundColor = [UIColor clearColor];
+ self.layoutStyle = TCLiveRoomVideoLayoutStyle_1v3;
+ [self initialVideoViews];
+ [[TILLiveManager getInstance] setAVListener:self];
+ [[ILiveRoomManager getInstance] setLocalVideoDelegate:self];
+ [[ILiveRoomManager getInstance] setRemoteVideoDelegate:self];
+ [[ILiveRoomManager getInstance] setScreenVideoDelegate:self];
+ //美颜处理器
+ self.preProcessor = [[TXCVideoPreprocessor alloc] init];
+ [self.preProcessor setDelegate:self];
+ //记录进入时间
+ self.startTime = [NSDate date];
+ }
+ return self;
+}
+//初始化每路视频view
+- (void)initialVideoViews{
+ _liveVideos = [NSMutableArray array];
+ //自定义视窗数组
+ NSArray *frames = [self customLayoutFrames];
+ for (int i = 0; i < frames.count; i++) {
+ TCLiveVideoElementView *view = [[TCLiveVideoElementView alloc] initWithFrame:CGRectZero];
+ view.delegate = self;
+ [self addSubview:view];
+ [_liveVideos addObject:view];
+ }
+}
+-(UITextView *)logView{
+ if(!_logView){
+ _logView = [[UITextView alloc] initWithFrame:CGRectMake(0, self.frame.size.height/5, self.frame.size.width/3*2, self.frame.size.height/2)];
+ _logView.textColor = [UIColor colorWithRGBHex:0xFF4081];
+ _logView.font = [UIFont systemFontOfSize:14];
+ _logView.backgroundColor = [UIColor clearColor];
+ _logView.editable = NO;
+ }
+ return _logView;
+}
+
+//自定义布局(开发者可自定义每个视窗的frame来实现,视窗个数即为frames数组个数)
+- (NSArray *)customLayoutFrames{
+ NSMutableArray *frames = [NSMutableArray array];
+ if (TCLiveRoomVideoLayoutStyle_1v3 == _layoutStyle) {
+ [frames removeAllObjects];
+ int smallViewWidth = ((self.frame.size.height - 20 - 150 - 10 *2)/3)*(3.0/4.0);
+ int smallViewHeight = (self.frame.size.height - 20 - 150 - 10 *2)/3;
+ CGRect frame1 = self.bounds;
+ CGRect frame2 = CGRectMake(self.bounds.size.width - 10 - smallViewWidth, 84 , smallViewWidth, smallViewHeight);
+ CGRect frame3 = CGRectMake(self.bounds.size.width - 10 - smallViewWidth, frame2.origin.y + frame2.size.height + 10, smallViewWidth, smallViewHeight);
+ CGRect frame4 = CGRectMake(self.bounds.size.width - 10 - smallViewWidth, frame3.origin.y + frame3.size.height + 10, smallViewWidth, smallViewHeight);
+
+ [frames addObject:[NSValue valueWithCGRect:frame1]];
+ [frames addObject:[NSValue valueWithCGRect:frame2]];
+ [frames addObject:[NSValue valueWithCGRect:frame3]];
+ [frames addObject:[NSValue valueWithCGRect:frame4]];
+ }
+ else if (TCLiveRoomVideoLayoutStyle_4_geizi == _layoutStyle) {
+ [frames removeAllObjects];
+ CGRect frame1 = CGRectMake(20, 50, (self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
+ CGRect frame2 = CGRectMake(20 + (self.frame.size.width -40)/2 +1, 50, (self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
+ CGRect frame3 = CGRectMake(20, 50 + (self.frame.size.height - 100)/2 +1, (self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
+ CGRect frame4 = CGRectMake(20 + (self.frame.size.width -40)/2 +1, 50 + (self.frame.size.height - 100)/2 +1,(self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
+ [frames addObject:[NSValue valueWithCGRect:frame1]];
+ [frames addObject:[NSValue valueWithCGRect:frame2]];
+ [frames addObject:[NSValue valueWithCGRect:frame3]];
+ [frames addObject:[NSValue valueWithCGRect:frame4]];
+ }
+ return frames;
+}
+
+//添加视频画面
+-(void)addLiveRenderView:(ILiveRenderView *)renderView{
+ NSArray *frames = [self customLayoutFrames];
+ for (int i = 0; i < _liveVideos.count;i++) {
+ TCLiveVideoElementView *videoView = _liveVideos[i];
+ videoView.frame = [frames[i] CGRectValue];
+ if (![self getLiveRenderViewOnElementView:videoView]) {
+ renderView.frame = videoView.bounds;
+ [videoView insertSubview:renderView atIndex:0];
+ break;
+ }
+ }
+}
+//移除视频画面
+- (void)removeLiverRenderVeiw:(ILiveRenderView *)renderView{
+ if ([[renderView superview] isKindOfClass:[TCLiveVideoElementView class]]) {
+ TCLiveVideoElementView *videoView = (TCLiveVideoElementView *)[renderView superview];
+ videoView.frame = CGRectZero;
+ [renderView removeFromSuperview];
+ }
+}
+
+//画面切换
+- (void)tapHandle:(TCLiveVideoElementView *)view{
+ NSUInteger index = [_liveVideos indexOfObject:view];
+ TCLiveVideoElementView *bigView = _liveVideos[0];
+ ILiveRenderView *bigRenderView = [self getLiveRenderViewOnElementView:bigView];
+ ILiveRenderView *renderView = [self getLiveRenderViewOnElementView:view];
+ if (index > 0) {
+ [UIView animateWithDuration:0.5 animations:^{
+ bigView.frame = view.frame;
+ view.frame = [[self customLayoutFrames][0] CGRectValue];
+ bigRenderView.frame = bigView.bounds;
+ renderView.frame = view.bounds;
+ [self.liveVideos exchangeObjectAtIndex:0 withObjectAtIndex:index];
+ [self exchangeSubviewAtIndex:0 withSubviewAtIndex:index];
+ } completion:^(BOOL finished) {
+
+ }];
+
+ }
+
+}
+//获取指定视窗的渲染view
+- (ILiveRenderView *)getLiveRenderViewOnElementView:(TCLiveVideoElementView *)elementView{
+ ILiveRenderView *renderView = nil;
+ for (id view in [elementView subviews]) {
+ if ([view isKindOfClass:[ILiveRenderView class]]) {
+ renderView = view;
+ }
+ }
+ return renderView;
+}
+-(void)setBeautyLevel:(NSInteger)level{
+ [self.preProcessor setBeautyLevel:level];
+}
+#pragma mark - ILiveMemStatusListener
+//房间成员状态变化事件
+- (BOOL)onEndpointsUpdateInfo:(QAVUpdateEvent)event updateList:(NSArray *)endpoints{
+ if (endpoints.count <= 0) {
+ return NO;
+ }
+ for (QAVEndpoint *endoption in endpoints) {
+ switch (event) {
+ case QAV_EVENT_ID_ENDPOINT_HAS_CAMERA_VIDEO:
+ {
+ ILiveFrameDispatcher *frameDispatcher = [[ILiveRoomManager getInstance] getFrameDispatcher];
+ ILiveRenderView *renderView = [frameDispatcher addRenderAt:CGRectZero forIdentifier:endoption.identifier srcType:QAVVIDEO_SRC_TYPE_CAMERA];
+ renderView.isRotate = NO;
+ renderView.autoRotate = NO;
+ renderView.isMirror = YES;
+ renderView.identifier = endoption.identifier;
+ renderView.diffDirectionRenderMode = ILIVERENDERMODE_SCALEASPECTFILL;
+ if ([[TCLiveRequestManager getInstance].userID isEqualToString:endoption.identifier]) {
+ renderView.rotateAngle = ILIVEROTATION_90;
+ }
+ [self addLiveRenderView:renderView];
+ }
+ break;
+ case QAV_EVENT_ID_ENDPOINT_NO_CAMERA_VIDEO:
+ {
+ ILiveFrameDispatcher *frameDispatcher = [[ILiveRoomManager getInstance] getFrameDispatcher];
+ ILiveRenderView *renderView = [frameDispatcher removeRenderViewFor:endoption.identifier srcType:QAVVIDEO_SRC_TYPE_CAMERA];
+ [self removeLiverRenderVeiw:renderView];
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return YES;
+}
+/***本地画面帧数据回调***/
+#pragma mark - QAVLocalVideoDelegate
+- (void)OnLocalVideoPreview:(QAVVideoFrame *)frameData{
+ [self showElementVideoInfoWithVideoFrame:frameData];
+}
+
+- (void)OnLocalVideoPreProcess:(QAVVideoFrame *)frameData{
+
+
+ [self.preProcessor setOutputSize:CGSizeMake(frameData.frameDesc.width, frameData.frameDesc.height)];
+ [self.preProcessor setCropRect:CGRectMake(0, 0,frameData.frameDesc.width, frameData.frameDesc.height)];
+ [self.preProcessor processFrame:frameData.data width:frameData.frameDesc.width height:frameData.frameDesc.height orientation:TXE_ROTATION_0 inputFormat:TXE_FRAME_FORMAT_NV12 outputFormat:TXE_FRAME_FORMAT_NV12];
+ //将处理完的数据拷贝到原来的地址空间,如果是同步处理,此时会先执行didProcessFrame回调
+ if(self.processorBytes){
+ memcpy(frameData.data, self.processorBytes, frameData.frameDesc.width * frameData.frameDesc.height * 3 / 2);
+ }
+}
+
+- (void)OnLocalVideoRawSampleBuf:(CMSampleBufferRef)buf result:(CMSampleBufferRef *)ret{
+
+}
+
+/***远端视频帧数据回调***/
+#pragma mark - QAVRemoteVideoDelegate
+- (void)OnVideoPreview:(QAVVideoFrame *)frameData{
+ [self showElementVideoInfoWithVideoFrame:frameData];
+}
+/***远端屏幕分享帧数据回调***/
+#pragma mark - ILiveScreenVideoDelegate
+
+-(void)onScreenVideoPreview:(QAVVideoFrame *)frameData{
+ [self showElementVideoInfoWithVideoFrame:frameData];
+}
+/***首帧回调***/
+#pragma mark - ILVLiveAVListener
+- (void)onFirstFrameRecved:(int)width height:(int)height identifier:(NSString *)identifier srcType:(avVideoSrcType)srcType;{
+
+}
+/***美颜相关处理***/
+#pragma mark - TXIVideoPreprocessorDelegate
+- (void)didProcessFrame:(Byte *)bytes width:(NSInteger)width height:(NSInteger)height format:(TXEFrameFormat)format timeStamp:(UInt64)timeStamp
+{
+ self.processorBytes = bytes;
+}
+#pragma mark - LOG相关
+- (void)showLogView:(NSString *)qualityParams{
+ NSString *role = [[[[qualityParams componentsSeparatedByString:@"ControlRole="] lastObject] componentsSeparatedByString:@","] firstObject];
+
+ self.logView.text = [NSString stringWithFormat:@"发送速率:%ldkbps 丢包率:%.1f%%\n接收速率:%ldkbps 丢包率:%.1f%%\n应用CPU:%.1f%% 系统CPU:%.1f%%\n角色:%@\nSDKAPPID:%d\nSDKVersion:%@",(long)[[ILiveRoomManager getInstance] getQualityData].sendRate,[[ILiveRoomManager getInstance] getQualityData].sendLossRate/100.0,(long)[[ILiveRoomManager getInstance] getQualityData].recvRate,[[ILiveRoomManager getInstance] getQualityData].recvLossRate/100.0,[[ILiveRoomManager getInstance] getQualityData].appCPURate/100.0,[[ILiveRoomManager getInstance] getQualityData].sysCPURate/100.0,role,[TCLiveRequestManager getInstance].sdkAppID,[[ILiveSDK getInstance] getVersion]];
+ if(![_logView superview]){
+ [self addSubview:_logView];
+ }
+ self.isShowLogInfo = YES;
+}
+
+- (void)showElementVideoInfoWithVideoFrame:(QAVVideoFrame *)frame{
+ if(!self.isShowLogInfo){
+ return;
+ }
+ NSString *userId = frame.identifier;
+ NSString *fps = @"";
+ if (userId.length == 0){
+ userId = [TCLiveRequestManager getInstance].userID;
+ }
+ else{
+ NSString *qualityParams = [[[ILiveSDK getInstance] getAVContext].room getQualityTips];
+ NSString *decode = [[qualityParams componentsSeparatedByString:@"音频部分:========"] firstObject];
+ NSString *itemDecode = [[[[decode componentsSeparatedByString:[NSString stringWithFormat:@"成员:%@",userId]] lastObject] componentsSeparatedByString:@"接收参数"] firstObject];
+ fps = [[[[itemDecode componentsSeparatedByString:@"FPS="] lastObject] componentsSeparatedByString:@","] firstObject];
+ }
+ int width = frame.frameDesc.width;
+ int height = frame.frameDesc.height;
+ for (int i = 0; i < _liveVideos.count;i++) {
+ TCLiveVideoElementView *videoView = _liveVideos[i];
+ ILiveRenderView *renderView = [self getLiveRenderViewOnElementView:videoView];
+ if ([renderView.identifier isEqualToString:userId]) {
+ if ([userId isEqualToString:[TCLiveRequestManager getInstance].userID]) {
+ videoView.videoInfoLable.text = [NSString stringWithFormat:@"%dx%d",width,height];
+ }
+ else{
+ videoView.videoInfoLable.text = [NSString stringWithFormat:@"%dx%d fps:%d",width,height,[fps intValue]/10];
+ }
+ videoView.userIdLabel.text = [NSString stringWithFormat:@"%@",userId];
+
+ }
+ }
+}
+
+- (void)closeLogView{
+ [_logView removeFromSuperview];
+ _logView = nil;
+ for (int i = 0; i < _liveVideos.count;i++) {
+ TCLiveVideoElementView *videoView = _liveVideos[i];
+ [videoView.userIdLabel removeFromSuperview];
+ videoView.userIdLabel = nil;
+ [videoView.videoInfoLable removeFromSuperview];
+ videoView.videoInfoLable = nil;
+ }
+ self.isShowLogInfo = NO;
+}
+@end
+
+@interface TCLiveVideoElementView()
+@property(nonatomic,strong) UIPanGestureRecognizer *panGesture;
+@end
+
+@implementation TCLiveVideoElementView
+
+-(instancetype)initWithFrame:(CGRect)frame{
+ if (self = [super initWithFrame:frame]) {
+ [self ennableDraggable:YES];
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestuer:)];
+ [self addGestureRecognizer:tap];
+ }
+ return self;
+}
+- (UILabel *)userIdLabel{
+ if (!_userIdLabel) {
+ _userIdLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 12, self.bounds.size.width, 8)];
+ _userIdLabel.textColor = [UIColor colorWithRGBHex:0xFF4081];
+ _userIdLabel.textAlignment = NSTextAlignmentLeft;
+ _userIdLabel.font = [UIFont systemFontOfSize:9];
+ [self addSubview:_userIdLabel];
+ }
+ return _userIdLabel;
+}
+-(UILabel *)videoInfoLable{
+ if (!_videoInfoLable) {
+ _videoInfoLable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0 , self.bounds.size.width, 12)];
+ _videoInfoLable.textColor = [UIColor colorWithRGBHex:0xFF4081];
+ _videoInfoLable.textAlignment = NSTextAlignmentLeft;
+ _videoInfoLable.font = [UIFont systemFontOfSize:13];
+ [self addSubview:_videoInfoLable];
+ }
+ return _videoInfoLable;
+}
+- (void)layoutSubviews{
+ if (self.bounds.size.width == [UIScreen mainScreen].bounds.size.width) {
+ _videoInfoLable.frame = CGRectMake(0, 84 , self.bounds.size.width, 12);
+ _userIdLabel.frame = CGRectMake(0, 12 + 84, self.bounds.size.width, 8);
+ }
+ else{
+ _videoInfoLable.frame = CGRectMake(0, 0 , self.bounds.size.width, 12);
+ _userIdLabel.frame = CGRectMake(0, 12, self.bounds.size.width, 8);
+ }
+}
+//可拖动能力
+-(void)ennableDraggable:(BOOL)draggable {
+ [self setUserInteractionEnabled:YES];
+ [self removeConstraints:self.constraints];
+ for (NSLayoutConstraint *constraint in self.superview.constraints) {
+ if ([constraint.firstItem isEqual:self]) {
+ [self.superview removeConstraint:constraint];
+ }
+ }
+ [self setTranslatesAutoresizingMaskIntoConstraints:YES];
+ if (draggable) {
+ if (!_panGesture) {
+ _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
+ _panGesture.delegate = self;
+ [self addGestureRecognizer:_panGesture];
+ }
+ }else{
+ if (_panGesture) {
+ _panGesture = nil;
+ [self removeGestureRecognizer:_panGesture];
+ }
+ }
+}
+- (void)tapGestuer:(UITapGestureRecognizer *)gesture{
+ if ([_delegate respondsToSelector:@selector(tapHandle:)]) {
+ [_delegate tapHandle:self];
+ }
+}
+- (void)pan:(UIPanGestureRecognizer *)panGestureRecognizer {
+ switch (panGestureRecognizer.state) {
+ case UIGestureRecognizerStateBegan: {
+ [self dragging:panGestureRecognizer];
+ }
+ break;
+ case UIGestureRecognizerStateChanged: {
+ [self dragging:panGestureRecognizer];
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+-(void)dragging:(UIPanGestureRecognizer *)panGestureRecognizer {
+ UIView *view = panGestureRecognizer.view;
+ CGPoint translation = [panGestureRecognizer translationInView:view.superview];
+ CGPoint center = CGPointMake(view.center.x + translation.x, view.center.y + translation.y);
+ //不能拖过边界
+ CGSize size = view.frame.size;
+ CGSize superSize = view.superview.frame.size;
+ CGFloat width = size.width;
+ CGFloat height = size.height;
+ CGFloat superWidth = superSize.width;
+ CGFloat superHeight = superSize.height;
+ center.x = (center.xsuperWidth)?superWidth-width/2:center.x;
+ center.y = (center.ysuperHeight)?superHeight-height/2:center.y;
+
+ [view setCenter:center];
+ [panGestureRecognizer setTranslation:CGPointZero inView:view.superview];
+}
+@end
+
diff --git a/src/ios/TrtcConfig.plist b/src/ios/TrtcConfig.plist
new file mode 100644
index 0000000..97666d3
--- /dev/null
+++ b/src/ios/TrtcConfig.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ AppId
+
+ IsDebug
+
+
+
diff --git a/src/ios/TrtcPlugin.h b/src/ios/TrtcPlugin.h
new file mode 100644
index 0000000..1da8479
--- /dev/null
+++ b/src/ios/TrtcPlugin.h
@@ -0,0 +1,13 @@
+//
+// TrtcPlugin.h
+//
+// Created by 布丁丸子酱 on 2018/12/26.
+//
+
+#import
+
+@interface TrtcPlugin : CDVPlugin
+
+- (void) showCreatePage:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/src/ios/TrtcPlugin.m b/src/ios/TrtcPlugin.m
new file mode 100644
index 0000000..0bfa416
--- /dev/null
+++ b/src/ios/TrtcPlugin.m
@@ -0,0 +1,49 @@
+//
+// TrtcPlugin.m
+//
+// Created by 布丁丸子酱 on 2018/12/26.
+//
+
+#import "TrtcPlugin.h"
+#import "TCLiveRequestManager.h"
+#import
+#import "TCLiveJoinRoomViewController.h"
+#import
+#import "UIToastView.h"
+#import
+
+@interface TrtcPlugin()
+{}
+@end
+
+@implementation TrtcPlugin
+
+- (void) showCreatePage:(CDVInvokedUrlCommand*)command {
+ NSLog(@"showCreatePage");
+ [[TCLiveRequestManager getInstance] requestLoginInfo:^(int code) {
+ if (code == 0) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ int retCode = [[ILiveSDK getInstance] initSdk:[TCLiveRequestManager getInstance].sdkAppID accountType:[TCLiveRequestManager getInstance].accountType];
+ NSLog(@"initSdk success %d",retCode);
+ if (retCode == 0) {
+ NSLog(@"userId & sig:");
+ NSLog(@"%@", [TCLiveRequestManager getInstance].userID);
+ NSLog(@"%@", [TCLiveRequestManager getInstance].userSig);
+ [[ILiveLoginManager getInstance] iLiveLogin:[TCLiveRequestManager getInstance].userID sig:[TCLiveRequestManager getInstance].userSig succ:^{
+ NSLog(@"-----> login succ");
+ [[UIToastView getInstance] showToastWithMessage:@"登录成功" toastMode:UIToastShowMode_Succ];
+ } failed:^(NSString *module, int errId, NSString *errMsg) {
+ NSLog(@"-----> login fail,%@ %d %@",module, errId, errMsg);
+ [[UIToastView getInstance] showToastWithMessage:@"登录失败" toastMode:UIToastShowMode_fail];
+ }];
+ }
+ });
+ }
+ }];
+ TCLiveJoinRoomViewController *vc = [TCLiveJoinRoomViewController new];
+// vc.defaultRoomId = self.defaultRoomId;
+ UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
+ [self.viewController presentViewController:nav animated:YES completion:nil];
+}
+
+@end
diff --git a/src/ios/libs/.gitkeep b/src/ios/libs/.gitkeep
new file mode 100644
index 0000000..f2b04b1
--- /dev/null
+++ b/src/ios/libs/.gitkeep
@@ -0,0 +1,4 @@
+# AVSDK
+# BeautySDK
+# ILiveSDK
+# IMSDK
diff --git a/src/ios/res/beauty-dis.png b/src/ios/res/beauty-dis.png
new file mode 100644
index 0000000..3f709c7
Binary files /dev/null and b/src/ios/res/beauty-dis.png differ
diff --git a/src/ios/res/beauty.png b/src/ios/res/beauty.png
new file mode 100644
index 0000000..b38bff1
Binary files /dev/null and b/src/ios/res/beauty.png differ
diff --git a/src/ios/res/bg.png b/src/ios/res/bg.png
new file mode 100644
index 0000000..26fc385
Binary files /dev/null and b/src/ios/res/bg.png differ
diff --git a/src/ios/res/camera-gray.png b/src/ios/res/camera-gray.png
new file mode 100644
index 0000000..5a0086b
Binary files /dev/null and b/src/ios/res/camera-gray.png differ
diff --git a/src/ios/res/camera.png b/src/ios/res/camera.png
new file mode 100644
index 0000000..424a2ee
Binary files /dev/null and b/src/ios/res/camera.png differ
diff --git a/src/ios/res/chat.png b/src/ios/res/chat.png
new file mode 100644
index 0000000..426ce06
Binary files /dev/null and b/src/ios/res/chat.png differ
diff --git a/src/ios/res/doubleroom.png b/src/ios/res/doubleroom.png
new file mode 100644
index 0000000..3ab3329
Binary files /dev/null and b/src/ios/res/doubleroom.png differ
diff --git a/src/ios/res/feedback.png b/src/ios/res/feedback.png
new file mode 100644
index 0000000..8c52b22
Binary files /dev/null and b/src/ios/res/feedback.png differ
diff --git a/src/ios/res/ic_toast_success@2x.png b/src/ios/res/ic_toast_success@2x.png
new file mode 100644
index 0000000..e46d177
Binary files /dev/null and b/src/ios/res/ic_toast_success@2x.png differ
diff --git a/src/ios/res/icon_sign@2x.png b/src/ios/res/icon_sign@2x.png
new file mode 100644
index 0000000..0f8f7ef
Binary files /dev/null and b/src/ios/res/icon_sign@2x.png differ
diff --git a/src/ios/res/log.png b/src/ios/res/log.png
new file mode 100644
index 0000000..9435fa3
Binary files /dev/null and b/src/ios/res/log.png differ
diff --git a/src/ios/res/log2.png b/src/ios/res/log2.png
new file mode 100644
index 0000000..012a5c7
Binary files /dev/null and b/src/ios/res/log2.png differ
diff --git a/src/ios/res/mic-dis.png b/src/ios/res/mic-dis.png
new file mode 100644
index 0000000..d948549
Binary files /dev/null and b/src/ios/res/mic-dis.png differ
diff --git a/src/ios/res/mic.png b/src/ios/res/mic.png
new file mode 100644
index 0000000..7390460
Binary files /dev/null and b/src/ios/res/mic.png differ
diff --git a/src/ios/res/muti_room_bg.png b/src/ios/res/muti_room_bg.png
new file mode 100644
index 0000000..90b77b9
Binary files /dev/null and b/src/ios/res/muti_room_bg.png differ
diff --git a/src/ios/res/role.png b/src/ios/res/role.png
new file mode 100644
index 0000000..f25c11b
Binary files /dev/null and b/src/ios/res/role.png differ
diff --git a/src/ios/res/ui_title_arrow_left.png b/src/ios/res/ui_title_arrow_left.png
new file mode 100644
index 0000000..065336e
Binary files /dev/null and b/src/ios/res/ui_title_arrow_left.png differ
diff --git a/www/trtc.js b/www/trtc.js
new file mode 100644
index 0000000..fd27ab1
--- /dev/null
+++ b/www/trtc.js
@@ -0,0 +1,13 @@
+var exec = require('cordova/exec');
+
+function Trtc() {}
+
+Trtc.prototype.joinChannel = function(arg, success, error) {
+ exec(success, error, 'Trtc', 'joinChannel', [arg]);
+};
+
+Trtc.prototype.showCreatePage = function(success, error) {
+ exec(success, error, 'Trtc', 'showCreatePage', []);
+}
+
+module.exports = new Trtc();