diff --git a/QNDroidRTCDemo/app/build.gradle b/QNDroidRTCDemo/app/build.gradle
index d61687a..9c2b93c 100644
--- a/QNDroidRTCDemo/app/build.gradle
+++ b/QNDroidRTCDemo/app/build.gradle
@@ -1,16 +1,16 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 25
- buildToolsVersion '26.0.2'
+ compileSdkVersion 28
defaultConfig {
applicationId "com.qiniu.droid.rtc.demo"
minSdkVersion 18
- targetSdkVersion 25
- versionCode 9
- versionName "1.2.0"
+ targetSdkVersion 28
+ versionCode 12
+ versionName "2.0.0"
buildConfigField "long", "BUILD_TIMESTAMP", System.currentTimeMillis() + "L"
}
+
buildTypes {
release {
minifyEnabled false
@@ -18,18 +18,25 @@ android {
}
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ lintOptions {
+ disable 'GoogleAppIndexingWarning'
+ abortOnError false
}
}
dependencies {
- compile files('libs/qndroid-rtc-1.2.0.jar')
- compile 'com.android.support:appcompat-v7:25+'
- compile 'com.squareup.okhttp3:okhttp:3.9.1'
- compile 'com.bugsnag:bugsnag-android-ndk:1.+'
- compile 'de.greenrobot:eventbus:2.4.0'
- compile 'com.android.support:recyclerview-v7:25.4.0'
- implementation files('libs/pldroid-player-2.1.4.jar')
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.squareup.okhttp3:okhttp:3.9.1'
+ implementation 'com.bugsnag:bugsnag-android-ndk:1.+'
+ implementation 'de.greenrobot:eventbus:2.4.0'
+ implementation 'com.android.support:recyclerview-v7:28.0.0'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+
+ // QNDroidRTCLibrary
+ if (buildWithQNDroidRTCLibrary) {
+ implementation project(':library')
+ implementation files('libs/pldroid-player-2.1.4.jar')
+ } else {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ }
}
diff --git a/QNDroidRTCDemo/app/libs/qndroid-rtc-1.2.0.jar b/QNDroidRTCDemo/app/libs/qndroid-rtc-1.2.0.jar
deleted file mode 100644
index 005b579..0000000
Binary files a/QNDroidRTCDemo/app/libs/qndroid-rtc-1.2.0.jar and /dev/null differ
diff --git a/QNDroidRTCDemo/app/libs/qndroid-rtc-2.0.0.jar b/QNDroidRTCDemo/app/libs/qndroid-rtc-2.0.0.jar
new file mode 100644
index 0000000..7bddf83
Binary files /dev/null and b/QNDroidRTCDemo/app/libs/qndroid-rtc-2.0.0.jar differ
diff --git a/QNDroidRTCDemo/app/src/main/AndroidManifest.xml b/QNDroidRTCDemo/app/src/main/AndroidManifest.xml
index 6c98aa0..ecc6ebc 100644
--- a/QNDroidRTCDemo/app/src/main/AndroidManifest.xml
+++ b/QNDroidRTCDemo/app/src/main/AndroidManifest.xml
@@ -34,18 +34,15 @@
+
-
+
+
@@ -64,14 +61,17 @@
android:resource="@xml/update_apk_paths" />
-
+ android:screenOrientation="portrait" />
-
+ android:launchMode="singleTop"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme" />
-
\ No newline at end of file
+
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/RTCApplication.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/RTCApplication.java
index b254279..2d8d3dc 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/RTCApplication.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/RTCApplication.java
@@ -16,5 +16,11 @@ public void onCreate() {
*/
QNRTCEnv.init(getApplicationContext());
QNRTCEnv.setLogFileEnabled(true);
+ /**
+ * 正式版本需要去掉!!!!
+ * 正式版本需要去掉!!!!
+ * 正式版本需要去掉!!!!
+ */
+ QNRTCEnv.setLogFileMaxCount(10);
}
}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/LiveRoomActivity.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/LiveRoomActivity.java
index 0acbd86..c35cb8a 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/LiveRoomActivity.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/LiveRoomActivity.java
@@ -337,6 +337,10 @@ public void run() {
}
+ @Override
+ public void onCameraCaptureReady() {
+ }
+
@Override
public void onJoinedRoom() {
mIsJoinedRoom = true;
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/MainActivity.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/MainActivity.java
index aa0001a..49ba0af 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/MainActivity.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/MainActivity.java
@@ -25,6 +25,7 @@
import com.qiniu.droid.rtc.demo.model.UpdateInfo;
import com.qiniu.droid.rtc.demo.model.UserList;
import com.qiniu.droid.rtc.demo.service.DownloadService;
+import com.qiniu.droid.rtc.demo.ui.RadioGroupFlow;
import com.qiniu.droid.rtc.demo.utils.Config;
import com.qiniu.droid.rtc.demo.utils.QNAppServer;
import com.qiniu.droid.rtc.demo.utils.ToastUtils;
@@ -39,10 +40,11 @@ public class MainActivity extends AppCompatActivity {
private EditText mRoomEditText;
private ProgressDialog mProgressDialog;
- private RadioGroup mCaptureModeRadioGroup;
+ private RadioGroupFlow mCaptureModeRadioGroup;
private RadioButton mScreenCapture;
private RadioButton mCameraCapture;
private RadioButton mOnlyAudioCapture;
+ private RadioButton mMutiTrackCapture;
private String mUserName;
private String mRoomName;
@@ -116,7 +118,7 @@ public void onClickConference(final View v) {
handleRoomInfo();
SharedPreferences preferences = getSharedPreferences(getString(R.string.app_name), Context.MODE_PRIVATE);
mUserName = preferences.getString(Config.USER_NAME, "");
- mIsScreenCaptureEnabled = (mCaptureMode == Config.SCREEN_CAPTURE);
+ mIsScreenCaptureEnabled = (mCaptureMode == Config.SCREEN_CAPTURE || mCaptureMode == Config.MUTI_TRACK_CAPTURE);
if (mIsScreenCaptureEnabled) {
QNScreenCaptureUtil.requestScreenCapture(this);
} else {
@@ -145,7 +147,7 @@ public void run() {
ToastUtils.s(MainActivity.this, getString(R.string.null_room_token_toast));
return;
}
- Intent intent = new Intent(MainActivity.this, mIsScreenCaptureEnabled ? ScreenCaptureActivity.class : RoomActivity.class);
+ Intent intent = new Intent(MainActivity.this, RoomActivity.class);
intent.putExtra(RoomActivity.EXTRA_ROOM_ID, roomName.trim());
intent.putExtra(RoomActivity.EXTRA_ROOM_TOKEN, token);
intent.putExtra(RoomActivity.EXTRA_USER_ID, mUserName);
@@ -221,11 +223,12 @@ private boolean handleRoomInfo() {
private void initView() {
setContentView(R.layout.activity_main);
mRoomEditText = (EditText) findViewById(R.id.room_edit_text);
- mCaptureModeRadioGroup = (RadioGroup) findViewById(R.id.capture_mode_button);
+ mCaptureModeRadioGroup = findViewById(R.id.capture_mode_button);
mCaptureModeRadioGroup.setOnCheckedChangeListener(mOnCheckedChangeListener);
mScreenCapture = (RadioButton) findViewById(R.id.screen_capture_button);
mCameraCapture = (RadioButton) findViewById(R.id.camera_capture_button);
mOnlyAudioCapture = (RadioButton) findViewById(R.id.audio_capture_button);
+ mMutiTrackCapture = findViewById(R.id.muti_track_button);
SharedPreferences preferences = getSharedPreferences(getString(R.string.app_name), Context.MODE_PRIVATE);
String roomName = preferences.getString(Config.ROOM_NAME, Config.PILI_ROOM);
@@ -235,8 +238,10 @@ private void initView() {
mScreenCapture.setChecked(true);
} else if (captureMode == Config.CAMERA_CAPTURE) {
mCameraCapture.setChecked(true);
- } else {
+ } else if (captureMode == Config.ONLY_AUDIO_CAPTURE){
mOnlyAudioCapture.setChecked(true);
+ } else {
+ mMutiTrackCapture.setChecked(true);
}
} else {
mScreenCapture.setEnabled(false);
@@ -313,6 +318,9 @@ public void onCheckedChanged(RadioGroup group, int checkedId) {
case R.id.audio_capture_button:
mCaptureMode = Config.ONLY_AUDIO_CAPTURE;
break;
+ case R.id.muti_track_button:
+ mCaptureMode = Config.MUTI_TRACK_CAPTURE;
+ break;
}
}
};
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/RoomActivity.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/RoomActivity.java
index 1e9b8ea..4ada495 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/RoomActivity.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/RoomActivity.java
@@ -11,59 +11,51 @@
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
-import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
import android.widget.Toast;
import com.qiniu.droid.rtc.QNBeautySetting;
import com.qiniu.droid.rtc.QNCameraSwitchResultCallback;
-import com.qiniu.droid.rtc.QNRTCManager;
+import com.qiniu.droid.rtc.QNErrorCode;
+import com.qiniu.droid.rtc.QNRTCEngine;
+import com.qiniu.droid.rtc.QNRTCEngineEventListener;
import com.qiniu.droid.rtc.QNRTCSetting;
-import com.qiniu.droid.rtc.QNRemoteAudioCallback;
-import com.qiniu.droid.rtc.QNRemoteSurfaceView;
-import com.qiniu.droid.rtc.QNRoomEventListener;
import com.qiniu.droid.rtc.QNRoomState;
+import com.qiniu.droid.rtc.QNSourceType;
import com.qiniu.droid.rtc.QNStatisticsReport;
+import com.qiniu.droid.rtc.QNTrackInfo;
+import com.qiniu.droid.rtc.QNTrackKind;
import com.qiniu.droid.rtc.QNVideoFormat;
import com.qiniu.droid.rtc.demo.R;
import com.qiniu.droid.rtc.demo.fragment.ControlFragment;
-import com.qiniu.droid.rtc.demo.ui.LocalVideoView;
-import com.qiniu.droid.rtc.demo.ui.RTCVideoView;
+import com.qiniu.droid.rtc.demo.ui.UserTrackView;
import com.qiniu.droid.rtc.demo.utils.Config;
import com.qiniu.droid.rtc.demo.utils.QNAppServer;
import com.qiniu.droid.rtc.demo.utils.ToastUtils;
+import com.qiniu.droid.rtc.demo.utils.TrackWindowMgr;
import com.qiniu.droid.rtc.model.QNAudioDevice;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import static com.qiniu.droid.rtc.QNErrorCode.ERROR_KICKED_OUT_OF_ROOM;
import static com.qiniu.droid.rtc.demo.utils.Config.DEFAULT_BITRATE;
import static com.qiniu.droid.rtc.demo.utils.Config.DEFAULT_FPS;
import static com.qiniu.droid.rtc.demo.utils.Config.DEFAULT_RESOLUTION;
-public class RoomActivity extends Activity implements QNRoomEventListener, ControlFragment.OnCallEvents {
-
+public class RoomActivity extends Activity implements QNRTCEngineEventListener, ControlFragment.OnCallEvents {
private static final String TAG = "RoomActivity";
+ private static final int BITRATE_FOR_SCREEN_VIDEO = (int) (1.5 * 1000 * 1000);
- public static final String EXTRA_ROOM_ID = "ROOM_ID";
- public static final String EXTRA_ROOM_TOKEN = "ROOM_TOKEN";
public static final String EXTRA_USER_ID = "USER_ID";
- public static final String EXTRA_VIDEO_WIDTH = "VIDEO_WIDTH";
- public static final String EXTRA_VIDEO_HEIGHT = "VIDEO_HEIGHT";
- public static final String EXTRA_HW_CODEC = "HW_CODEC";
+ public static final String EXTRA_ROOM_TOKEN = "ROOM_TOKEN";
+ public static final String EXTRA_ROOM_ID = "ROOM_ID";
private static final String[] MANDATORY_PERMISSIONS = {
"android.permission.MODIFY_AUDIO_SETTINGS",
@@ -71,112 +63,93 @@ public class RoomActivity extends Activity implements QNRoomEventListener, Contr
"android.permission.INTERNET"
};
+ private Toast mLogToast;
private List mHWBlackList = new ArrayList<>();
- private List mUsedWindowList;
- private List mUnusedWindowList;
- private ConcurrentHashMap mUserWindowMap;
- private String[] mMergeStreamPosition;
-
- private RTCVideoView mRemoteWindowA;
- private RTCVideoView mRemoteWindowB;
- private RTCVideoView mRemoteWindowC;
- private RTCVideoView mRemoteWindowD;
- private RTCVideoView mRemoteWindowE;
- private RTCVideoView mRemoteWindowF;
- private RTCVideoView mRemoteWindowG;
- private RTCVideoView mRemoteWindowH;
- private RTCVideoView mLocalWindow;
- private Toast mLogToast;
- private QNRTCManager mRTCManager;
+ private UserTrackView mTrackWindowFullScreen;
+ private List mTrackWindowsList;
+ private AlertDialog mKickOutDialog;
- private boolean mIsError;
- private boolean mCallControlFragmentVisible = true;
- private long mCallStartedTimeMs = 0;
+ private QNRTCEngine mEngine;
+ private String mRoomToken;
+ private String mUserId;
+ private String mRoomId;
private boolean mMicEnabled = true;
private boolean mBeautyEnabled = false;
private boolean mVideoEnabled = true;
private boolean mSpeakerEnabled = true;
+ private boolean mIsError = false;
+ private boolean mIsAdmin = false;
private boolean mIsJoinedRoom = false;
- private String mRoomId;
- private String mRoomToken;
- private String mUserId;
- private String mLocalLogText;
private ControlFragment mControlFragment;
+ private List mLocalTrackList;
+
+ private QNTrackInfo mLocalVideoTrack;
+ private QNTrackInfo mLocalAudioTrack;
+ private QNTrackInfo mLocalScreenTrack;
private int mScreenWidth = 0;
private int mScreenHeight = 0;
- private int mVideoWidth = 0;
- private int mVideoHeight = 0;
- private float mDensity = 0;
- private boolean mIsAdmin = false;
+ private int mCaptureMode = Config.CAMERA_CAPTURE;
- private AlertDialog mKickoutDialog;
+ private TrackWindowMgr mTrackWindowMgr;
@Override
- public void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().addFlags(LayoutParams.FLAG_FULLSCREEN | LayoutParams.FLAG_KEEP_SCREEN_ON
- | LayoutParams.FLAG_DISMISS_KEYGUARD | LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | LayoutParams.FLAG_TURN_SCREEN_ON);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility());
- WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ final WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getRealMetrics(outMetrics);
-
mScreenWidth = outMetrics.widthPixels;
mScreenHeight = outMetrics.heightPixels;
- mDensity = outMetrics.density;
- setContentView(R.layout.activity_room);
+ setContentView(R.layout.activity_muti_track_room);
Intent intent = getIntent();
- mRoomId = intent.getStringExtra(EXTRA_ROOM_ID);
mRoomToken = intent.getStringExtra(EXTRA_ROOM_TOKEN);
mUserId = intent.getStringExtra(EXTRA_USER_ID);
-
- mLocalWindow = (LocalVideoView) findViewById(R.id.local_video_view);
- mLocalWindow.setUserId(mUserId);
-
- mRemoteWindowA = (RTCVideoView) findViewById(R.id.remote_video_view_a);
- mRemoteWindowB = (RTCVideoView) findViewById(R.id.remote_video_view_b);
- mRemoteWindowC = (RTCVideoView) findViewById(R.id.remote_video_view_c);
- mRemoteWindowD = (RTCVideoView) findViewById(R.id.remote_video_view_d);
- mRemoteWindowE = (RTCVideoView) findViewById(R.id.remote_video_view_e);
- mRemoteWindowF = (RTCVideoView) findViewById(R.id.remote_video_view_f);
- mRemoteWindowG = (RTCVideoView) findViewById(R.id.remote_video_view_g);
- mRemoteWindowH = (RTCVideoView) findViewById(R.id.remote_video_view_h);
-
- mUsedWindowList = Collections.synchronizedList(new LinkedList());
- mUsedWindowList.add(mLocalWindow);
- mUnusedWindowList = Collections.synchronizedList(new LinkedList());
- mUnusedWindowList.add(mRemoteWindowA);
- mUnusedWindowList.add(mRemoteWindowB);
- mUnusedWindowList.add(mRemoteWindowC);
- mUnusedWindowList.add(mRemoteWindowD);
- mUnusedWindowList.add(mRemoteWindowE);
- mUnusedWindowList.add(mRemoteWindowF);
- mUnusedWindowList.add(mRemoteWindowG);
- mUnusedWindowList.add(mRemoteWindowH);
-
- // every remote window can switch with local window
- for (final RTCVideoView rtcVideoView : mUnusedWindowList) {
- rtcVideoView.setOnLongClickListener(mOnLongClickListener);
- rtcVideoView.setOnClickListener(new View.OnClickListener() {
+ mRoomId = intent.getStringExtra(EXTRA_ROOM_ID);
+ mIsAdmin = mUserId.equals(QNAppServer.ADMIN_USER);
+
+ mTrackWindowFullScreen = (UserTrackView) findViewById(R.id.track_window_full_screen);
+ mTrackWindowsList = new LinkedList(Arrays.asList(
+ (UserTrackView) findViewById(R.id.track_window_a),
+ (UserTrackView) findViewById(R.id.track_window_b),
+ (UserTrackView) findViewById(R.id.track_window_c),
+ (UserTrackView) findViewById(R.id.track_window_d),
+ (UserTrackView) findViewById(R.id.track_window_e),
+ (UserTrackView) findViewById(R.id.track_window_f),
+ (UserTrackView) findViewById(R.id.track_window_g),
+ (UserTrackView) findViewById(R.id.track_window_h),
+ (UserTrackView) findViewById(R.id.track_window_i)
+ ));
+
+ for (final UserTrackView view : mTrackWindowsList) {
+ view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
- public void onClick(View v) {
- mRTCManager.switchWindow(rtcVideoView.getRemoteSurfaceView());
+ public boolean onLongClick(View v) {
+ if (mIsAdmin) {
+ showKickoutDialog(view.getUserId());
+ }
+ return false;
}
});
}
- mUserWindowMap = new ConcurrentHashMap<>();
-
+ // init Control fragment
mControlFragment = new ControlFragment();
+ mControlFragment.setArguments(intent.getExtras());
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.add(R.id.control_fragment_container, mControlFragment);
+ ft.commitAllowingStateLoss();
+ // permission check
for (String permission : MANDATORY_PERMISSIONS) {
if (checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
logAndToast("Permission " + permission + " is not granted");
@@ -186,18 +159,28 @@ public void onClick(View v) {
}
}
+ // init rtcEngine and local track info list.
+ initQNRTCEngine();
+ initLocalTrackInfoList();
+
+ // init decorate and set default to p2p mode
+ mTrackWindowMgr = new TrackWindowMgr(mUserId, mScreenWidth, mScreenHeight, outMetrics.density
+ , mEngine, mTrackWindowFullScreen, mTrackWindowsList);
+
+ List localTrackListExcludeScreenTrack = new ArrayList<>(mLocalTrackList);
+ localTrackListExcludeScreenTrack.remove(mLocalScreenTrack);
+ mTrackWindowMgr.addTrackInfo(mUserId, localTrackListExcludeScreenTrack);
+ }
+
+ private void initQNRTCEngine() {
SharedPreferences preferences = getSharedPreferences(getString(R.string.app_name), Context.MODE_PRIVATE);
- mVideoWidth = preferences.getInt(Config.WIDTH, DEFAULT_RESOLUTION[1][0]);
- mVideoHeight = preferences.getInt(Config.HEIGHT, DEFAULT_RESOLUTION[1][1]);
+ int videoWidth = preferences.getInt(Config.WIDTH, DEFAULT_RESOLUTION[1][0]);
+ int videoHeight = preferences.getInt(Config.HEIGHT, DEFAULT_RESOLUTION[1][1]);
int fps = preferences.getInt(Config.FPS, DEFAULT_FPS[1]);
- boolean isHwCodec = preferences.getInt(Config.CODEC_MODE, Config.SW) == Config.HW;
- boolean isScreenCaptureEnabled = preferences.getInt(Config.CAPTURE_MODE, Config.CAMERA_CAPTURE) == Config.SCREEN_CAPTURE;
- boolean isAudioOnly = preferences.getInt(Config.CAPTURE_MODE, Config.CAMERA_CAPTURE) == Config.ONLY_AUDIO_CAPTURE;
- boolean isVideoEnable = !isAudioOnly;
-
- if (isScreenCaptureEnabled || isAudioOnly) {
- mLocalWindow.setAudioViewVisible(0);
- }
+ boolean isHwCodec = preferences.getInt(Config.CODEC_MODE, Config.HW) == Config.HW;
+ int videoBitrate = preferences.getInt(Config.BITRATE, DEFAULT_BITRATE[1]);
+ boolean isMaintainRes = preferences.getBoolean(Config.MAINTAIN_RES, false);
+ mCaptureMode = preferences.getInt(Config.CAPTURE_MODE, Config.CAMERA_CAPTURE);
// get the items in hw black list, and set isHwCodec false forcibly
String[] hwBlackList = getResources().getStringArray(R.array.hw_black_list);
@@ -206,149 +189,100 @@ public void onClick(View v) {
isHwCodec = false;
}
+ QNVideoFormat format = new QNVideoFormat(videoWidth, videoHeight, fps);
QNRTCSetting setting = new QNRTCSetting();
- setting.setVideoEnabled(isVideoEnable)
- .setCameraID(QNRTCSetting.CAMERA_FACING_ID.FRONT)
+ setting.setCameraID(QNRTCSetting.CAMERA_FACING_ID.FRONT)
.setHWCodecEnabled(isHwCodec)
- .setScreenCaptureEnabled(isScreenCaptureEnabled)
- .setVideoPreviewFormat(new QNVideoFormat(mVideoWidth, mVideoHeight, fps))
- .setVideoEncodeFormat(new QNVideoFormat(mVideoWidth, mVideoHeight, fps));
-
- int audioBitrate = 64 * 1000;
- int videoBitrate = preferences.getInt(Config.BITRATE, DEFAULT_BITRATE[1]);
- //设置音频初始码率
- setting.setAudioBitrate(audioBitrate);
- //设置视频初始码率
- setting.setVideoBitrate(videoBitrate);
- //当设置的最低码率,远高于弱网下的常规传输码率值时,会严重影响连麦的画面流畅度
- //音频码率上限内部已有默认值,此处只传递视频码率上限即可
- setting.setBitrateRange(0, videoBitrate);
-
- mControlFragment.setArguments(intent.getExtras());
- mControlFragment.setScreenCaptureEnabled(isScreenCaptureEnabled);
- mControlFragment.setAudioOnly(isAudioOnly);
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.add(R.id.control_fragment_container, mControlFragment);
- ft.commitAllowingStateLoss();
-
- mRTCManager = new QNRTCManager();
- mRTCManager.setRoomEventListener(this);
- mRTCManager.addRemoteWindow(mRemoteWindowA.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowB.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowC.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowD.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowE.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowF.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowG.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowH.getRemoteSurfaceView());
- mRTCManager.initialize(this, setting);
- mRTCManager.setLocalWindow(mLocalWindow.getLocalSurfaceView());
- }
-
- public void onClickScreen(View v) {
- if (mUsedWindowList.size() < 3) {
- toggleControlFragmentVisibility();
- }
- }
-
- private void toggleControlFragmentVisibility() {
- if (!mControlFragment.isAdded()) {
- return;
- }
-
- mCallControlFragmentVisible = !mCallControlFragmentVisible;
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- if (mCallControlFragmentVisible) {
- ft.show(mControlFragment);
- } else {
- ft.hide(mControlFragment);
+ .setMaintainResolution(isMaintainRes)
+ .setVideoBitrate(videoBitrate)
+ .setVideoEncodeFormat(format)
+ .setVideoPreviewFormat(format);
+ mEngine = QNRTCEngine.createEngine(getApplicationContext(), setting, this);
+ }
+
+ private void initLocalTrackInfoList() {
+ mLocalTrackList = new ArrayList<>();
+ mLocalAudioTrack = mEngine.createTrackInfoBuilder()
+ .setSourceType(QNSourceType.AUDIO)
+ .setMaster(true)
+ .create();
+ mLocalTrackList.add(mLocalAudioTrack);
+
+ QNVideoFormat screenEncodeFormat = new QNVideoFormat(mScreenWidth/2, mScreenHeight/2, 15);
+ switch (mCaptureMode) {
+ case Config.CAMERA_CAPTURE:
+ mLocalVideoTrack = mEngine.createTrackInfoBuilder()
+ .setSourceType(QNSourceType.VIDEO_CAMERA)
+ .setMaster(true)
+ .setTag(UserTrackView.TAG_CAMERA).create();
+ mLocalTrackList.add(mLocalVideoTrack);
+ break;
+ case Config.ONLY_AUDIO_CAPTURE:
+ mControlFragment.setAudioOnly(true);
+ break;
+ case Config.SCREEN_CAPTURE:
+ mLocalScreenTrack = mEngine.createTrackInfoBuilder()
+ .setVideoPreviewFormat(screenEncodeFormat)
+ .setBitrate(BITRATE_FOR_SCREEN_VIDEO)
+ .setSourceType(QNSourceType.VIDEO_SCREEN)
+ .setMaster(true)
+ .setTag(UserTrackView.TAG_SCREEN).create();
+ mLocalTrackList.add(mLocalScreenTrack);
+ mControlFragment.setAudioOnly(true);
+ break;
+ case Config.MUTI_TRACK_CAPTURE:
+ mLocalScreenTrack = mEngine.createTrackInfoBuilder()
+ .setSourceType(QNSourceType.VIDEO_SCREEN)
+ .setVideoPreviewFormat(screenEncodeFormat)
+ .setBitrate(BITRATE_FOR_SCREEN_VIDEO)
+ .setMaster(true)
+ .setTag(UserTrackView.TAG_SCREEN).create();
+ mLocalVideoTrack = mEngine.createTrackInfoBuilder()
+ .setSourceType(QNSourceType.VIDEO_CAMERA)
+ .setTag(UserTrackView.TAG_CAMERA).create();
+ mLocalTrackList.add(mLocalScreenTrack);
+ mLocalTrackList.add(mLocalVideoTrack);
+ break;
}
- ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- ft.commitAllowingStateLoss();
}
- private void startCall() {
- if (mRTCManager == null || mIsJoinedRoom) {
- return;
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (!mIsJoinedRoom) {
+ mEngine.joinRoom(mRoomToken);
}
- mCallStartedTimeMs = System.currentTimeMillis();
-
- logAndToast(getString(R.string.connecting_to, mRoomId));
- mRTCManager.joinRoom(mRoomToken);
- mIsJoinedRoom = true;
}
- private void onConnectedInternal() {
- final long delay = System.currentTimeMillis() - mCallStartedTimeMs;
- Log.i(TAG, "Call connected: delay=" + delay + "ms");
- logAndToast(getString(R.string.connected_to_room));
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ finish();
}
- private void subscribeAllRemoteStreams() {
- ArrayList publishingUsers = mRTCManager.getPublishingUserList();
- if (publishingUsers != null && !publishingUsers.isEmpty()) {
- for (String userId : publishingUsers) {
- mRTCManager.subscribe(userId);
- mRTCManager.addRemoteAudioCallback(userId, new QNRemoteAudioCallback() {
- @Override
- public void onRemoteAudioAvailable(String userId, ByteBuffer audioData, int size, int bitsPerSample, int sampleRate, int numberOfChannels) {
- }
- });
- }
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mEngine != null) {
+ mEngine.destroy();
+ mEngine = null;
}
- }
-
- private void clearAllRemoteStreams() {
- if (mUsedWindowList.size() > 2) {
- setTargetWindowParams(1, 0, mLocalWindow);
+ if (mTrackWindowFullScreen != null) {
+ mTrackWindowFullScreen.dispose();
}
- mUsedWindowList.clear();
- mUsedWindowList.add(mLocalWindow);
-
- for (RTCVideoView rtcVideoView : mUserWindowMap.values()) {
- rtcVideoView.setVisible(false);
+ for (UserTrackView item : mTrackWindowsList) {
+ item.dispose();
}
- mUserWindowMap.clear();
-
- mUnusedWindowList.clear();
- mUnusedWindowList.add(mRemoteWindowA);
- mUnusedWindowList.add(mRemoteWindowB);
- mUnusedWindowList.add(mRemoteWindowC);
- mUnusedWindowList.add(mRemoteWindowD);
- mUnusedWindowList.add(mRemoteWindowE);
- mUnusedWindowList.add(mRemoteWindowF);
- mUnusedWindowList.add(mRemoteWindowG);
- mUnusedWindowList.add(mRemoteWindowH);
+ mTrackWindowsList.clear();
}
- private void disconnect() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mControlFragment.stopTimer();
- }
- });
+ private void logAndToast(final String msg) {
+ Log.d(TAG, msg);
if (mLogToast != null) {
mLogToast.cancel();
}
- if (mRTCManager != null) {
- if (mIsAdmin) {
- mRTCManager.stopMergeStream();
- }
- mRTCManager.destroy();
- mRTCManager = null;
- }
- mLocalWindow = null;
- mRemoteWindowA = null;
- mRemoteWindowB = null;
- mRemoteWindowC = null;
- mRemoteWindowD = null;
- mRemoteWindowE = null;
- mRemoteWindowF = null;
- mRemoteWindowG = null;
- mRemoteWindowH = null;
-
- mIsJoinedRoom = false;
+ mLogToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
+ mLogToast.show();
}
private void disconnectWithErrorMessage(final String errorMessage) {
@@ -367,189 +301,34 @@ public void onClick(DialogInterface dialog, int id) {
.show();
}
- private void logAndToast(final String msg) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, msg);
- if (mLogToast != null) {
- mLogToast.cancel();
- }
- mLogToast = Toast.makeText(RoomActivity.this, msg, Toast.LENGTH_SHORT);
- mLogToast.show();
- }
- });
- }
-
private void reportError(final String description) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (!mIsError) {
- mIsError = true;
- disconnectWithErrorMessage(description);
- }
- }
- });
- }
-
- private RTCVideoView getWindowByUserId(String userId) {
- return mUserWindowMap.containsKey(userId) ? mUserWindowMap.get(userId) : null;
- }
-
- private void toggleToMultiUsersUI(final int userCount, List windowList) {
- for (int i = 0; i < userCount; i++) {
- setTargetWindowParams(userCount, i, windowList.get(i));
- }
- }
-
- public synchronized void setTargetWindowParams(final int userCount, final int targetPos, final RTCVideoView targetWindow) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- switch (userCount) {
- case 1:
- updateLayoutParams(targetWindow, 0, mScreenWidth, mScreenHeight, 0, 0, -1);
- case 2:
- if (targetPos == 0) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth, mScreenHeight, 0, 0, -1);
- } else if (targetPos == 1) {
- updateLayoutParams(targetWindow, targetPos, (int) (120 * mDensity + 0.5f), (int) (160 * mDensity + 0.5f), 0, 0, Gravity.TOP | Gravity.END);
- }
- break;
- case 3:
- if (targetPos == 0) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, 0, 0, -1);
- } else if (targetPos == 1) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, 0, -1);
- } else {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, 0, mScreenWidth / 2, Gravity.CENTER_HORIZONTAL);
- }
- break;
- case 4:
- if (targetPos == 0) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, 0, 0, -1);
- } else if (targetPos == 1) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, 0, -1);
- } else if (targetPos == 2) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, 0, mScreenWidth / 2, Gravity.START);
- } else {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, -1);
- }
- break;
- case 5:
- case 6:
- case 7:
- case 8:
- case 9:
- if (targetPos == 0) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, 0, 0, -1);
- } else if (targetPos == 1) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, 0, -1);
- } else if (targetPos == 2) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, 0, Gravity.END);
- } else if (targetPos == 3) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, 0, mScreenWidth / 3, -1);
- } else if (targetPos == 4) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, -1);
- } else if (targetPos == 5) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, mScreenWidth / 3, -1);
- } else if (targetPos == 6) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, 0, mScreenWidth * 2 / 3, -1);
- } else if (targetPos == 7) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, -1);
- } else if (targetPos == 8) {
- updateLayoutParams(targetWindow, targetPos, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, mScreenWidth * 2 / 3, -1);
- }
- break;
- }
- }
- });
- }
-
- private void updateLayoutParams(RTCVideoView targetView, int targetPos, int width, int height, int marginStart, int marginTop, int gravity) {
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) targetView.getLayoutParams();
- lp.width = width;
- lp.height = height;
- lp.topMargin = marginTop;
- lp.gravity = gravity;
- lp.setMarginStart(marginStart);
- targetView.setLayoutParams(lp);
- targetView.setMicrophoneStateVisibility(
- (width == mScreenWidth && height == mScreenHeight) ? View.INVISIBLE : View.VISIBLE);
- if (targetView.getAudioViewVisibility() == View.VISIBLE) {
- targetView.updateAudioView(targetPos);
- }
- }
-
- private void updateRemoteLogText(final String logText) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mControlFragment.updateRemoteLogText(logText);
- }
- });
- }
-
- private boolean isAdmin() {
- return mUserId.equals(QNAppServer.ADMIN_USER);
- }
-
- private synchronized void clearMergeStreamPos(String userId) {
- int pos = -1;
- if (mMergeStreamPosition != null && !TextUtils.isEmpty(userId)) {
- for (int i = 0; i < mMergeStreamPosition.length; i++) {
- if (userId.equals(mMergeStreamPosition[i])) {
- pos = i;
- break;
- }
- }
- }
- if (pos >= 0 && pos < mMergeStreamPosition.length) {
- mMergeStreamPosition[pos] = null;
- }
- }
-
- private int getMergeStreamIdlePos() {
- int pos = -1;
- for (int i = 0; i < mMergeStreamPosition.length; i++) {
- if (TextUtils.isEmpty(mMergeStreamPosition[i])) {
- pos = i;
- break;
- }
- }
- return pos;
- }
-
- private synchronized void setMergeRemoteStreamLayout(String userId) {
- if (mIsAdmin) {
- int pos = getMergeStreamIdlePos();
- if (pos == -1) {
- Log.e(TAG, "No idle position for merge streaming, so discard.");
- return;
- }
- int x = QNAppServer.MERGE_STREAM_POS[pos][0];
- int y = QNAppServer.MERGE_STREAM_POS[pos][1];
- mRTCManager.setMergeStreamLayout(userId, x, y, 1, QNAppServer.MERGE_STREAM_WIDTH, QNAppServer.MERGE_STREAM_HEIGHT);
- mMergeStreamPosition[pos] = userId;
+ // TODO: handle error.
+ if (!mIsError) {
+ mIsError = true;
+ disconnectWithErrorMessage(description);
}
}
private void showKickoutDialog(final String userId) {
- if (mKickoutDialog == null) {
- mKickoutDialog = new AlertDialog.Builder(this)
+ if (mKickOutDialog == null) {
+ mKickOutDialog = new AlertDialog.Builder(this)
.setNegativeButton(R.string.negative_dialog_tips, null)
.create();
}
- mKickoutDialog.setMessage(getString(R.string.kickout_tips, userId));
- mKickoutDialog.setButton(DialogInterface.BUTTON_POSITIVE, getResources().getString(R.string.positive_dialog_tips),
+ mKickOutDialog.setMessage(getString(R.string.kickout_tips, userId));
+ mKickOutDialog.setButton(DialogInterface.BUTTON_POSITIVE, getResources().getString(R.string.positive_dialog_tips),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- mRTCManager.kickOutUser(userId);
+ mEngine.kickOutUser(userId);
}
});
- mKickoutDialog.show();
+ mKickOutDialog.show();
+ }
+
+ private void updateRemoteLogText(final String logText) {
+ Log.i(TAG, logText);
+ mControlFragment.updateRemoteLogText(logText);
}
@TargetApi(19)
@@ -562,303 +341,197 @@ private static int getSystemUiVisibility() {
}
@Override
- protected void onResume() {
- super.onResume();
- startCall();
- }
-
- @Override
- public void onBackPressed() {
- disconnect();
- super.onBackPressed();
- }
-
- @Override
- public void onCallHangUp() {
- disconnect();
- finish();
+ public void onRoomStateChanged(QNRoomState state) {
+ Log.i(TAG, "onRoomStateChanged:" + state.name());
+ switch (state) {
+ case RECONNECTING:
+ logAndToast(getString(R.string.reconnecting_to_room));
+ mControlFragment.stopTimer();
+ break;
+ case CONNECTED:
+ mEngine.publishTracks(mLocalTrackList);
+ logAndToast(getString(R.string.connected_to_room));
+ mIsJoinedRoom = true;
+ mControlFragment.startTimer();
+ break;
+ case RECONNECTED:
+ logAndToast(getString(R.string.connected_to_room));
+ mControlFragment.startTimer();
+ break;
+ case CONNECTING:
+ logAndToast(getString(R.string.connecting_to, mRoomId));
+ break;
+ }
}
@Override
- public void onCameraSwitch() {
- if (mRTCManager != null) {
- mRTCManager.switchCamera(new QNCameraSwitchResultCallback() {
- @Override
- public void onCameraSwitchDone(boolean isFrontCamera) {
- }
-
- @Override
- public void onCameraSwitchError(String errorMessage) {
- }
- });
- }
+ public void onRemoteUserJoined(String remoteUserId, String userData) {
+ updateRemoteLogText("onRemoteUserJoined:remoteUserId = " + remoteUserId + " ,userData = " + userData);
}
@Override
- public boolean onToggleMic() {
- if (mRTCManager != null) {
- mMicEnabled = !mMicEnabled;
- mRTCManager.muteLocalAudio(!mMicEnabled);
- mLocalWindow.updateMicrophoneStateView(!mMicEnabled);
- }
- return mMicEnabled;
+ public void onRemoteUserLeft(final String remoteUserId) {
+ updateRemoteLogText("onRemoteUserLeft:remoteUserId = " + remoteUserId);
}
@Override
- public boolean onToggleVideo() {
- if (mRTCManager != null) {
- mVideoEnabled = !mVideoEnabled;
- mRTCManager.muteLocalVideo(!mVideoEnabled);
- if (!mVideoEnabled) {
- mUsedWindowList.get(0).setAudioViewVisible(0);
- } else {
- mUsedWindowList.get(0).setAudioViewInvisible();
- }
- mRTCManager.setPreviewEnabled(mVideoEnabled);
- }
- return mVideoEnabled;
+ public void onLocalPublished(List trackInfoList) {
+ updateRemoteLogText("onLocalPublished");
+ mEngine.enableStatistics();
}
@Override
- public boolean onToggleSpeaker() {
- if (mRTCManager != null) {
- mSpeakerEnabled = !mSpeakerEnabled;
- mRTCManager.muteRemoteAudio(!mSpeakerEnabled);
- }
- return mSpeakerEnabled;
+ public void onRemotePublished(String remoteUserId, List trackInfoList) {
+ updateRemoteLogText("onRemotePublished:remoteUserId = " + remoteUserId);
}
@Override
- public boolean onToggleBeauty() {
- if (mRTCManager != null) {
- mBeautyEnabled = !mBeautyEnabled;
- QNBeautySetting beautySetting = new QNBeautySetting(0.5f, 0.5f, 0.5f);
- beautySetting.setEnable(mBeautyEnabled);
- mRTCManager.setBeauty(beautySetting);
+ public void onRemoteUnpublished(final String remoteUserId, List trackInfoList) {
+ updateRemoteLogText("onRemoteUnpublished:remoteUserId = " + remoteUserId);
+ if (mTrackWindowMgr != null) {
+ mTrackWindowMgr.removeTrackInfo(remoteUserId, trackInfoList);
}
- return mBeautyEnabled;
}
@Override
- public void onJoinedRoom() {
- mIsAdmin = isAdmin();
- if (mIsAdmin) {
- mMergeStreamPosition = new String[9];
+ public void onRemoteUserMuted(String remoteUserId, List trackInfoList) {
+ updateRemoteLogText("onRemoteUserMuted:remoteUserId = " + remoteUserId);
+ if (mTrackWindowMgr != null) {
+ mTrackWindowMgr.onTrackInfoMuted(remoteUserId);
}
- onConnectedInternal();
- mRTCManager.publish();
- subscribeAllRemoteStreams();
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mControlFragment.startTimer();
- }
- });
}
@Override
- public void onLocalPublished() {
- if (mIsAdmin) {
- mRTCManager.setMergeStreamLayout(mUserId, 0, 0, 0, QNAppServer.STREAMING_WIDTH, QNAppServer.STREAMING_HEIGHT);
+ public void onSubscribed(String remoteUserId, List trackInfoList) {
+ updateRemoteLogText("onSubscribed:remoteUserId = " + remoteUserId);
+ if (mTrackWindowMgr != null) {
+ mTrackWindowMgr.addTrackInfo(remoteUserId, trackInfoList);
}
- mRTCManager.setStatisticsInfoEnabled(mUserId, true, 3000);
}
@Override
- public void onSubscribed(String userId) {
- Log.i(TAG, "onSubscribed: userId: " + userId);
- updateRemoteLogText("onSubscribed : " + userId);
- setMergeRemoteStreamLayout(userId);
- mRTCManager.setStatisticsInfoEnabled(userId, true, 3000);
+ public void onKickedOut(String userId) {
+ ToastUtils.s(RoomActivity.this, getString(R.string.kicked_by_admin));
+ finish();
}
@Override
- public void onRemotePublished(String userId, boolean hasAudio, boolean hasVideo) {
- Log.i(TAG, "onRemotePublished: userId: " + userId);
- updateRemoteLogText("onRemotePublished : " + userId + " hasAudio : " + hasAudio + " hasVideo : " + hasVideo);
- mRTCManager.subscribe(userId);
- mRTCManager.addRemoteAudioCallback(userId, new QNRemoteAudioCallback() {
- @Override
- public void onRemoteAudioAvailable(String userId, ByteBuffer audioData, int size, int bitsPerSample, int sampleRate, int numberOfChannels) {
+ public void onStatisticsUpdated(final QNStatisticsReport report) {
+ if (report.userId == null || report.userId.equals(mUserId)) {
+ if (QNTrackKind.AUDIO.equals(report.trackKind)) {
+ final String log = "音频码率:" + report.audioBitrate / 1000 + "kbps \n" +
+ "音频丢包率:" + report.audioPacketLostRate;
+ mControlFragment.updateLocalAudioLogText(log);
+ } else if (QNTrackKind.VIDEO.equals(report.trackKind)) {
+ final String log = "视频码率:" + report.videoBitrate / 1000 + "kbps \n" +
+ "视频丢包率:" + report.videoPacketLostRate + " \n" +
+ "视频的宽:" + report.width + " \n" +
+ "视频的高:" + report.height + " \n" +
+ "视频的帧率:" + report.frameRate;
+ mControlFragment.updateLocalVideoLogText(log);
}
- });
- }
-
- @Override
- public QNRemoteSurfaceView onRemoteStreamAdded(final String userId, final boolean isAudioEnabled, final boolean isVideoEnabled,
- final boolean isAudioMuted, final boolean isVideoMuted) {
- Log.i(TAG, "onRemoteStreamAdded: user = " + userId + ", hasAudio = " + isAudioEnabled + ", hasVideo = " + isVideoEnabled
- + ", isAudioMuted = " + isAudioMuted + ", isVideoMuted = " + isVideoMuted);
- updateRemoteLogText("onRemoteStreamAdded : " + userId);
-
- // 判断是否还有空闲的窗口用来绘制画面
- if (mUnusedWindowList.size() == 0) {
- Log.e(TAG, "There were more than 9 published users in the room, with no unUsedWindow to draw.");
- return null;
}
- final RTCVideoView remoteWindow = mUnusedWindowList.remove(0);
- remoteWindow.getRemoteSurfaceView().setZOrderMediaOverlay(true);
- remoteWindow.setUserId(userId);
- mUserWindowMap.put(userId, remoteWindow);
- mUsedWindowList.add(remoteWindow);
- final int userCount = mUsedWindowList.size();
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- remoteWindow.setVisible(true);
- remoteWindow.updateMicrophoneStateView(isAudioMuted);
- if (isVideoMuted || !isVideoEnabled) {
- remoteWindow.setAudioViewVisible(mUsedWindowList.indexOf(remoteWindow));
- remoteWindow.setAudioOnly(!isVideoEnabled);
- }
-
- if (userCount <= 5) {
- toggleToMultiUsersUI(userCount, mUsedWindowList);
- } else {
- setTargetWindowParams(userCount, userCount - 1, remoteWindow);
- }
- if (userCount >= 3) {
- mCallControlFragmentVisible = true;
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.show(mControlFragment);
- ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- ft.commitAllowingStateLoss();
- }
- }
- });
- return remoteWindow.getRemoteSurfaceView();
}
@Override
- public void onRemoteStreamRemoved(final String userId) {
- Log.i(TAG, "onRemoteStreamRemoved: " + userId);
- updateRemoteLogText("onRemoteStreamRemoved : " + userId);
- clearMergeStreamPos(userId);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mUserWindowMap.containsKey(userId)) {
- RTCVideoView remoteVideoView = mUserWindowMap.remove(userId);
- remoteVideoView.setVisible(false);
- mUsedWindowList.remove(remoteVideoView);
- mUnusedWindowList.add(remoteVideoView);
- }
- toggleToMultiUsersUI(mUsedWindowList.size(), mUsedWindowList);
- }
- });
+ public void onAudioRouteChanged(QNAudioDevice routing) {
+ updateRemoteLogText("onAudioRouteChanged: " + routing.name());
}
@Override
- public void onRemoteUserLeaved(String userId) {
- Log.i(TAG, "onUserOut: " + userId);
- updateRemoteLogText("onRemoteUserLeaved : " + userId);
+ public void onCreateMergeJobSuccess(String mergeJobId) {
}
@Override
- public void onRemoteUserJoined(String userId) {
- Log.i(TAG, "onUserIn: " + userId);
- updateRemoteLogText("onRemoteUserJoined : " + userId);
+ public void onError(int errorCode, String description) {
+ if (errorCode == QNErrorCode.ERROR_TOKEN_ERROR
+ || errorCode == QNErrorCode.ERROR_TOKEN_EXPIRED
+ || errorCode == QNErrorCode.ERROR_AUTH_FAIL
+ || errorCode == QNErrorCode.ERROR_RECONNECT_TOKEN_ERROR
+ || errorCode == QNErrorCode.ERROR_SIGNAL_IO_EXCEPTION
+ || errorCode == QNErrorCode.ERROR_TOKEN_INVALID) {
+ reportError("roomToken 错误,请重新加入房间");
+ } else if (errorCode == QNErrorCode.ERROR_PUBLISH_FAIL) {
+ reportError("发布失败,请重新加入房间发布");
+ } else if (errorCode == QNErrorCode.ERROR_ACCESSTOKEN_INVALID) {
+ reportError("服务端发生了一些问题,加入房间失败,请重试");
+ } else {
+ logAndToast("errorCode:" + errorCode + " description:" + description);
+ }
}
+ // Demo control
@Override
- public void onRemoteUnpublished(String userId) {
- Log.i(TAG, "onRemoteUnpublish: " + userId);
- updateRemoteLogText("onRemoteUnpublished : " + userId);
+ public void onCallHangUp() {
+ if (mEngine != null) {
+ mEngine.leaveRoom();
+ }
+ finish();
}
@Override
- public void onRemoteMute(final String userId, final boolean isAudioMuted, final boolean isVideoMuted) {
- Log.i(TAG, "onRemoteMute: user = " + userId + ", isAudioMuted = " + isAudioMuted + ", isVideoMuted = " + isVideoMuted);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- RTCVideoView remoteWindow = getWindowByUserId(userId);
- if (remoteWindow != null) {
- if (isVideoMuted && remoteWindow.getAudioViewVisibility() != View.VISIBLE) {
- remoteWindow.setAudioViewVisible(mUsedWindowList.indexOf(remoteWindow));
- } else if (!isVideoMuted && remoteWindow.getAudioViewVisibility() != View.INVISIBLE && !remoteWindow.isAudioOnly()) {
- remoteWindow.setAudioViewInvisible();
- }
- remoteWindow.updateMicrophoneStateView(isAudioMuted);
+ public void onCameraSwitch() {
+ if (mEngine != null) {
+ mEngine.switchCamera(new QNCameraSwitchResultCallback() {
+ @Override
+ public void onCameraSwitchDone(boolean isFrontCamera) {
}
- }
- });
- }
- @Override
- public void onStateChanged(QNRoomState state) {
- Log.i(TAG, "onStateChanged: " + state);
- updateRemoteLogText("onStateChanged : " + state.name());
- switch (state) {
- case RECONNECTING:
- mCallStartedTimeMs = System.currentTimeMillis();
- logAndToast(getString(R.string.reconnecting_to_room));
- break;
- case CONNECTED:
- break;
+ @Override
+ public void onCameraSwitchError(String errorMessage) {
+ }
+ });
}
}
@Override
- public void onError(final int errorCode, String description) {
- Log.i(TAG, "onError: " + errorCode + " " + description);
- updateRemoteLogText("onError : " + errorCode + " " + description);
- switch (errorCode) {
- case ERROR_KICKED_OUT_OF_ROOM:
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- ToastUtils.s(RoomActivity.this, getString(R.string.kicked_by_admin));
- }
- });
- onCallHangUp();
- break;
- default:
- reportError("errorCode: " + errorCode + "\ndescription: \n" + description);
- break;
+ public boolean onToggleMic() {
+ if (mEngine != null && mLocalAudioTrack != null) {
+ mMicEnabled = !mMicEnabled;
+ mLocalAudioTrack.setMuted(!mMicEnabled);
+ mEngine.muteTracks(Collections.singletonList(mLocalAudioTrack));
+ if (mTrackWindowMgr != null) {
+ mTrackWindowMgr.onTrackInfoMuted(mUserId);
+ }
}
+ return mMicEnabled;
}
@Override
- public void onStatisticsUpdated(QNStatisticsReport report) {
- Log.d(TAG, "onStatisticsUpdated: " + report.toString());
- if (!mUserId.equals(report.userId)) {
- return;
- }
- mLocalLogText = String.format(getString(R.string.log_text), report.userId, report.frameRate, report.videoBitrate / 1000, report.audioBitrate / 1000, report.videoPacketLostRate, report.audioPacketLostRate, report.width, report.height);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mControlFragment.updateLocalLogText(mLocalLogText);
+ public boolean onToggleVideo() {
+ if (mEngine != null && mLocalVideoTrack != null) {
+ mVideoEnabled = !mVideoEnabled;
+ mLocalVideoTrack.setMuted(!mVideoEnabled);
+ if (mLocalScreenTrack != null) {
+ mLocalScreenTrack.setMuted(!mVideoEnabled);
+ mEngine.muteTracks(Arrays.asList(mLocalScreenTrack, mLocalVideoTrack));
+ } else {
+ mEngine.muteTracks(Collections.singletonList(mLocalVideoTrack));
}
- });
- }
-
- @Override
- public void onUserKickedOut(String userId) {
- Log.i(TAG, "kicked out user: " + userId);
- updateRemoteLogText("onUserKickedOut : " + userId);
+ if (mTrackWindowMgr != null) {
+ mTrackWindowMgr.onTrackInfoMuted(mUserId);
+ }
+ }
+ return mVideoEnabled;
}
@Override
- public void onAudioRouteChanged(QNAudioDevice routing) {
- Log.i(TAG, "onAudioRouteChanged: " + routing.value());
+ public boolean onToggleSpeaker() {
+ if (mEngine != null) {
+ mSpeakerEnabled = !mSpeakerEnabled;
+ mEngine.muteRemoteAudio(!mSpeakerEnabled);
+ }
+ return mSpeakerEnabled;
}
@Override
- public void onCreateMergeJobSuccess(String mergeJobId) {
- Log.i(TAG, "onCreateMergeJobSuccess: " + mergeJobId);
- }
-
- private RTCVideoView.OnLongClickListener mOnLongClickListener = new RTCVideoView.OnLongClickListener() {
- @Override
- public void onLongClick(String userId) {
- if (!mIsAdmin) {
- Log.i(TAG, "Only admin user can kick a player!");
- return;
- }
- showKickoutDialog(userId);
+ public boolean onToggleBeauty() {
+ if (mEngine != null) {
+ mBeautyEnabled = !mBeautyEnabled;
+ QNBeautySetting beautySetting = new QNBeautySetting(0.5f, 0.5f, 0.5f);
+ beautySetting.setEnable(mBeautyEnabled);
+ mEngine.setBeauty(beautySetting);
}
- };
-}
\ No newline at end of file
+ return mBeautyEnabled;
+ }
+}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/ScreenCaptureActivity.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/ScreenCaptureActivity.java
deleted file mode 100644
index 981707b..0000000
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/ScreenCaptureActivity.java
+++ /dev/null
@@ -1,431 +0,0 @@
-package com.qiniu.droid.rtc.demo.activity;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.TextureView;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-
-import com.qiniu.droid.rtc.QNLocalSurfaceView;
-import com.qiniu.droid.rtc.QNRTCManager;
-import com.qiniu.droid.rtc.QNRTCSetting;
-import com.qiniu.droid.rtc.QNRemoteAudioCallback;
-import com.qiniu.droid.rtc.QNRemoteSurfaceView;
-import com.qiniu.droid.rtc.QNRoomEventListener;
-import com.qiniu.droid.rtc.QNRoomState;
-import com.qiniu.droid.rtc.QNStatisticsReport;
-import com.qiniu.droid.rtc.QNVideoFormat;
-import com.qiniu.droid.rtc.demo.R;
-import com.qiniu.droid.rtc.demo.ui.RTCVideoView;
-import com.qiniu.droid.rtc.demo.ui.RemoteVideoView;
-import com.qiniu.droid.rtc.demo.utils.Config;
-import com.qiniu.droid.rtc.demo.utils.ToastUtils;
-import com.qiniu.droid.rtc.model.QNAudioDevice;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static com.qiniu.droid.rtc.QNErrorCode.ERROR_KICKED_OUT_OF_ROOM;
-
-public class ScreenCaptureActivity extends Activity implements QNRoomEventListener {
- private static final String TAG = "ScreenCaptureActivity";
- public static final String EXTRA_ROOM_TOKEN = "ROOM_TOKEN";
-
- private static final String[] MANDATORY_PERMISSIONS = {
- "android.permission.MODIFY_AUDIO_SETTINGS",
- "android.permission.RECORD_AUDIO",
- "android.permission.INTERNET",
- "android.permission.CAMERA"
- };
-
- private List mUsedWindowList;
- private List mUnusedWindowList;
- private ConcurrentHashMap mUserWindowMap;
-
- private ImageButton mBtnVideo;
- private RemoteVideoView mRemoteWindowA;
- private RemoteVideoView mRemoteWindowB;
- private RemoteVideoView mRemoteWindowC;
- private RemoteVideoView mRemoteWindowD;
- private RemoteVideoView mRemoteWindowE;
- private RemoteVideoView mRemoteWindowF;
- private RemoteVideoView mRemoteWindowG;
- private RemoteVideoView mRemoteWindowH;
-
- private QNRTCManager mRTCManager;
- private String mRoomToken;
- private Camera mCamera;
- private FrameLayout mPreviewLayout;
- private boolean mIsPreviewEnable = true;
- private boolean mIsPreviewFirstFrame = true;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility());
-
- setContentView(R.layout.activity_screen_capture);
-
- for (String permission : MANDATORY_PERMISSIONS) {
- if (checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
- ToastUtils.s(getApplicationContext(), "Permission " + permission + " is not granted");
- setResult(RESULT_CANCELED);
- finish();
- return;
- }
- }
-
- Intent intent = getIntent();
- mRoomToken = intent.getStringExtra(EXTRA_ROOM_TOKEN);
-
- SharedPreferences preferences = getSharedPreferences(getString(R.string.app_name), Context.MODE_PRIVATE);
- int videoWidth = preferences.getInt(Config.WIDTH, QNRTCSetting.DEFAULT_WIDTH);
- int videoHeight = preferences.getInt(Config.HEIGHT, QNRTCSetting.DEFAULT_HEIGHT);
- boolean isHwCodec = preferences.getInt(Config.CODEC_MODE, Config.SW) == Config.HW;
-
- mPreviewLayout = (FrameLayout) findViewById(R.id.camera_preview);
- mBtnVideo = (ImageButton) findViewById(R.id.video_button);
- mRemoteWindowA = (RemoteVideoView) findViewById(R.id.remote_video_view_a);
- mRemoteWindowB = (RemoteVideoView) findViewById(R.id.remote_video_view_b);
- mRemoteWindowC = (RemoteVideoView) findViewById(R.id.remote_video_view_c);
- mRemoteWindowD = (RemoteVideoView) findViewById(R.id.remote_video_view_d);
- mRemoteWindowE = (RemoteVideoView) findViewById(R.id.remote_video_view_e);
- mRemoteWindowF = (RemoteVideoView) findViewById(R.id.remote_video_view_f);
- mRemoteWindowG = (RemoteVideoView) findViewById(R.id.remote_video_view_g);
- mRemoteWindowH = (RemoteVideoView) findViewById(R.id.remote_video_view_h);
-
- mUserWindowMap = new ConcurrentHashMap<>();
- mUsedWindowList = Collections.synchronizedList(new LinkedList());
- mUnusedWindowList = Collections.synchronizedList(new LinkedList());
- mUnusedWindowList.add(mRemoteWindowA);
- mUnusedWindowList.add(mRemoteWindowB);
- mUnusedWindowList.add(mRemoteWindowC);
- mUnusedWindowList.add(mRemoteWindowD);
- mUnusedWindowList.add(mRemoteWindowE);
- mUnusedWindowList.add(mRemoteWindowF);
- mUnusedWindowList.add(mRemoteWindowG);
- mUnusedWindowList.add(mRemoteWindowH);
-
- TextureView previewTextureView = (TextureView) findViewById(R.id.camera_preview_texture_view);
- previewTextureView.setSurfaceTextureListener(mPreviewSurfaceTextureListener);
-
- QNLocalSurfaceView localSurfaceView = (QNLocalSurfaceView) findViewById(R.id.local_surface_view);
- QNRTCSetting setting = new QNRTCSetting();
- setting.setHWCodecEnabled(isHwCodec)
- .setScreenCaptureEnabled(true)
- .setVideoPreviewFormat(new QNVideoFormat(videoWidth, videoHeight, QNRTCSetting.DEFAULT_FPS))
- .setVideoEncodeFormat(new QNVideoFormat(videoWidth, videoHeight, QNRTCSetting.DEFAULT_FPS));
- mRTCManager = new QNRTCManager();
- mRTCManager.setRoomEventListener(this);
- mRTCManager.addRemoteWindow(mRemoteWindowA.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowB.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowC.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowD.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowE.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowF.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowG.getRemoteSurfaceView());
- mRTCManager.addRemoteWindow(mRemoteWindowH.getRemoteSurfaceView());
- mRTCManager.initialize(getApplicationContext(), setting, localSurfaceView);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- startCameraPreview();
- mIsPreviewFirstFrame = true;
- mRTCManager.joinRoom(mRoomToken);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (mCamera != null) {
- mCamera.stopPreview();
- mCamera.release();
- mCamera = null;
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mRTCManager != null) {
- mRTCManager.destroy();
- }
-
- mRemoteWindowA = null;
- mRemoteWindowB = null;
- mRemoteWindowC = null;
- mRemoteWindowD = null;
- mRemoteWindowE = null;
- mRemoteWindowF = null;
- mRemoteWindowG = null;
- mRemoteWindowH = null;
- }
-
- @TargetApi(19)
- private static int getSystemUiVisibility() {
- int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- }
- return flags;
- }
-
- private static Camera getCameraInstance() {
- Camera c = null;
- try {
- c = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); // 打开前置摄像头
- } catch (Exception e) {
- // 相机不可用
- }
- return c;
- }
-
- private void startCameraPreview() {
- mCamera = getCameraInstance();
- if (mCamera != null) {
- Camera.Parameters parameters = mCamera.getParameters();
- float ratio = 9f / 16f;
- float temp = 0f;
- float minDiff = 100f;
- int previewWidth = 0;
- int previewHeight = 0;
- List supportedPreviewSizes = parameters.getSupportedPreviewSizes();
- for (Camera.Size s : supportedPreviewSizes) {
- temp = Math.abs(((float) s.height / (float) s.width) - ratio);
- if (temp < minDiff) {
- minDiff = temp;
- previewWidth = s.width;
- previewHeight = s.height;
- }
- }
- parameters.setPreviewSize(previewWidth, previewHeight);
- mCamera.setParameters(parameters);
- Log.i(TAG, "previewWidth = " + previewWidth + " previewHeight = " + previewHeight);
- mCamera.startPreview();
- }
- }
-
- private void subscribeAllRemoteStreams() {
- ArrayList publishingUsers = mRTCManager.getPublishingUserList();
- if (publishingUsers != null && !publishingUsers.isEmpty()) {
- for (String userId : publishingUsers) {
- mRTCManager.subscribe(userId);
- mRTCManager.addRemoteAudioCallback(userId, new QNRemoteAudioCallback() {
- @Override
- public void onRemoteAudioAvailable(String userId, ByteBuffer audioData, int size, int bitsPerSample, int sampleRate, int numberOfChannels) {
- }
- });
- }
- }
- }
-
- public void onClickHangUp(View view) {
- finish();
- }
-
- public void onClickPreviewEnable(View view) {
- mPreviewLayout.setVisibility(mIsPreviewEnable ? View.GONE : View.VISIBLE);
- mIsPreviewEnable = !mIsPreviewEnable;
- mBtnVideo.setImageResource(mIsPreviewEnable ? R.mipmap.video_open : R.mipmap.video_close);
- }
-
- public void onClickToHome(View view) {
- ToastUtils.l(this, "正在全局录制,可以返回应用");
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addCategory(Intent.CATEGORY_HOME);
- startActivity(intent);
- }
-
- @Override
- public void onJoinedRoom() {
- mRTCManager.publish();
- }
-
- @Override
- public void onLocalPublished() {
- subscribeAllRemoteStreams();
- }
-
- @Override
- public void onSubscribed(String userId) {
- Log.i(TAG, "onSubscribed = " + userId);
- }
-
- @Override
- public void onRemotePublished(String userId, boolean hasAudio, boolean hasVideo) {
- mRTCManager.subscribe(userId);
- }
-
- @Override
- public QNRemoteSurfaceView onRemoteStreamAdded(final String userId, final boolean isAudioEnabled, final boolean isVideoEnabled,
- final boolean isAudioMuted, final boolean isVideoMuted) {
- Log.i(TAG, "onRemoteStreamAdded: user = " + userId + ", hasAudio = " + isAudioEnabled + ", hasVideo = " + isVideoEnabled
- + ", isAudioMuted = " + isAudioMuted + ", isVideoMuted = " + isVideoMuted);
- final RTCVideoView remoteWindow = mUnusedWindowList.remove(0);
- remoteWindow.setUserId(userId);
- mUserWindowMap.put(userId, remoteWindow);
- mUsedWindowList.add(remoteWindow);
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- remoteWindow.setVisible(true);
- remoteWindow.setMicrophoneStateVisibility(View.GONE);
- remoteWindow.updateMicrophoneStateView(isAudioMuted);
- if (isVideoMuted || !isVideoEnabled) {
- remoteWindow.setAudioViewVisible(mUsedWindowList.indexOf(remoteWindow));
- remoteWindow.setAudioOnly(!isVideoEnabled);
- }
- }
- });
- return remoteWindow.getRemoteSurfaceView();
- }
-
- @Override
- public void onRemoteStreamRemoved(final String userId) {
- Log.i(TAG, "onRemoteStreamRemoved: " + userId);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mUserWindowMap.containsKey(userId)) {
- RTCVideoView remoteVideoView = mUserWindowMap.remove(userId);
- remoteVideoView.setVisibility(View.GONE);
- mUsedWindowList.remove(remoteVideoView);
- mUnusedWindowList.add(remoteVideoView);
- }
- }
- });
- }
-
- @Override
- public void onRemoteUserJoined(String userId) {
- Log.i(TAG, "onUserIn: " + userId);
- }
-
- @Override
- public void onRemoteUserLeaved(String userId) {
- Log.i(TAG, "onUserOut: " + userId);
- }
-
- @Override
- public void onRemoteUnpublished(String userId) {
- Log.i(TAG, "onRemoteUnpublish: " + userId);
- }
-
- @Override
- public void onRemoteMute(final String userId, final boolean isAudioMuted, final boolean isVideoMuted) {
- Log.i(TAG, "onRemoteMute: user = " + userId + ", audio = " + isAudioMuted + ", video = " + isVideoMuted);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- RTCVideoView remoteWindow = mUserWindowMap.containsKey(userId) ? mUserWindowMap.get(userId) : null;
- if (remoteWindow != null) {
- if (isVideoMuted && remoteWindow.getAudioViewVisibility() != View.VISIBLE) {
- remoteWindow.setAudioViewVisible(mUsedWindowList.indexOf(remoteWindow));
- } else if (!isVideoMuted && remoteWindow.getAudioViewVisibility() != View.INVISIBLE && !remoteWindow.isAudioOnly()) {
- remoteWindow.setAudioViewInvisible();
- }
- }
- }
- });
- }
-
- @Override
- public void onStateChanged(QNRoomState state) {
- Log.i(TAG, "onStateChanged: " + state);
- }
-
- @Override
- public void onError(int errorCode, String description) {
- Log.i(TAG, "onError: " + errorCode + " " + description);
- if (errorCode == ERROR_KICKED_OUT_OF_ROOM) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- ToastUtils.s(ScreenCaptureActivity.this, getString(R.string.kicked_by_admin));
- }
- });
- finish();
- }
- }
-
- @Override
- public void onStatisticsUpdated(QNStatisticsReport report) {
- Log.d(TAG, "onStatisticsUpdated: " + report.toString());
- }
-
- @Override
- public void onUserKickedOut(String userId) {
- Log.i(TAG, "kicked out user: " + userId);
- }
-
- @Override
- public void onAudioRouteChanged(QNAudioDevice routing) {
- Log.i(TAG, "onAudioRouteChanged: " + routing.value());
- }
-
- @Override
- public void onCreateMergeJobSuccess(String mergeJobId) {
- Log.i(TAG, "onCreateMergeJobSuccess: " + mergeJobId);
- }
-
- private TextureView.SurfaceTextureListener mPreviewSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- try {
- mCamera.setPreviewTexture(surface);
- mCamera.setDisplayOrientation(90);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- Log.i(TAG, "onSurfaceTextureSizeChanged");
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- Log.i(TAG, "onSurfaceTextureDestroyed");
- return false;
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if (mCamera != null && mIsPreviewFirstFrame) {
- try {
- mCamera.setPreviewTexture(surface);
- mCamera.setDisplayOrientation(90);
- mIsPreviewFirstFrame = false;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- };
-}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/SettingActivity.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/SettingActivity.java
index c1530a2..3e7286c 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/SettingActivity.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/activity/SettingActivity.java
@@ -39,11 +39,15 @@ public class SettingActivity extends AppCompatActivity {
private RadioGroup mCodecModeRadioGroup;
private RadioButton mHwCodecMode;
private RadioButton mSwCodecMode;
+ private RadioGroup mMaintainResRadioGroup;
+ private RadioButton mMaintainResolutionYes;
+ private RadioButton mMaintainResolutionNo;
private EditText mAppIdEditText;
private int mSelectPos = 0;
private String mUserName;
private int mEncodeMode = 0;
+ private boolean mMaintainResolution = false;
private List mDefaultConfiguration = new ArrayList<>();
private ArrayAdapter mAdapter;
private SpinnerPopupWindow mSpinnerPopupWindow;
@@ -64,6 +68,11 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
mHwCodecMode = (RadioButton) findViewById(R.id.hw_radio_button);
mSwCodecMode = (RadioButton) findViewById(R.id.sw_radio_button);
+ mMaintainResRadioGroup = (RadioGroup) findViewById(R.id.maintain_resolution_button);
+ mMaintainResRadioGroup.setOnCheckedChangeListener(mOnMaintainResCheckedChangeListener);
+ mMaintainResolutionYes = (RadioButton) findViewById(R.id.maintain_res_button_yes);
+ mMaintainResolutionNo = (RadioButton) findViewById(R.id.maintain_res_button_no);
+
mAppIdEditText = (EditText) findViewById(R.id.app_id_edit_text);
mVersionCodeTextView.setText(String.format(getString(R.string.version_code), getVersionDescription(), getBuildTimeDescription()));
@@ -87,13 +96,20 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
mSelectPos = preferences.getInt(Config.CONFIG_POS, 1);
mConfigTextView.setText(mDefaultConfiguration.get(mSelectPos));
- int codecMode = preferences.getInt(Config.CODEC_MODE, Config.SW);
+ int codecMode = preferences.getInt(Config.CODEC_MODE, Config.HW);
if (codecMode == Config.HW) {
mHwCodecMode.setChecked(true);
} else {
mSwCodecMode.setChecked(true);
}
+ mMaintainResolution = preferences.getBoolean(Config.MAINTAIN_RES, false);
+ if (mMaintainResolution) {
+ mMaintainResolutionYes.setChecked(true);
+ } else {
+ mMaintainResolutionNo.setChecked(true);
+ }
+
mSpinnerPopupWindow = new SpinnerPopupWindow(this);
mSpinnerPopupWindow.setOnSpinnerItemClickListener(mOnSpinnerItemClickListener);
@@ -128,6 +144,7 @@ public void onClickSaveConfiguration(View v) {
editor.putInt(Config.CONFIG_POS, mSelectPos);
editor.putInt(Config.CODEC_MODE, mEncodeMode);
+ editor.putBoolean(Config.MAINTAIN_RES, mMaintainResolution);
editor.putInt(Config.WIDTH, Config.DEFAULT_RESOLUTION[mSelectPos][0]);
editor.putInt(Config.HEIGHT, Config.DEFAULT_RESOLUTION[mSelectPos][1]);
@@ -190,9 +207,9 @@ protected String getBuildTimeDescription() {
}
private boolean isTestMode() {
- if (mAppIdEditText.getText().toString().compareTo(QNAppServer.TEST_MODE_APP_ID) == 0) {
- return true;
- }
+// if (mAppIdEditText.getText().toString().compareTo(QNAppServer.TEST_MODE_APP_ID) == 0) {
+// return true;
+// }
return false;
}
@@ -211,9 +228,25 @@ public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (group.getCheckedRadioButtonId()) {
case R.id.hw_radio_button:
mEncodeMode = Config.HW;
+ mMaintainResRadioGroup.setVisibility(View.GONE);
break;
case R.id.sw_radio_button:
mEncodeMode = Config.SW;
+ mMaintainResRadioGroup.setVisibility(View.VISIBLE);
+ break;
+ }
+ }
+ };
+
+ private RadioGroup.OnCheckedChangeListener mOnMaintainResCheckedChangeListener = new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ switch (group.getCheckedRadioButtonId()) {
+ case R.id.maintain_res_button_yes:
+ mMaintainResolution = true;
+ break;
+ case R.id.maintain_res_button_no:
+ mMaintainResolution = false;
break;
}
}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/fragment/ControlFragment.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/fragment/ControlFragment.java
index be3b980..b2944cf 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/fragment/ControlFragment.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/fragment/ControlFragment.java
@@ -28,7 +28,8 @@ public class ControlFragment extends Fragment {
private ImageButton mToggleVideoButton;
private ImageButton mLogShownButton;
private LinearLayout mLogView;
- private TextView mLocalTextView;
+ private TextView mLocalTextViewForVideo;
+ private TextView mLocalTextViewForAudio;
private TextView mRemoteTextView;
private StringBuffer mRemoteLogText;
private Chronometer mTimer;
@@ -77,8 +78,10 @@ public View onCreateView(
mToggleVideoButton = (ImageButton) mControlView.findViewById(R.id.camera_button);
mLogShownButton = (ImageButton) mControlView.findViewById(R.id.log_shown_button);
mLogView = (LinearLayout) mControlView.findViewById(R.id.log_text);
- mLocalTextView = (TextView) mControlView.findViewById(R.id.local_log_text);
- mLocalTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
+ mLocalTextViewForVideo = (TextView) mControlView.findViewById(R.id.local_log_text_video);
+ mLocalTextViewForVideo.setMovementMethod(ScrollingMovementMethod.getInstance());
+ mLocalTextViewForAudio = (TextView) mControlView.findViewById(R.id.local_log_text_audio);
+ mLocalTextViewForAudio.setMovementMethod(ScrollingMovementMethod.getInstance());
mRemoteTextView = (TextView) mControlView.findViewById(R.id.remote_log_text);
mRemoteTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
mTimer = (Chronometer) mControlView.findViewById(R.id.timer);
@@ -156,9 +159,15 @@ public void stopTimer() {
mTimer.stop();
}
- public void updateLocalLogText(String logText) {
+ public void updateLocalVideoLogText(String logText) {
if (mLogView.getVisibility() == View.VISIBLE) {
- mLocalTextView.setText(logText);
+ mLocalTextViewForVideo.setText(logText);
+ }
+ }
+
+ public void updateLocalAudioLogText(String logText) {
+ if (mLogView.getVisibility() == View.VISIBLE) {
+ mLocalTextViewForAudio.setText(logText);
}
}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/LocalVideoView.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/LocalVideoView.java
deleted file mode 100644
index c2a3937..0000000
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/LocalVideoView.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.qiniu.droid.rtc.demo.ui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-
-import com.qiniu.droid.rtc.QNLocalSurfaceView;
-import com.qiniu.droid.rtc.QNLocalVideoCallback;
-import com.qiniu.droid.rtc.demo.R;
-
-import org.webrtc.VideoFrame;
-
-public class LocalVideoView extends RTCVideoView implements QNLocalVideoCallback {
-
- public LocalVideoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- LayoutInflater.from(mContext).inflate(R.layout.local_video_view, this, true);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLocalSurfaceView = (QNLocalSurfaceView) findViewById(R.id.local_surface_view);
- mLocalSurfaceView.setLocalVideoCallback(this);
- }
-
- @Override
- public int onRenderingFrame(int textureId, int width, int height, VideoFrame.TextureBuffer.Type type, long timestampNs) {
- return textureId;
- }
-
- @Override
- public void onPreviewFrame(byte[] data, int width, int height, int rotation, int fmt, long timestampNs) {
- }
-
- @Override
- public void onSurfaceCreated() {
-
- }
-
- @Override
- public void onSurfaceChanged(int i, int i1) {
-
- }
-
- @Override
- public void onSurfaceDestroyed() {
-
- }
-}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RTCVideoView.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RTCVideoView.java
deleted file mode 100644
index 1933d7e..0000000
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RTCVideoView.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package com.qiniu.droid.rtc.demo.ui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.qiniu.droid.rtc.QNLocalSurfaceView;
-import com.qiniu.droid.rtc.QNRemoteSurfaceView;
-import com.qiniu.droid.rtc.demo.R;
-
-public class RTCVideoView extends FrameLayout implements View.OnLongClickListener {
-
- protected Context mContext;
- protected QNLocalSurfaceView mLocalSurfaceView;
- protected QNRemoteSurfaceView mRemoteSurfaceView;
- private ImageView mMicrophoneStateView;
- private TextView mAudioView;
- private OnLongClickListener mOnLongClickListener;
- private String mUserId;
- private boolean mIsAudioOnly = false;
-
- public interface OnLongClickListener {
- void onLongClick(String userId);
- }
-
- public RTCVideoView(Context context) {
- super(context);
- mContext = context;
- }
-
- public RTCVideoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- }
-
- public void setUserId(String userId) {
- mUserId = userId;
- }
-
- public void updateMicrophoneStateView(boolean isMute) {
- mMicrophoneStateView.setImageResource(isMute ? R.mipmap.microphone_disable : R.drawable.microphone_state_enable);
- }
-
- public QNLocalSurfaceView getLocalSurfaceView() {
- return mLocalSurfaceView;
- }
-
- public QNRemoteSurfaceView getRemoteSurfaceView() {
- return mRemoteSurfaceView;
- }
-
- public ImageView getMicrophoneStateView() {
- return mMicrophoneStateView;
- }
-
- public void setVisible(boolean isVisible) {
- if (!isVisible) {
- mUserId = null;
- mAudioView.setVisibility(INVISIBLE);
- }
- setVisibility(isVisible ? VISIBLE : INVISIBLE);
- mMicrophoneStateView.setVisibility(isVisible ? VISIBLE : INVISIBLE);
- setVideoViewVisible(isVisible);
- }
-
- public void setMicrophoneStateVisibility(int visibility) {
- mMicrophoneStateView.setVisibility(visibility);
- }
-
- public void setAudioViewVisible(int pos) {
- if (getVisibility() != VISIBLE) {
- setVisibility(VISIBLE);
- }
- mAudioView.setText(mUserId);
- mAudioView.setBackgroundColor(getTargetColor(pos));
- mAudioView.setVisibility(VISIBLE);
- setVideoViewVisible(false);
- }
-
- public void setAudioViewInvisible() {
- mAudioView.setVisibility(INVISIBLE);
- setVideoViewVisible(true);
- }
-
- public void updateAudioView(int pos) {
- mAudioView.setBackgroundColor(getTargetColor(pos));
- }
-
- public int getAudioViewVisibility() {
- return mAudioView.getVisibility();
- }
-
- public void setOnLongClickListener(OnLongClickListener listener) {
- mOnLongClickListener = listener;
- }
-
- public void setAudioOnly(boolean isAudioOnly) {
- mIsAudioOnly = isAudioOnly;
- }
-
- public boolean isAudioOnly() {
- return mIsAudioOnly;
- }
-
- private void setVideoViewVisible(boolean isVisible) {
- if (mLocalSurfaceView != null) {
- mLocalSurfaceView.setVisibility(isVisible ? VISIBLE : INVISIBLE);
- }
- if (mRemoteSurfaceView != null) {
- mRemoteSurfaceView.setVisibility(isVisible ? VISIBLE : INVISIBLE);
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- setOnLongClickListener(this);
- mMicrophoneStateView = (ImageView) findViewById(R.id.microphone_state_view);
- mAudioView = (TextView) findViewById(R.id.qn_audio_view);
- }
-
- private int getTargetColor(int pos) {
- int[] customizedColors = mContext.getResources().getIntArray(R.array.audioBackgroundColors);
- return customizedColors[pos % 6];
- }
-
- @Override
- public boolean onLongClick(View v) {
- if (mOnLongClickListener != null) {
- mOnLongClickListener.onLongClick(mUserId);
- }
- return false;
- }
-}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RadioGroupFlow.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RadioGroupFlow.java
new file mode 100644
index 0000000..1d919ab
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RadioGroupFlow.java
@@ -0,0 +1,67 @@
+package com.qiniu.droid.rtc.demo.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RadioGroup;
+
+public class RadioGroupFlow extends RadioGroup {
+ public RadioGroupFlow(Context context) {
+ super(context);
+ }
+
+ public RadioGroupFlow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int childCount = getChildCount();
+ int x = 0;
+ int y = 0;
+ int row = 0;
+
+ for (int index = 0; index < childCount; index++) {
+ final View child = getChildAt(index);
+ if (child.getVisibility() != View.GONE) {
+ child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
+ x += width;
+ y = row * height + height;
+ if (x > maxWidth) {
+ x = width;
+ row++;
+ y = row * height + height;
+ }
+ }
+ }
+ setMeasuredDimension(maxWidth, y);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int childCount = getChildCount();
+ int maxWidth = r - l;
+ int x = 0;
+ int y = 0;
+ int row = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View child = this.getChildAt(i);
+ if (child.getVisibility() != View.GONE) {
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
+ x += width;
+ y = row * height + height;
+ if (x > maxWidth) {
+ x = width;
+ row++;
+ y = row * height + height;
+ }
+ child.layout(x - width, y - height, x, y);
+ }
+ }
+ }
+}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RemoteVideoView.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RemoteVideoView.java
deleted file mode 100644
index 718fae5..0000000
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/RemoteVideoView.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.qiniu.droid.rtc.demo.ui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-
-import com.qiniu.droid.rtc.QNRemoteSurfaceView;
-import com.qiniu.droid.rtc.QNRemoteVideoCallback;
-import com.qiniu.droid.rtc.demo.R;
-
-import org.webrtc.VideoFrame;
-
-public class RemoteVideoView extends RTCVideoView implements QNRemoteVideoCallback {
-
- public RemoteVideoView(Context context) {
- super(context);
- mContext = context;
- }
-
- public RemoteVideoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- LayoutInflater.from(mContext).inflate(R.layout.remote_video_view, this, true);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mRemoteSurfaceView = (QNRemoteSurfaceView) findViewById(R.id.remote_surface_view);
- mRemoteSurfaceView.setRemoteVideoCallback(this);
- }
-
- @Override
- public void onRenderingFrame(VideoFrame frame) {
-
- }
-
- @Override
- public void onSurfaceCreated() {
-
- }
-
- @Override
- public void onSurfaceChanged(int i, int i1) {
-
- }
-
- @Override
- public void onSurfaceDestroyed() {
-
- }
-}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/UserTrackView.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/UserTrackView.java
new file mode 100644
index 0000000..fa0af6f
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/UserTrackView.java
@@ -0,0 +1,432 @@
+package com.qiniu.droid.rtc.demo.ui;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.qiniu.droid.rtc.QNRTCEngine;
+import com.qiniu.droid.rtc.QNSurfaceView;
+import com.qiniu.droid.rtc.QNTrackInfo;
+import com.qiniu.droid.rtc.QNTrackKind;
+import com.qiniu.droid.rtc.demo.R;
+
+import org.webrtc.RendererCommon;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class UserTrackView extends FrameLayout {
+
+ private static final String TAG = "UserTrackView";
+ private static final boolean PRINT_DEBUG_LOG = false;
+
+ private static final boolean DISPLAY_LARGE_VIDEO_TRACK = true;
+ private static final boolean DISPLAY_SMALL_VIDEO_TRACK = true;
+
+ public static final String TAG_CAMERA = "camera";
+ public static final String TAG_SCREEN = "screen";
+
+ private boolean inited = false;
+ private ViewGroup mVideoViewLargeParent;
+ private QNSurfaceView mSurfaceViewLarge;
+
+ private ViewGroup mVideoViewSmallParent;
+ private QNSurfaceView mSurfaceViewSmall;
+
+ private ImageView mMicrophoneStateView;
+ private TextView mAudioView;
+
+ private QNRTCEngine mQNRTCEngine;
+ private String mUserId;
+ private QNTrackInfo mQNAudioTrackInfo;
+ private List mQNVideoTrackInfos = new ArrayList<>();
+
+ private QNTrackInfo mTrackInfoDisplayInLargeView = null;
+ private QNTrackInfo mTrackInfoDisplayInSmallView = null;
+ private int mMicrophoneViewVisibility = -1;
+ private int mPos = -1;
+
+ public UserTrackView(@NonNull Context context) {
+ super(context);
+ init();
+ }
+
+ public UserTrackView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ if (inited) {
+ return;
+ }
+ inited = true;
+ LayoutInflater.from(getContext()).inflate(getLayout(), this, true);
+ }
+
+ protected int getLayout() {
+ return R.layout.user_tracks_view;
+ }
+
+ public String getUserId() {
+ return mUserId;
+ }
+
+ public boolean isTaken() {
+ return !TextUtils.isEmpty(getUserId());
+ }
+
+ public List getTrackInfos() {
+ List trackInfos = new ArrayList<>();
+ if (mQNAudioTrackInfo != null) {
+ trackInfos.add(mQNAudioTrackInfo);
+ }
+ trackInfos.addAll(mQNVideoTrackInfos);
+ return trackInfos;
+ }
+
+
+ public void setUserTrackInfo(QNRTCEngine engine, String userId, List trackInfos) {
+ setUserTrackInfo(engine, userId, trackInfos, View.VISIBLE);
+ }
+
+ public void setUserTrackInfo(QNRTCEngine engine, String userId, List trackInfos, int microphoneViewVisibility) {
+ LogD(TAG, "setUserTrackInfo() userId: " + userId);
+ mQNRTCEngine = engine;
+ mUserId = userId;
+ mQNAudioTrackInfo = null;
+ mQNVideoTrackInfos.clear();
+
+ if (TextUtils.isEmpty(mUserId)) {
+ return;
+ }
+ setMicrophoneStateVisibility(microphoneViewVisibility);
+ mAudioView.setText(mUserId);
+ onAddTrackInfo(trackInfos);
+ }
+
+ public void unSetUserTrackInfo() {
+ if (mQNRTCEngine != null) {
+ if (mTrackInfoDisplayInLargeView != null) {
+ mQNRTCEngine.setRenderWindow(mTrackInfoDisplayInLargeView, null);
+ }
+ if (mTrackInfoDisplayInSmallView != null) {
+ mQNRTCEngine.setRenderWindow(mTrackInfoDisplayInSmallView, null);
+ }
+ }
+ reset();
+ }
+
+ public void reset() {
+ LogD(TAG, "reset()");
+ mQNRTCEngine = null;
+ mUserId = null;
+ mQNAudioTrackInfo = null;
+ mQNVideoTrackInfos.clear();
+ mPos = -1;
+
+ mSurfaceViewLarge.setVisibility(View.GONE);
+ mVideoViewLargeParent.setVisibility(View.GONE);
+ mSurfaceViewSmall.setVisibility(View.GONE);
+ mVideoViewSmallParent.setVisibility(View.GONE);
+ mAudioView.setText("");
+ mAudioView.setVisibility(View.GONE);
+ mTrackInfoDisplayInLargeView = null;
+ mTrackInfoDisplayInSmallView = null;
+ }
+
+ public void dispose() {
+ LogD(TAG, "dispose()");
+ mSurfaceViewLarge.release();
+ mSurfaceViewSmall.release();
+ }
+
+ public void onAddTrackInfo(List trackInfos) {
+ LogD(TAG, "onAddTrackInfo()");
+ for (QNTrackInfo item : trackInfos) {
+ onAddTrackInfo(item, false);
+ }
+ onTrackInfoChanged();
+ }
+
+ @SuppressWarnings("unused")
+ public void onAddTrackInfo(QNTrackInfo trackInfo) {
+ onAddTrackInfo(trackInfo, true);
+ }
+
+ private void onAddTrackInfo(QNTrackInfo trackInfo, boolean notify) {
+ if (QNTrackKind.AUDIO.equals(trackInfo.getTrackKind())) {
+ mQNAudioTrackInfo = trackInfo;
+ } else {
+ mQNVideoTrackInfos.add(trackInfo);
+ }
+ if (notify) {
+ onTrackInfoChanged();
+ }
+ }
+
+ public boolean onRemoveTrackInfo(List trackInfos) {
+ LogD(TAG, "onRemoveTrackInfo()");
+ for (QNTrackInfo item : trackInfos) {
+ onRemoveTrackInfo(item, false);
+ }
+ onTrackInfoChanged();
+ return mQNAudioTrackInfo != null || !mQNVideoTrackInfos.isEmpty();
+ }
+
+ @SuppressWarnings("unused")
+ public void onRemoveTrackInfo(QNTrackInfo trackInfo) {
+ onRemoveTrackInfo(trackInfo, true);
+ }
+
+ public void onRemoveTrackInfo(QNTrackInfo trackInfo, boolean notify) {
+ if (QNTrackKind.AUDIO.equals(trackInfo.getTrackKind())) {
+ mQNAudioTrackInfo = null;
+ } else {
+ mQNVideoTrackInfos.remove(trackInfo);
+ }
+ if (notify) {
+ onTrackInfoChanged();
+ }
+ }
+
+ public void onTracksMuteChanged() {
+ LogD(TAG, "onTracksMuteChanged()");
+ // audio track
+ if (mQNAudioTrackInfo != null) {
+ setMicrophoneStateVisibilityInner(View.VISIBLE);
+ updateMicrophoneStateView(mQNAudioTrackInfo.isMuted());
+ } else {
+ setMicrophoneStateVisibilityInner(View.INVISIBLE);
+ }
+
+ // video tracks
+ boolean hideAudioView = containsUnMutedVideoTracks(2);
+ setAudioViewStateVisibility(hideAudioView ? View.GONE : View.VISIBLE);
+ // note : mSurfaceViewSmall is on top, so set visibility
+ if (mTrackInfoDisplayInSmallView != null) {
+ mSurfaceViewSmall.setVisibility(hideAudioView ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private void onTrackInfoChanged() {
+ onTracksMuteChanged();
+
+ QNTrackInfo cameraTrackInfo = findVideoTrack(TAG_CAMERA);
+ QNTrackInfo screenTrackInfo = findVideoTrack(TAG_SCREEN);
+
+ // in case, camera has no tag.
+ if (cameraTrackInfo == null && !mQNVideoTrackInfos.isEmpty()) {
+ List trackInfosExcludeScreenTrack = new ArrayList<>(mQNVideoTrackInfos);
+ trackInfosExcludeScreenTrack.remove(screenTrackInfo);
+ if (!trackInfosExcludeScreenTrack.isEmpty()) {
+ cameraTrackInfo = trackInfosExcludeScreenTrack.get(0);
+ }
+ }
+
+ QNTrackInfo trackInfoDisplayInLargeView = null;
+ QNTrackInfo trackInfoDisplayInSmallView = null;
+ if (cameraTrackInfo != null && screenTrackInfo != null) {
+ LogD(TAG, "contains camera and screen track info");
+ trackInfoDisplayInLargeView = screenTrackInfo;
+ trackInfoDisplayInSmallView = cameraTrackInfo;
+ } else {
+ if (cameraTrackInfo != null) {
+ LogD(TAG, "just contains camera track info");
+ trackInfoDisplayInLargeView = cameraTrackInfo;
+ }
+ if (screenTrackInfo != null) {
+ LogD(TAG, "just contains screen track info");
+ trackInfoDisplayInLargeView = screenTrackInfo;
+ }
+ }
+ updateTrackInfoInLargeView(trackInfoDisplayInLargeView);
+ updateTrackInfoInSmallView(trackInfoDisplayInSmallView);
+ }
+
+ private QNTrackInfo findVideoTrack(String tag) {
+ QNTrackInfo found = null;
+ for (QNTrackInfo item : mQNVideoTrackInfos) {
+ if (tag.equals(item.getTag())) {
+ found = item;
+ break;
+ }
+ }
+ return found;
+ }
+
+ private void updateTrackInfoInLargeView(QNTrackInfo trackInfoDisplayInLargeView) {
+ if (mTrackInfoDisplayInLargeView != null && mTrackInfoDisplayInLargeView == trackInfoDisplayInLargeView) {
+ LogD(TAG, "skip updateTrackInfoInLargeView, same track");
+ return;
+ }
+ mTrackInfoDisplayInLargeView = trackInfoDisplayInLargeView;
+ if (mTrackInfoDisplayInLargeView != null) {
+ if (DISPLAY_LARGE_VIDEO_TRACK) {
+ mSurfaceViewLarge.setVisibility(View.VISIBLE);
+ mQNRTCEngine.setRenderWindow(mTrackInfoDisplayInLargeView, mSurfaceViewLarge);
+ if (TAG_SCREEN.equals(mTrackInfoDisplayInLargeView.getTag())) {
+ mSurfaceViewLarge.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
+ } else {
+ mSurfaceViewLarge.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
+ }
+ } else {
+ mSurfaceViewLarge.setVisibility(View.GONE);
+ mVideoViewLargeParent.setBackgroundColor(getTargetColor(new Random().nextInt(6)));
+ }
+ mVideoViewLargeParent.setVisibility(View.VISIBLE);
+ } else {
+ mSurfaceViewLarge.setVisibility(View.GONE);
+ mVideoViewLargeParent.setVisibility(View.GONE);
+ }
+ }
+
+ private void updateTrackInfoInSmallView(QNTrackInfo trackInfoDisplayInSmallView) {
+ if (mTrackInfoDisplayInSmallView != null && mTrackInfoDisplayInSmallView == trackInfoDisplayInSmallView) {
+ LogD(TAG, "skip updateTrackInfoInSmallView, same track");
+ return;
+ }
+ mTrackInfoDisplayInSmallView = trackInfoDisplayInSmallView;
+ if (mTrackInfoDisplayInSmallView != null) {
+ if (DISPLAY_SMALL_VIDEO_TRACK) {
+ mSurfaceViewSmall.setVisibility(View.VISIBLE);
+ mQNRTCEngine.setRenderWindow(mTrackInfoDisplayInSmallView, mSurfaceViewSmall);
+ } else {
+ mSurfaceViewSmall.setVisibility(View.GONE);
+ mVideoViewSmallParent.setBackgroundColor(getTargetColor(new Random().nextInt(6)));
+ }
+ mVideoViewSmallParent.setVisibility(View.VISIBLE);
+ } else {
+ mSurfaceViewSmall.setVisibility(View.GONE);
+ mVideoViewSmallParent.setVisibility(View.GONE);
+ }
+ }
+
+ @SuppressWarnings("all")
+ private boolean containsUnMutedVideoTracks(int count) {
+ boolean unMuted = false;
+ for (int i = 0; i < mQNVideoTrackInfos.size() && i < count; i++) {
+ QNTrackInfo item = mQNVideoTrackInfos.get(i);
+ if (!item.isMuted()) {
+ unMuted = true;
+ break;
+ }
+ }
+ return unMuted;
+ }
+
+
+ public void changeViewBackgroundByPos(int pos) {
+ LogD(TAG, "changeViewBackgroundByPos() " + pos);
+ mPos = pos;
+ if (mPos != -1) {
+ mAudioView.setBackgroundColor(getTargetColor(pos));
+ }
+ }
+
+ private void setAudioViewStateVisibility(int visibility) {
+ mAudioView.setVisibility(visibility);
+ }
+
+ public void setMicrophoneStateVisibility(int visibility) {
+ mMicrophoneViewVisibility = visibility;
+ mMicrophoneStateView.setVisibility(visibility);
+ }
+
+ private void setMicrophoneStateVisibilityInner(int visibility) {
+ if (mMicrophoneViewVisibility == -1 || mMicrophoneViewVisibility == View.VISIBLE) {
+ mMicrophoneStateView.setVisibility(visibility);
+ }
+ }
+
+ private void updateMicrophoneStateView(boolean isMute) {
+ mMicrophoneStateView.setImageResource(isMute ? R.mipmap.microphone_disable : R.drawable.microphone_state_enable);
+ }
+
+ @SuppressWarnings("all")
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mVideoViewLargeParent = findViewById(R.id.qn_surface_view_large_parent);
+ mSurfaceViewLarge = (QNSurfaceView) findViewById(R.id.qn_surface_view_large);
+
+ mVideoViewSmallParent = findViewById(R.id.qn_surface_view_small_parent);
+ mSurfaceViewSmall = (QNSurfaceView) findViewById(R.id.qn_surface_view_small);
+
+ mMicrophoneStateView = (ImageView) findViewById(R.id.microphone_state_view);
+ mAudioView = (TextView) findViewById(R.id.qn_audio_view);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (visibility == View.GONE) {
+ mSurfaceViewLarge.setVisibility(visibility);
+ mSurfaceViewSmall.setVisibility(visibility);
+ } else {
+ if (mTrackInfoDisplayInLargeView != null) {
+ mSurfaceViewLarge.setVisibility(visibility);
+ }
+ if (mTrackInfoDisplayInSmallView != null) {
+ mSurfaceViewSmall.setVisibility(visibility);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ private int getTargetColor(int pos) {
+ int[] customizedColors = getContext().getResources().getIntArray(R.array.audioBackgroundColors);
+ return customizedColors[pos % 6];
+ }
+
+ public void setZOrderMediaOverlay(boolean isMediaOverlay, boolean onTop) {
+ if (DISPLAY_LARGE_VIDEO_TRACK) {
+ mSurfaceViewLarge.setZOrderMediaOverlay(isMediaOverlay);
+ }
+ if (DISPLAY_SMALL_VIDEO_TRACK) {
+ mSurfaceViewSmall.setZOrderMediaOverlay(isMediaOverlay);
+ mSurfaceViewSmall.setZOrderOnTop(onTop);
+ }
+ }
+
+ public static void swap(QNRTCEngine engine, UserTrackView trackViewFirst, UserTrackView trackViewSecond) {
+ String userIdFirst = trackViewFirst.getUserId();
+ List trackInfosFirst = trackViewFirst.getTrackInfos();
+ int postFirst = trackViewFirst.mPos;
+
+ String userIdSecond = trackViewSecond.getUserId();
+ List trackInfosSecond = trackViewSecond.getTrackInfos();
+ int postSecond = trackViewSecond.mPos;
+
+ trackViewFirst.setUserTrackInfo(engine, userIdSecond, trackInfosSecond, trackViewFirst.mMicrophoneViewVisibility);
+ trackViewFirst.changeViewBackgroundByPos(postSecond);
+
+ trackViewSecond.setUserTrackInfo(engine, userIdFirst, trackInfosFirst, trackViewSecond.mMicrophoneViewVisibility);
+ trackViewSecond.changeViewBackgroundByPos(postFirst);
+ }
+
+ public String getResourceName() {
+ try {
+ return this.getResources().getResourceEntryName(this.getId());
+ } catch (Resources.NotFoundException var2) {
+ return "";
+ }
+ }
+
+
+ private void LogD(String tag, String message) {
+ if (PRINT_DEBUG_LOG) {
+ Log.d(tag + " " + getResourceName(), message);
+ }
+ }
+}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/UserTrackViewFullScreen.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/UserTrackViewFullScreen.java
new file mode 100644
index 0000000..c3097f2
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/UserTrackViewFullScreen.java
@@ -0,0 +1,24 @@
+package com.qiniu.droid.rtc.demo.ui;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+
+import com.qiniu.droid.rtc.demo.R;
+
+public class UserTrackViewFullScreen extends UserTrackView {
+
+ public UserTrackViewFullScreen(@NonNull Context context) {
+ super(context);
+ }
+
+ public UserTrackViewFullScreen(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected int getLayout() {
+ return R.layout.user_tracks_view_full_screen;
+ }
+}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/Config.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/Config.java
index ed887fb..0af67e6 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/Config.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/Config.java
@@ -19,12 +19,14 @@ public class Config {
public static final String CODEC_MODE = "encodeMode";
public static final String CAPTURE_MODE = "captureMode";
public static final String BITRATE = "bitrate";
+ public static final String MAINTAIN_RES = "maintainRes";
public static final int HW = 0;
public static final int SW = 1;
public static final int CAMERA_CAPTURE = 0;
public static final int SCREEN_CAPTURE = 1;
public static final int ONLY_AUDIO_CAPTURE = 2;
+ public static final int MUTI_TRACK_CAPTURE = 3;
public static final int[][] DEFAULT_RESOLUTION = {
{352, 288},
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/MyHashMap.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/MyHashMap.java
new file mode 100644
index 0000000..1a2c4a2
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/MyHashMap.java
@@ -0,0 +1,53 @@
+package com.qiniu.droid.rtc.demo.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+public class MyHashMap extends HashMap {
+
+ private List mOrderedValues = new LinkedList<>();
+
+ public List getOrderedValues() {
+ return new ArrayList<>(mOrderedValues);
+ }
+
+ public List getOrderedValues(V exclude) {
+ LinkedList result = new LinkedList<>(mOrderedValues);
+ result.remove(exclude);
+ return result;
+ }
+
+ public V put(K key, V value, boolean insertToFirst) {
+ // in case replace key
+ mOrderedValues.remove(get(key));
+ // in case replace value
+ mOrderedValues.remove(value);
+
+ if (insertToFirst) {
+ mOrderedValues.add(0, value);
+ } else {
+ mOrderedValues.add(value);
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return put(key, value, false);
+ }
+
+ @Override
+ public V remove(Object key) {
+ V result = super.remove(key);
+ mOrderedValues.remove(result);
+ return result;
+ }
+
+ @Override
+ public void clear() {
+ mOrderedValues.clear();
+ super.clear();
+ }
+}
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/TrackWindowMgr.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/TrackWindowMgr.java
new file mode 100644
index 0000000..615ff55
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/utils/TrackWindowMgr.java
@@ -0,0 +1,308 @@
+package com.qiniu.droid.rtc.demo.utils;
+
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.qiniu.droid.rtc.QNRTCEngine;
+import com.qiniu.droid.rtc.QNTrackInfo;
+import com.qiniu.droid.rtc.demo.ui.UserTrackView;
+
+import org.webrtc.Logging;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TrackWindowMgr {
+
+ private static final String TAG = "TrackWindowMgr";
+
+ // Current userId.
+ private String mCurrentUserId;
+ // Screen resolution.
+ private int mScreenWidth;
+ @SuppressWarnings("all")
+ private int mScreenHeight;
+ // Screen density.
+ private float mDensity;
+ // QNRTCEngine instance.
+ private QNRTCEngine mEngine;
+ // TrackView full screen.
+ private UserTrackView mTrackFullScreenWin;
+ // TrackView item in grid.
+ private List mTrackCandidateWins;
+ // Map userId to TrackView.
+ private MyHashMap mUserWindowMap = new MyHashMap<>();
+ // Flag, Windows mode p2p, otherwise multi user.
+ private Boolean mTrackWindowP2PMode = null;
+
+ public TrackWindowMgr(String currentUserId, int screenWidth, int screenHeight, float density
+ , QNRTCEngine engine, UserTrackView trackFullScreenWin, List trackCandidateWins) {
+ mCurrentUserId = currentUserId;
+ mScreenWidth = screenWidth;
+ mScreenHeight = screenHeight;
+ mDensity = density;
+ mEngine = engine;
+ mTrackFullScreenWin = trackFullScreenWin;
+ mTrackCandidateWins = new ArrayList<>(trackCandidateWins);
+
+ mTrackFullScreenWin.setZOrderMediaOverlay(false, true);
+ mTrackFullScreenWin.changeViewBackgroundByPos(0);
+ mTrackFullScreenWin.setMicrophoneStateVisibility(View.GONE);
+ mTrackFullScreenWin.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mUserWindowMap.size() <= 1) {
+ Logging.d(TAG, "skip for single user.");
+ } else if (mUserWindowMap.size() == 2) {
+ // swap
+ switchToFullScreenWindow(mUserWindowMap.getOrderedValues(mTrackFullScreenWin).get(0));
+ } else {
+ // exit from full screen and display all
+ if (mTrackCandidateWins.isEmpty()) {
+ mTrackFullScreenWin.unSetUserTrackInfo();
+ mTrackFullScreenWin.setVisibility(View.GONE);
+ } else {
+ UserTrackView userTrackView = mTrackCandidateWins.remove(0);
+ switchToFullScreenWindow(userTrackView);
+ userTrackView.changeViewBackgroundByPos(mUserWindowMap.size());
+
+ setTrackUserWindowsVisibility(View.VISIBLE);
+ updateTrackWindowsLayout();
+ }
+ }
+ }
+ });
+
+ for (final UserTrackView view : mTrackCandidateWins) {
+ view.setZOrderMediaOverlay(true, true);
+ view.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mUserWindowMap.size() <= 1) {
+ Logging.d(TAG, "skip for single user.");
+ } else if (mUserWindowMap.size() == 2) {
+ // swap
+ switchToFullScreenWindow(view);
+ } else {
+ // full screen and hide others
+ switchToFullScreenWindow(view);
+ setTrackUserWindowsVisibility(View.GONE);
+ }
+ }
+ });
+ }
+ }
+
+ public void addTrackInfo(String userId, List trackInfoList) {
+ if (mTrackCandidateWins.size() == 0) {
+ Logging.e(TAG, "There were more than 9 published users in the room, with no unUsedWindow to draw.");
+ return;
+ }
+ UserTrackView userTrackView = mUserWindowMap.get(userId);
+ if (userTrackView != null) {
+ // user has already displayed in screen
+ userTrackView.onAddTrackInfo(trackInfoList);
+ } else {
+ // allocate new track windows
+ userTrackView = mTrackCandidateWins.remove(0);
+ mUserWindowMap.put(userId, userTrackView, userId.equals(mCurrentUserId));
+ userTrackView.setUserTrackInfo(mEngine, userId, trackInfoList);
+ userTrackView.changeViewBackgroundByPos(mUserWindowMap.size());
+
+ userTrackView.setVisibility(View.VISIBLE);
+
+ // update whole layout
+ updateTrackWindowsLayout();
+ }
+ }
+
+ public void onTrackInfoMuted(String remoteUserId) {
+ UserTrackView window = mUserWindowMap.get(remoteUserId);
+ if (window != null) {
+ window.onTracksMuteChanged();
+ }
+ }
+
+ public void removeTrackInfo(String userId, List trackInfoList) {
+ UserTrackView remoteVideoView = mUserWindowMap.get(userId);
+ if (remoteVideoView == null) {
+ return;
+ }
+ boolean trackInfoRemains = remoteVideoView.onRemoveTrackInfo(trackInfoList);
+ if (userId.equals(mCurrentUserId)) {
+ // always show myself in screen
+ return;
+ }
+ if (!trackInfoRemains) {
+ // check, if no more tracks for this user. remove it
+ removeTrackWindow(userId);
+ }
+ }
+
+ private void removeTrackWindow(String remoteUserId) {
+ UserTrackView remoteVideoView = mUserWindowMap.remove(remoteUserId);
+ if (remoteVideoView == null) {
+ return;
+ }
+ remoteVideoView.reset();
+ if (mTrackFullScreenWin == remoteVideoView) {
+ if (mUserWindowMap.size() == 1) {
+ switchToFullScreenWindow(mUserWindowMap.getOrderedValues().get(0));
+ } else {
+ mTrackFullScreenWin.setVisibility(View.GONE);
+ updateTrackWindowsLayout();
+ }
+ } else {
+ remoteVideoView.setVisibility(View.GONE);
+ mTrackCandidateWins.add(remoteVideoView);
+ updateTrackWindowsLayout();
+ }
+ }
+
+ private void updateTrackWindowsLayout() {
+ if (mUserWindowMap.isEmpty()) {
+ return;
+ }
+ updateTrackWindowMode(mUserWindowMap.size() <= 2);
+
+ List userWindows = mUserWindowMap.getOrderedValues(mTrackFullScreenWin);
+ int userCountInGridWindow = userWindows.size();
+ for (int i = 0; i < userCountInGridWindow; i++) {
+ UserTrackView trackView = userWindows.get(i);
+ setTargetWindowParams(userCountInGridWindow, i, trackView);
+ }
+ }
+
+ private void switchToFullScreenWindow(UserTrackView userTrackView) {
+ if (userTrackView == mTrackFullScreenWin) {
+ return;
+ }
+
+ UserTrackView.swap(mEngine, mTrackFullScreenWin, userTrackView);
+ if (mTrackFullScreenWin.isTaken()) {
+ Logging.d(TAG, "put " + mTrackFullScreenWin.getUserId() + " to " + mTrackFullScreenWin.getResourceName());
+ mTrackFullScreenWin.setVisibility(View.VISIBLE);
+ mUserWindowMap.put(mTrackFullScreenWin.getUserId(), mTrackFullScreenWin, mTrackFullScreenWin.getUserId().equals(mCurrentUserId));
+ } else {
+ Logging.d(TAG, "recycle " + mTrackFullScreenWin.getResourceName());
+ mTrackFullScreenWin.reset();
+ mTrackFullScreenWin.setVisibility(View.GONE);
+ }
+
+ if (userTrackView.isTaken()) {
+ Logging.d(TAG, "put " + userTrackView.getUserId() + " to " + userTrackView.getResourceName());
+ userTrackView.setVisibility(View.VISIBLE);
+ mUserWindowMap.put(userTrackView.getUserId(), userTrackView, userTrackView.getUserId().equals(mCurrentUserId));
+ } else {
+ Logging.d(TAG, "recycle " + userTrackView.getResourceName());
+ userTrackView.reset();
+ userTrackView.setVisibility(View.GONE);
+ // recycle
+ mTrackCandidateWins.add(userTrackView);
+ }
+ }
+
+ private void setTrackUserWindowsVisibility(int visibility) {
+ List userWindows = mUserWindowMap.getOrderedValues(mTrackFullScreenWin);
+ for (int i = 0; i < userWindows.size(); i++) {
+ UserTrackView trackView = userWindows.get(i);
+ trackView.setVisibility(visibility);
+ }
+ }
+
+ private void updateTrackWindowMode(boolean trackWindowP2PMode) {
+ if (mUserWindowMap.isEmpty()) {
+ return;
+ }
+ if (mTrackWindowP2PMode != null && mTrackWindowP2PMode == trackWindowP2PMode) {
+ return;
+ }
+ if (trackWindowP2PMode) {
+ Logging.d(TAG, "switch to p2p mode");
+ // relayout. switch to p2p mode. put first user to full screen.
+ // ( 0 user -> 1 user || 3 users -> 2 users)
+ switchToFullScreenWindow(mUserWindowMap.getOrderedValues().get(0));
+ } else {
+ Logging.d(TAG, "switch to multi user mode");
+ // relayout. switch to multi user mode.
+ // (2 users -> 3 users)
+ if (mTrackFullScreenWin.isTaken()) {
+ if (!mTrackCandidateWins.isEmpty()) {
+ UserTrackView userTrackView = mTrackCandidateWins.remove(0);
+ switchToFullScreenWindow(userTrackView);
+ userTrackView.changeViewBackgroundByPos(mUserWindowMap.size());
+ }
+ }
+ }
+ mTrackWindowP2PMode = trackWindowP2PMode;
+ }
+
+ private void setTargetWindowParams(final int userCount, final int targetPos, final UserTrackView targetWindow) {
+ switch (userCount) {
+ case 1:
+ if (targetPos == 0) {
+ updateLayoutParams(targetWindow, (int) (120 * mDensity + 0.5f), (int) (160 * mDensity + 0.5f), 0, 0, Gravity.TOP | Gravity.END);
+ }
+ break;
+ case 2:
+ // never in this case.
+ break;
+ case 3:
+ if (targetPos == 0) {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, 0, 0, -1);
+ } else if (targetPos == 1) {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, 0, -1);
+ } else {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, 0, mScreenWidth / 2, Gravity.CENTER_HORIZONTAL);
+ }
+ break;
+ case 4:
+ if (targetPos == 0) {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, 0, 0, -1);
+ } else if (targetPos == 1) {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, 0, -1);
+ } else if (targetPos == 2) {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, 0, mScreenWidth / 2, Gravity.START);
+ } else {
+ updateLayoutParams(targetWindow, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, mScreenWidth / 2, -1);
+ }
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ if (targetPos == 0) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, 0, 0, -1);
+ } else if (targetPos == 1) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, 0, -1);
+ } else if (targetPos == 2) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, 0, Gravity.END);
+ } else if (targetPos == 3) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, 0, mScreenWidth / 3, -1);
+ } else if (targetPos == 4) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, -1);
+ } else if (targetPos == 5) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, mScreenWidth / 3, -1);
+ } else if (targetPos == 6) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, 0, mScreenWidth * 2 / 3, -1);
+ } else if (targetPos == 7) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, -1);
+ } else if (targetPos == 8) {
+ updateLayoutParams(targetWindow, mScreenWidth / 3, mScreenWidth / 3, mScreenWidth * 2 / 3, mScreenWidth * 2 / 3, -1);
+ }
+ break;
+ }
+ }
+
+ private void updateLayoutParams(UserTrackView targetView, int width, int height, int marginStart, int marginTop, int gravity) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) targetView.getLayoutParams();
+ lp.width = width;
+ lp.height = height;
+ lp.topMargin = marginTop;
+ lp.gravity = gravity;
+ lp.setMarginStart(marginStart);
+ targetView.setLayoutParams(lp);
+ }
+}
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_beauty.so b/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_beauty.so
index e96c20a..d96ac6a 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_beauty.so and b/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_beauty.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_rtc.so
index 0c1ed9a..11fe11e 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_rtc.so and b/QNDroidRTCDemo/app/src/main/jniLibs/arm64-v8a/libqndroid_rtc.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_beauty.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_beauty.so
index b195e19..3751d45 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_beauty.so and b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_beauty.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_rtc.so
index bd14e0f..abf1bce 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_rtc.so and b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_rtc.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so
index 3001ae6..dedba51 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so and b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so
index 263ced1..eb58c78 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so and b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_beauty.so b/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_beauty.so
index fc5c8a2..5ac2bbf 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_beauty.so and b/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_beauty.so differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_rtc.so
index 23765ed..9785e4f 100755
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_rtc.so and b/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_rtc.so differ
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/activity_main.xml b/QNDroidRTCDemo/app/src/main/res/layout/activity_main.xml
index 040a3ee..3bbc426 100644
--- a/QNDroidRTCDemo/app/src/main/res/layout/activity_main.xml
+++ b/QNDroidRTCDemo/app/src/main/res/layout/activity_main.xml
@@ -1,14 +1,14 @@
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/backgroundColor">
@@ -16,30 +16,30 @@
android:id="@+id/setting_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@mipmap/setting"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true"
+ android:layout_margin="16dp"
android:background="@color/backgroundColor"
android:onClick="onClickToSetting"
- android:layout_margin="16dp"
- android:layout_alignParentTop="true"
- android:layout_alignParentEnd="true" />
+ android:src="@mipmap/setting" />
+ android:textColor="@color/textColor"
+ android:textColorHint="@color/textColor"
+ android:textSize="15sp" />
+ android:paddingEnd="15dp"
+ android:text="@string/room_tips"
+ android:textColor="@color/textColor" />
-
+ android:paddingTop="2dp"
+ android:paddingEnd="0dp"
+ android:paddingBottom="2dp">
@@ -96,7 +95,16 @@
android:text="@string/screen_capture"
android:textColor="@color/textColor" />
-
+
+
+
+ android:orientation="horizontal">
+
+ android:layout_margin="8dp"
+ android:background="@color/white" />
+
+ android:layout_margin="8dp"
+ android:layout_toStartOf="@id/dividing_line"
+ android:src="@mipmap/ic_niu_logo" />
+
+ android:textColor="@color/white" />
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/activity_room.xml b/QNDroidRTCDemo/app/src/main/res/layout/activity_muti_track_room.xml
similarity index 55%
rename from QNDroidRTCDemo/app/src/main/res/layout/activity_room.xml
rename to QNDroidRTCDemo/app/src/main/res/layout/activity_muti_track_room.xml
index 42b6ede..05c1237 100644
--- a/QNDroidRTCDemo/app/src/main/res/layout/activity_room.xml
+++ b/QNDroidRTCDemo/app/src/main/res/layout/activity_muti_track_room.xml
@@ -1,55 +1,59 @@
-
-
-
+
+
-
-
-
-
-
-
-
@@ -59,4 +63,5 @@
android:layout_height="wrap_content"
android:layout_gravity="center|bottom"
android:paddingBottom="32dp" />
+
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/activity_screen_capture.xml b/QNDroidRTCDemo/app/src/main/res/layout/activity_screen_capture.xml
deleted file mode 100644
index 31d5394..0000000
--- a/QNDroidRTCDemo/app/src/main/res/layout/activity_screen_capture.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml b/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml
index 1a0ef32..39cbd74 100644
--- a/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml
+++ b/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml
@@ -104,7 +104,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:shadowColor="@color/blue"
- android:text="@string/sw"
+ android:text="@string/codec_sw"
android:textColor="@color/textColor" />
+
+
+
+
+
+
+
@@ -122,7 +156,7 @@
android:layout_height="wrap_content"
android:layout_alignEnd="@id/user_name_edit_text"
android:layout_alignStart="@id/user_name_edit_text"
- android:layout_below="@id/codec_mode_button"
+ android:layout_below="@id/maintain_resolution_button"
android:layout_marginTop="15dp"
android:paddingEnd="15dp"
android:paddingStart="15dp"
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/fragment_room.xml b/QNDroidRTCDemo/app/src/main/res/layout/fragment_room.xml
index 7f922e5..6b28ee4 100644
--- a/QNDroidRTCDemo/app/src/main/res/layout/fragment_room.xml
+++ b/QNDroidRTCDemo/app/src/main/res/layout/fragment_room.xml
@@ -25,9 +25,17 @@
android:background="@color/editTextBackground"
android:visibility="gone">
+
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/remote_video_view.xml b/QNDroidRTCDemo/app/src/main/res/layout/remote_video_view.xml
deleted file mode 100644
index cc8c93b..0000000
--- a/QNDroidRTCDemo/app/src/main/res/layout/remote_video_view.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/track_local_view.xml b/QNDroidRTCDemo/app/src/main/res/layout/track_local_view.xml
new file mode 100644
index 0000000..388dba4
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/res/layout/track_local_view.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/track_remote_view.xml b/QNDroidRTCDemo/app/src/main/res/layout/track_remote_view.xml
new file mode 100644
index 0000000..27b8177
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/res/layout/track_remote_view.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/user_tracks_view.xml b/QNDroidRTCDemo/app/src/main/res/layout/user_tracks_view.xml
new file mode 100644
index 0000000..cacc15f
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/res/layout/user_tracks_view.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/user_tracks_view_full_screen.xml b/QNDroidRTCDemo/app/src/main/res/layout/user_tracks_view_full_screen.xml
new file mode 100644
index 0000000..789e7c1
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/res/layout/user_tracks_view_full_screen.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QNDroidRTCDemo/app/src/main/res/values/strings.xml b/QNDroidRTCDemo/app/src/main/res/values/strings.xml
index 2873399..6e760be 100644
--- a/QNDroidRTCDemo/app/src/main/res/values/strings.xml
+++ b/QNDroidRTCDemo/app/src/main/res/values/strings.xml
@@ -6,7 +6,7 @@
正在连接: %1$s
连接成功
正在重连……
- room token 不能为空!!!
+ 获取 room token 失败,请检查网络
房间名称
会议房间
如果没有该房间,则会自动创建,房间名仅支持 3 ~ 64 位字母、数字、_ 和 - 的组合
@@ -17,8 +17,10 @@
昵称仅支持 3 ~ 50 位字母、数字、_ 和 - 的组合
非法昵称,仅支持 3 ~ 50 位字母、数字、_ 和 - 的组合 !
昵称不能为空!!!
- 硬编
- 软编
+ 硬编
+ 软编
+ 分辨率固定
+ 分辨率自适应
您被管理员踢出房间!!!
版本号:%1$s, 编译时间:%2$s
userId: %1$s\nfps: %2$d\nvideo bitrate: %3$d kbps\naudio bitrate: %4$d kbps\nvideo packet loss rate: %5$d%%\naudio packet loss rate: %6$d%%\nresolution: %7$d x %8$d
@@ -33,6 +35,7 @@
屏幕采集
视频通话
音频通话
+ 视频通话 + 屏幕共享
屏幕采集要求 Android 5.0+ 以及硬编
直播房间
只有 admin 才有合流权限
diff --git a/QNDroidRTCDemo/build.gradle b/QNDroidRTCDemo/build.gradle
index f209f27..017e531 100644
--- a/QNDroidRTCDemo/build.gradle
+++ b/QNDroidRTCDemo/build.gradle
@@ -9,7 +9,7 @@ buildscript {
}
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.1'
+ classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -29,3 +29,7 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
}
+
+ext {
+ buildWithQNDroidRTCLibrary = false
+}
\ No newline at end of file
diff --git a/QNDroidRTCDemo/gradle/wrapper/gradle-wrapper.properties b/QNDroidRTCDemo/gradle/wrapper/gradle-wrapper.properties
index 9cd37a2..8202ae6 100644
--- a/QNDroidRTCDemo/gradle/wrapper/gradle-wrapper.properties
+++ b/QNDroidRTCDemo/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Sep 20 16:15:39 CST 2018
+#Thu Nov 01 11:52:52 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/README.md b/README.md
index 8981fc7..2bfb143 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,9 @@ QNDroidRTC 是七牛云推出的一款适用于 Android 平台的实时音视频
- 支持大小窗口切换功能
- 支持外部导入音视频数据
- 支持摄像头 YUV 数据的回调
+- 支持自动订阅功能
+- 支持本地发布多路视频
+- 支持音频与视频分开发布
# 3 方案介绍
@@ -59,11 +62,11 @@ QNDroidRTC 是七牛云推出的一款适用于 Android 平台的实时音视频
- 计算加入房间的 roomToken 并提供给 App,该 roomToken 是结合 userId、roomName 等信息使用七牛的 AccessKey 和 SecretKey 按照一定的规则生成
- 提供通话的业务逻辑,如:通话请求/应答业务逻辑、服务端房间管理和踢人等
-关于 roomToken 的计算方法请查阅[《七牛实时音视频云服务端 API 接口规范》](https://developer.qiniu.com/rtn/sdk/4538/server-api-reference#5),另外,我们也提供了多种开发语言的 SDK [服务端开发手册及 SDK 下载](https://developer.qiniu.com/rtn/sdk/4354/the-server-sdk-development-manual)。
+关于 roomToken 的计算方法请查阅[《七牛实时音视频云服务端 API 接口规范》](https://doc.qnsdk.com/rtn/docs/server_overview#1),另外,我们也提供了多种开发语言的 SDK [服务端开发手册及 SDK 下载](https://doc.qnsdk.com/rtn/docs/server_sdk)。
### 3.3 房间管理
-关于音视频通话房间的 API 主要分为两个部分,一部分在客户端,另一部分在服务端。在客户端 SDK 中,只有加入/离开连麦房间的接口。我们把创建/销毁连麦房间的功能放到了服务端,由 App Server 向七牛的服务器发送请求来完成。关于服务端 API 的详细内容,请查阅[《七牛实时音视频云服务端 API 接口规范》](https://developer.qiniu.com/rtn/sdk/4538/server-api-reference)。
+关于音视频通话房间的 API 主要分为两个部分,一部分在客户端,另一部分在服务端。在客户端 SDK 中,只有加入/离开连麦房间的接口。我们把创建/销毁连麦房间的功能放到了服务端,由 App Server 向七牛的服务器发送请求来完成。关于服务端 API 的详细内容,请查阅[《七牛实时音视频云服务端 API 接口规范》](https://doc.qnsdk.com/rtn/docs/server_overview)。
# 4 方案优势
@@ -118,7 +121,7 @@ QNDroidRTC 是七牛云推出的一款适用于 Android 平台的实时音视频
- 提供云端存储空间及海量数据的处理能力,提供的高可用的技术和高稳定的平台
# 6 开发文档
-请参考开发文档:[QNDroidRTC 开发文档](https://developer.qiniu.com/rtn/sdk/4351/summary-of-the-android-sdk)
+请参考开发文档:[QNDroidRTC 开发文档](https://doc.qnsdk.com/rtn/android)
# 7 反馈及意见
@@ -143,5 +146,5 @@ QNDroidRTC 是七牛云推出的一款适用于 Android 平台的实时音视频
## 8.4 是否有服务端的 SDK 或者 demo 代码可以参考?
-有的,请参考: [QNRTC-Server](https://developer.qiniu.com/rtn/sdk/4354/the-server-sdk-development-manual)
+有的,请参考: [QNRTC-Server](https://doc.qnsdk.com/rtn/docs/server_sdk)
diff --git a/ReleaseNotes/release-notes-2.0.0.md b/ReleaseNotes/release-notes-2.0.0.md
new file mode 100644
index 0000000..e1b79fd
--- /dev/null
+++ b/ReleaseNotes/release-notes-2.0.0.md
@@ -0,0 +1,34 @@
+# QNDroidRTC Release Notes for 2.0.0
+
+## 简介
+
+QNDroidRTC 是七牛推出的一款适用于 Android 平台的音视频通话 SDK,提供了包括美颜、滤镜、水印、音视频通话等多种功能,提供灵活的接口,支持高度定制以及二次开发。
+
+## 版本
+
+- 发布 qndroid-rtc-2.0.0.jar
+- 更新 libqndroid_rtc.so
+- 更新 libqndroid_beauty.so
+
+## 注意
+
+本次升级为主版本升级(1.2.0 -> 2.0.0),为了支持更灵活的连麦控制和更低的资源开销有比较大的重构。请查看我们的[新版文档站](https://doc.qnsdk.com/rtn/android/)。
+
+自 2.0.0 后,我们为了提高用户阅读文档的体验,使用了新的文档站(老文档地址继续保留)。新文档站地址 https://doc.qnsdk.com/rtn
+
+## 功能
+
+- 新增核心类 QNRTCEngine,支持本地发布多路视频,支持音频与视频分开发布
+- 新增自动订阅功能
+
+## 优化
+
+- 内部优化,使用 QNRTCEngine 能够达到更低的功耗和更低的内存占用
+
+## 注意事项
+
+- 2.0.0 之前的部分接口我们已标记为废弃,但不影响使用,我们不推荐继续使用 QNRTCManager 内的接口。建议将 QNRTCManager 升级至 QNRTCEngine,获得更好的体验。
+
+## 问题反馈
+
+当你遇到任何问题时,可以通过在 GitHub 的 repo 提交 `issues` 来反馈问题,请尽可能的描述清楚遇到的问题,如果有错误信息也一同附带,并且在 ```Labels``` 中指明类型为 bug 或者其他。 [通过这里查看已有的 issues 和提交 bug](https://github.com/pili-engineering/QNRTC-Android/issues)
diff --git a/releases/arm64-v8a/libqndroid_beauty.so b/releases/arm64-v8a/libqndroid_beauty.so
index e96c20a..d96ac6a 100755
Binary files a/releases/arm64-v8a/libqndroid_beauty.so and b/releases/arm64-v8a/libqndroid_beauty.so differ
diff --git a/releases/arm64-v8a/libqndroid_rtc.so b/releases/arm64-v8a/libqndroid_rtc.so
index 0c1ed9a..11fe11e 100755
Binary files a/releases/arm64-v8a/libqndroid_rtc.so and b/releases/arm64-v8a/libqndroid_rtc.so differ
diff --git a/releases/armeabi-v7a/libqndroid_beauty.so b/releases/armeabi-v7a/libqndroid_beauty.so
index b195e19..3751d45 100755
Binary files a/releases/armeabi-v7a/libqndroid_beauty.so and b/releases/armeabi-v7a/libqndroid_beauty.so differ
diff --git a/releases/armeabi-v7a/libqndroid_rtc.so b/releases/armeabi-v7a/libqndroid_rtc.so
index bd14e0f..abf1bce 100755
Binary files a/releases/armeabi-v7a/libqndroid_rtc.so and b/releases/armeabi-v7a/libqndroid_rtc.so differ
diff --git a/releases/armeabi/libqndroid_beauty.so b/releases/armeabi/libqndroid_beauty.so
index 3001ae6..dedba51 100755
Binary files a/releases/armeabi/libqndroid_beauty.so and b/releases/armeabi/libqndroid_beauty.so differ
diff --git a/releases/armeabi/libqndroid_rtc.so b/releases/armeabi/libqndroid_rtc.so
index 263ced1..eb58c78 100755
Binary files a/releases/armeabi/libqndroid_rtc.so and b/releases/armeabi/libqndroid_rtc.so differ
diff --git a/releases/qndroid-rtc-1.2.0.jar b/releases/qndroid-rtc-1.2.0.jar
deleted file mode 100644
index 005b579..0000000
Binary files a/releases/qndroid-rtc-1.2.0.jar and /dev/null differ
diff --git a/releases/qndroid-rtc-2.0.0.jar b/releases/qndroid-rtc-2.0.0.jar
new file mode 100644
index 0000000..7bddf83
Binary files /dev/null and b/releases/qndroid-rtc-2.0.0.jar differ
diff --git a/releases/x86/libqndroid_beauty.so b/releases/x86/libqndroid_beauty.so
index fc5c8a2..5ac2bbf 100755
Binary files a/releases/x86/libqndroid_beauty.so and b/releases/x86/libqndroid_beauty.so differ
diff --git a/releases/x86/libqndroid_rtc.so b/releases/x86/libqndroid_rtc.so
index 23765ed..9785e4f 100755
Binary files a/releases/x86/libqndroid_rtc.so and b/releases/x86/libqndroid_rtc.so differ