diff --git a/QNDroidRTCDemo/app/build.gradle b/QNDroidRTCDemo/app/build.gradle
index 380b49a..5c8deb2 100644
--- a/QNDroidRTCDemo/app/build.gradle
+++ b/QNDroidRTCDemo/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ compileSdkVersion 29
defaultConfig {
applicationId "com.qiniu.droid.rtc.demo"
minSdkVersion 18
- targetSdkVersion 28
- versionCode 31
- versionName "3.0.2"
+ targetSdkVersion 29
+ versionCode 32
+ versionName "3.1.0"
buildConfigField "long", "BUILD_TIMESTAMP", System.currentTimeMillis() + "L"
}
diff --git a/QNDroidRTCDemo/app/libs/qndroid-rtc-3.0.2.jar b/QNDroidRTCDemo/app/libs/qndroid-rtc-3.1.0.jar
similarity index 55%
rename from QNDroidRTCDemo/app/libs/qndroid-rtc-3.0.2.jar
rename to QNDroidRTCDemo/app/libs/qndroid-rtc-3.1.0.jar
index fecba5a..cd09601 100644
Binary files a/QNDroidRTCDemo/app/libs/qndroid-rtc-3.0.2.jar and b/QNDroidRTCDemo/app/libs/qndroid-rtc-3.1.0.jar differ
diff --git a/QNDroidRTCDemo/app/src/main/AndroidManifest.xml b/QNDroidRTCDemo/app/src/main/AndroidManifest.xml
index 09a7ad6..231dbb3 100644
--- a/QNDroidRTCDemo/app/src/main/AndroidManifest.xml
+++ b/QNDroidRTCDemo/app/src/main/AndroidManifest.xml
@@ -11,9 +11,9 @@
-
+
+
+
+
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 8d92fc0..9931509 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,7 +16,7 @@ public void onCreate() {
* init must be called before any other func
*/
QNRTCEnv.init(getApplicationContext());
- QNRTCEnv.setLogFileEnabled(true);
+ QNRTCEnv.setLogFileEnabled(true, "牛会议");
// 设置自定义 DNS manager,不设置则使用 SDK 默认 DNS 服务
new Thread(() -> QNRTCEnv.setDnsManager(Utils.getDefaultDnsManager(getApplicationContext()))).start();
}
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 f956207..8040bf2 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
@@ -4,9 +4,11 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
@@ -14,6 +16,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
@@ -48,6 +51,7 @@
import com.qiniu.droid.rtc.demo.model.RTCRoomUsersMergeOption;
import com.qiniu.droid.rtc.demo.model.RTCTrackMergeOption;
import com.qiniu.droid.rtc.demo.model.RTCUserMergeOptions;
+import com.qiniu.droid.rtc.demo.service.ForegroundService;
import com.qiniu.droid.rtc.demo.ui.CircleTextView;
import com.qiniu.droid.rtc.demo.ui.MergeLayoutConfigView;
import com.qiniu.droid.rtc.demo.ui.UserTrackView;
@@ -65,13 +69,13 @@
import org.webrtc.Size;
import org.webrtc.VideoFrame;
-import java.util.concurrent.Semaphore;
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.Semaphore;
import java.util.concurrent.TimeUnit;
import static com.qiniu.droid.rtc.demo.utils.Config.DEFAULT_BITRATE;
@@ -269,7 +273,9 @@ public boolean onLongClick(View v) {
protected void onResume() {
super.onResume();
// 开始视频采集
- startCaptureAfterAcquire();
+ if (mCaptureMode == Config.CAMERA_CAPTURE || mCaptureMode == Config.MUTI_TRACK_CAPTURE) {
+ startCaptureAfterAcquire();
+ }
if (!mIsJoinedRoom) {
// 加入房间
mEngine.joinRoom(mRoomToken);
@@ -292,7 +298,9 @@ private void startCaptureAfterAcquire() {
protected void onPause() {
super.onPause();
// 停止视频采集
- mEngine.stopCapture();
+ if (mCaptureMode == Config.CAMERA_CAPTURE || mCaptureMode == Config.MUTI_TRACK_CAPTURE) {
+ mEngine.stopCapture();
+ }
if (mPopWindow != null && mPopWindow.isShowing()) {
mPopWindow.dismiss();
}
@@ -340,12 +348,18 @@ private void initQNRTCEngine() {
* 如果打开分辨率保持开关,则只会调整帧率来适应网络波动。
*/
boolean isMaintainRes = preferences.getBoolean(Config.MAINTAIN_RES, false);
+
+ /**
+ * 如果您的使用场景需要双讲,建议按照默认设置,保持 QNRTCSetting#setLowAudioSampleRateEnabled
+ * 和 QNRTCSetting#setAEC3Enabled 为 true 以防止出现对讲回声
+ */
boolean isLowSampleRateEnabled = preferences.getInt(Config.SAMPLE_RATE, Config.HIGH_SAMPLE_RATE) == Config.LOW_SAMPLE_RATE;
- boolean isAec3Enabled = preferences.getBoolean(Config.AEC3_ENABLE, false);
+ boolean isAec3Enabled = preferences.getBoolean(Config.AEC3_ENABLE, true);
mCaptureMode = preferences.getInt(Config.CAPTURE_MODE, Config.CAMERA_CAPTURE);
- // 1. VideoPreviewFormat 和 VideoEncodeFormat 建议保持一致
- // 2. 如果远端连麦出现回声的现象,可以通过配置 setLowAudioSampleRateEnabled(true) 和 setAEC3Enabled(true) 后再做进一步测试,并将设备信息反馈给七牛技术支持
+ /**
+ * VideoPreviewFormat 和 VideoEncodeFormat 建议保持一致
+ */
QNVideoFormat format = new QNVideoFormat(videoWidth, videoHeight, fps);
QNRTCSetting setting = new QNRTCSetting();
setting.setCameraID(QNRTCSetting.CAMERA_FACING_ID.FRONT)
@@ -454,7 +468,6 @@ public int onEncrypt(ByteBuffer frame, int frameSize, ByteBuffer encryptedFrame)
});
mLocalTrackList.add(mLocalAudioTrack);
- QNVideoFormat screenEncodeFormat = new QNVideoFormat(mScreenWidth/2, mScreenHeight/2, 15);
switch (mCaptureMode) {
case Config.CAMERA_CAPTURE:
// 创建 Camera 采集的视频 Track
@@ -469,23 +482,13 @@ public int onEncrypt(ByteBuffer frame, int frameSize, ByteBuffer encryptedFrame)
break;
case Config.SCREEN_CAPTURE:
// 创建屏幕录制的视频 Track
- mLocalScreenTrack = mEngine.createTrackInfoBuilder()
- .setVideoPreviewFormat(screenEncodeFormat)
- .setBitrate(BITRATE_FOR_SCREEN_VIDEO)
- .setSourceType(QNSourceType.VIDEO_SCREEN)
- .setMaster(true)
- .setTag(UserTrackView.TAG_SCREEN).create();
+ createScreenTrack();
mLocalTrackList.add(mLocalScreenTrack);
mControlFragment.setAudioOnly(true);
break;
case Config.MUTI_TRACK_CAPTURE:
// 视频通话 + 屏幕共享两路 track
- mLocalScreenTrack = mEngine.createTrackInfoBuilder()
- .setSourceType(QNSourceType.VIDEO_SCREEN)
- .setVideoPreviewFormat(screenEncodeFormat)
- .setBitrate(BITRATE_FOR_SCREEN_VIDEO)
- .setMaster(true)
- .setTag(UserTrackView.TAG_SCREEN).create();
+ createScreenTrack();
mLocalVideoTrack = mEngine.createTrackInfoBuilder()
.setSourceType(QNSourceType.VIDEO_CAMERA)
.setTag(UserTrackView.TAG_CAMERA).create();
@@ -495,6 +498,47 @@ public int onEncrypt(ByteBuffer frame, int frameSize, ByteBuffer encryptedFrame)
}
}
+
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ QNVideoFormat screenEncodeFormat = new QNVideoFormat(mScreenWidth / 2, mScreenHeight / 2, 15);
+ mLocalScreenTrack = mEngine.createTrackInfoBuilder()
+ .setSourceType(QNSourceType.VIDEO_SCREEN)
+ .setVideoPreviewFormat(screenEncodeFormat)
+ .setBitrate(BITRATE_FOR_SCREEN_VIDEO)
+ .setMaster(true)
+ .setTag(UserTrackView.TAG_SCREEN).create();
+ mLocalTrackList.add(mLocalScreenTrack);
+ if (mEngine.getRoomState().equals(QNRoomState.CONNECTED) || mEngine.getRoomState().equals(QNRoomState.RECONNECTED)) {
+ mEngine.publishTracks(Collections.singletonList(mLocalScreenTrack));
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+
+ }
+ };
+
+ // 处理 Build.VERSION_CODES.Q 及以上的兼容问题
+ private void createScreenTrack() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Intent intent = new Intent(this, ForegroundService.class);
+ startForegroundService(intent);
+ bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ Log.i(TAG, "start service for Q");
+ } else {
+ QNVideoFormat screenEncodeFormat = new QNVideoFormat(mScreenWidth / 2, mScreenHeight / 2, 15);
+ mLocalScreenTrack = mEngine.createTrackInfoBuilder()
+ .setSourceType(QNSourceType.VIDEO_SCREEN)
+ .setVideoPreviewFormat(screenEncodeFormat)
+ .setBitrate(BITRATE_FOR_SCREEN_VIDEO)
+ .setMaster(true)
+ .setTag(UserTrackView.TAG_SCREEN).create();
+ }
+ }
+
/**
* 合流转推、单路转推相关处理
*/
@@ -507,6 +551,9 @@ private void initMergeLayoutConfig() {
mMergeLayoutConfigView.setOnClickedListener(new MergeLayoutConfigView.OnClickedListener() {
@Override
public void onConfirmClicked() {
+ // 保存当前用户选择的配置信息
+ mMergeLayoutConfigView.updateMergeOptions();
+
if (mEngine == null) {
return;
}
@@ -546,8 +593,12 @@ public void onConfirmClicked() {
mCurrentMergeJob = mergeJob;
// 创建自定义合流任务
mEngine.createMergeJob(mCurrentMergeJob);
+ } else {
+ // 更新合流布局到自定义合流任务
+ setMergeStreamLayouts();
}
} else {
+ // 更新合流布局到默认合流任务
setMergeStreamLayouts();
}
if (mPopWindow != null) {
@@ -613,23 +664,26 @@ private void updateRemoteLogText(final String logText) {
mControlFragment.updateRemoteLogText(logText);
}
+ /**
+ * 当新的本地、远端 Track 变化时,重新排列合流画面配置
+ */
private void resetMergeStream() {
Log.d(TAG, "resetMergeStream()");
List configuredMergeTracksOptions = new ArrayList<>();
// video tracks merge layout options.
- List remoteVideoTrackInfoList = mRoomUsersMergeOption.getRTCVideoMergeOptions();
- if (!remoteVideoTrackInfoList.isEmpty()) {
- List mergeTrackOptions = SplitUtils.split(remoteVideoTrackInfoList.size(),
+ List roomVideoTrackInfoList = mRoomUsersMergeOption.getRTCVideoMergeOptions();
+ if (!roomVideoTrackInfoList.isEmpty()) {
+ List mergeTrackOptions = SplitUtils.split(roomVideoTrackInfoList.size(),
mCurrentMergeJob == null ? QNAppServer.STREAMING_WIDTH : mCurrentMergeJob.getWidth(),
mCurrentMergeJob == null ? QNAppServer.STREAMING_HEIGHT : mCurrentMergeJob.getHeight());
- if (mergeTrackOptions.size() != remoteVideoTrackInfoList.size()) {
+ if (mergeTrackOptions.size() != roomVideoTrackInfoList.size()) {
Log.e(TAG, "split option error.");
return;
}
for (int i = 0; i < mergeTrackOptions.size(); i++) {
- RTCTrackMergeOption trackMergeOption = remoteVideoTrackInfoList.get(i);
+ RTCTrackMergeOption trackMergeOption = roomVideoTrackInfoList.get(i);
if (!trackMergeOption.isTrackInclude()) {
continue;
@@ -641,9 +695,9 @@ private void resetMergeStream() {
}
// audio tracks merge layout options
- List remoteAudioTrackInfoList = mRoomUsersMergeOption.getRTCAudioTracks();
- if (!remoteAudioTrackInfoList.isEmpty()) {
- for (RTCTrackMergeOption trackMergeOption : remoteAudioTrackInfoList) {
+ List roomAudioTrackInfoList = mRoomUsersMergeOption.getRTCAudioTracks();
+ if (!roomAudioTrackInfoList.isEmpty()) {
+ for (RTCTrackMergeOption trackMergeOption : roomAudioTrackInfoList) {
if (!trackMergeOption.isTrackInclude()) {
continue;
}
@@ -663,8 +717,12 @@ private void userJoinedForStreaming(String userId, String userData) {
}
}
- private void userLeftForStreaming(String userId) {
- mRoomUsersMergeOption.onUserLeft(userId);
+ private void userLeftForStreaming(String userId, boolean localLeft) {
+ if (localLeft) {
+ mRoomUsersMergeOption.onUserLeft();
+ } else {
+ mRoomUsersMergeOption.onUserLeft(userId);
+ }
if (mUserListAdapter != null) {
mUserListAdapter.notifyDataSetChanged();
}
@@ -676,13 +734,22 @@ private int updateSerialNum() {
}
/**
- * 配置合流的布局信息
+ * 配置各个用户当前选中的 Track 信息到合流布局
*
* 如果使用的默认合流任务,则无需手动 createMergeJob,setMergeStreamLayouts 中 jobId 参数传 null 即可
*/
private void setMergeStreamLayouts() {
- // 配置合流布局信息
- List userTracks = mMergeLayoutConfigView.updateMergeOptions();
+ List userTracks = new ArrayList<>();
+ for (int user = 0; user < mRoomUsersMergeOption.size(); user++) {
+ RTCUserMergeOptions userMergeOptions = mRoomUsersMergeOption.getRoomUserByPosition(user);
+ if (userMergeOptions.getAudioTrack() != null) {
+ userTracks.add(userMergeOptions.getAudioTrack());
+ }
+ if (userMergeOptions.getVideoTracks().size() > 0) {
+ userTracks.addAll(userMergeOptions.getVideoTracks());
+ }
+ }
+
List addedTrackOptions = new ArrayList<>();
List removedTrackOptions = new ArrayList<>();
for (RTCTrackMergeOption item : userTracks) {
@@ -728,7 +795,7 @@ public void onRoomStateChanged(QNRoomState state) {
switch (state) {
case IDLE:
if (mIsAdmin) {
- userLeftForStreaming(mUserId);
+ userLeftForStreaming(mUserId, true);
}
break;
case RECONNECTING:
@@ -744,6 +811,16 @@ public void onRoomStateChanged(QNRoomState state) {
logAndToast(getString(R.string.connected_to_room));
mIsJoinedRoom = true;
mControlFragment.startTimer();
+
+ // 重连失败后再次加入房间后,恢复无效的合流任务
+ if (mIsMergeJobStreaming && mMergeLayoutConfigView.isCustomMergeJob() && !mMergeLayoutConfigView.isMergeJobValid()) {
+ QNMergeJob mergeJob = mMergeLayoutConfigView.getCustomMergeJob();
+ if (mergeJob != null) {
+ mCurrentMergeJob = mergeJob;
+ // 创建自定义合流任务
+ mEngine.createMergeJob(mCurrentMergeJob);
+ }
+ }
break;
case RECONNECTED:
logAndToast(getString(R.string.connected_to_room));
@@ -797,7 +874,7 @@ public void onRemoteUserReconnected(String remoteUserId) {
public void onRemoteUserLeft(final String remoteUserId) {
updateRemoteLogText("onRemoteUserLeft:remoteUserId = " + remoteUserId);
if (mIsAdmin) {
- userLeftForStreaming(remoteUserId);
+ userLeftForStreaming(remoteUserId, false);
}
}
@@ -1043,6 +1120,7 @@ public void onCreateForwardJobSuccess(String forwardJobId) {
mEngine.stopMergeStream(mCurrentMergeJob.getMergeJobId(), JOB_STOP_DELAY_TIME);
mIsMergeJobStreaming = false;
mMergeLayoutConfigView.updateStreamingStatus(false);
+ mMergeLayoutConfigView.updateMergeJobValid(false);
}
}
@@ -1076,6 +1154,7 @@ public void onError(int errorCode, String description) {
* 3 )请确认您的网络状况是否正常
* 3. QNErrorCode.ERROR_RECONNECT_TOKEN_ERROR 内部重连后出错,一般出现在网络非常不稳定时出现,建议提示用户并尝试重新加入房间;
+ * 另外,当前用户之前创建的合流任务、单路转推任务将会被服务销毁,重新加入房间后应该重新创建合流,单路转推任务 !!!
* 4. QNErrorCode.ERROR_INVALID_PARAMETER 服务交互参数错误,请在开发时注意合流、踢人动作等参数的设置。
* 5. QNErrorCode.ERROR_DEVICE_CAMERA 系统摄像头错误, 建议提醒用户检查
*/
@@ -1122,6 +1201,10 @@ public void onError(int errorCode, String description) {
mTrackWindowMgr.addTrackInfo(mUserId, localTrackListExcludeScreenTrack);
if (errorCode == QNErrorCode.ERROR_RECONNECT_TOKEN_ERROR) {
logAndToast("ERROR_RECONNECT_TOKEN_ERROR 即将重连,请注意网络质量!");
+ // 当重连超时后,用户创建的合流任务默认被销毁;需要重新创建合流任务
+ if (mIsMergeJobStreaming && mMergeLayoutConfigView.isCustomMergeJob()) {
+ mMergeLayoutConfigView.updateMergeJobValid(false);
+ }
}
if (errorCode == QNErrorCode.ERROR_AUTH_FAIL) {
logAndToast("ERROR_AUTH_FAIL 即将重连");
@@ -1291,7 +1374,7 @@ public void onToggleForwardJob() {
}
/**
- * 合流配置相关
+ * 用户合流配置相关
*/
private class UserListAdapter extends RecyclerView.Adapter {
int[] mColor = {
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 61b4907..3ed8888 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
@@ -20,6 +20,7 @@
import android.widget.Switch;
import android.widget.TextView;
+import com.qiniu.droid.rtc.QNFileLogHelper;
import com.qiniu.droid.rtc.demo.BuildConfig;
import com.qiniu.droid.rtc.demo.R;
import com.qiniu.droid.rtc.demo.ui.SpinnerPopupWindow;
@@ -38,6 +39,7 @@ public class SettingActivity extends AppCompatActivity {
private EditText mUserNameEditText;
private TextView mConfigTextView;
private TextView mVersionCodeTextView;
+ private TextView mUploadTextView;
private RadioGroup mCodecModeRadioGroup;
private RadioButton mHwCodecMode;
private RadioButton mSwCodecMode;
@@ -57,8 +59,10 @@ public class SettingActivity extends AppCompatActivity {
private boolean mMaintainResolution = false;
private boolean mIsAec3Enabled = false;
private List mDefaultConfiguration = new ArrayList<>();
- private ArrayAdapter mAdapter;
- private SpinnerPopupWindow mSpinnerPopupWindow;
+ private ArrayAdapter mConfigAdapter;
+ private SpinnerPopupWindow mConfigPopupWindow;
+ private List mLogFileNames;
+ private SpinnerPopupWindow mLogFilePopupWindow;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -71,6 +75,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
mUserNameEditText = (EditText) findViewById(R.id.user_name_edit_text);
mConfigTextView = (TextView) findViewById(R.id.config_text_view);
mVersionCodeTextView = (TextView) findViewById(R.id.version_code);
+ mUploadTextView = (TextView) findViewById(R.id.report_log);
mCodecModeRadioGroup = (RadioGroup) findViewById(R.id.codec_mode_button);
mCodecModeRadioGroup.setOnCheckedChangeListener(mOnCheckedChangeListener);
mHwCodecMode = (RadioButton) findViewById(R.id.hw_radio_button);
@@ -122,7 +127,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mSwCodecMode.setChecked(true);
}
- int sampleRatePos = preferences.getInt(Config.SAMPLE_RATE, Config.HIGH_SAMPLE_RATE);
+ int sampleRatePos = preferences.getInt(Config.SAMPLE_RATE, Config.LOW_SAMPLE_RATE);
if (sampleRatePos == Config.LOW_SAMPLE_RATE) {
mLowSampleRateBtn.setChecked(true);
} else {
@@ -136,13 +141,13 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mMaintainResolutionNo.setChecked(true);
}
- mIsAec3Enabled = preferences.getBoolean(Config.AEC3_ENABLE, false);
+ mIsAec3Enabled = preferences.getBoolean(Config.AEC3_ENABLE, true);
mAec3Switch.setChecked(mIsAec3Enabled);
- mSpinnerPopupWindow = new SpinnerPopupWindow(this);
- mSpinnerPopupWindow.setOnSpinnerItemClickListener(mOnSpinnerItemClickListener);
+ mConfigPopupWindow = new SpinnerPopupWindow(this);
+ mConfigPopupWindow.setOnSpinnerItemClickListener(mOnSpinnerItemClickListener);
- mAdapter = new ArrayAdapter(this, R.layout.spinner_item, mDefaultConfiguration);
+ mConfigAdapter = new ArrayAdapter(this, R.layout.spinner_item, mDefaultConfiguration);
}
public void onClickBack(View v) {
@@ -150,7 +155,11 @@ public void onClickBack(View v) {
}
public void onClickConfigParams(View v) {
- showPopupWindow();
+ showConfigPopupWindow();
+ }
+
+ public void onClickUploadLog(View v) {
+ showLogFilePopupWindow();
}
public void onClickSaveConfiguration(View v) {
@@ -216,10 +225,43 @@ private void saveTestMode(SharedPreferences.Editor editor) {
}
}
- private void showPopupWindow() {
- mSpinnerPopupWindow.setAdapter(mAdapter);
- mSpinnerPopupWindow.setWidth(mConfigTextView.getWidth());
- mSpinnerPopupWindow.showAsDropDown(mConfigTextView);
+ private void showConfigPopupWindow() {
+ mConfigPopupWindow.setAdapter(mConfigAdapter);
+ mConfigPopupWindow.setWidth(mConfigTextView.getWidth());
+ mConfigPopupWindow.showAsDropDown(mConfigTextView);
+ }
+
+ private void showLogFilePopupWindow() {
+ if (mLogFilePopupWindow == null) {
+ mLogFilePopupWindow = new SpinnerPopupWindow(this);
+ mLogFilePopupWindow.setOnSpinnerItemClickListener(new SpinnerPopupWindow.OnSpinnerItemClickListener() {
+ @Override
+ public void onItemClick(int pos) {
+ QNFileLogHelper.getInstance().reportLogFile(mLogFileNames.get(pos), new QNFileLogHelper.LogReportCallback() {
+ @Override
+ public void onReportSuccess(String name) {
+ ToastUtils.s(SettingActivity.this, "上传成功:" + name);
+ }
+
+ @Override
+ public void onReportError(String name, String errorMsg) {
+ ToastUtils.s(SettingActivity.this, "上传失败:" + name + ";" + errorMsg);
+ }
+ });
+ mLogFilePopupWindow.dismiss();
+ }
+ });
+ }
+ mLogFileNames = QNFileLogHelper.getInstance().getLogFiles();
+ if (mLogFileNames == null || mLogFileNames.size() == 0) {
+ ToastUtils.s(SettingActivity.this, "当前无可上报日志");
+ return;
+ }
+
+ ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.spinner_item, mLogFileNames);
+ mLogFilePopupWindow.setAdapter(adapter);
+ mLogFilePopupWindow.setWidth(mAppIdEditText.getWidth());
+ mLogFilePopupWindow.showAsDropDown(mUploadTextView);
}
private String getVersionDescription() {
@@ -253,7 +295,7 @@ private boolean isTestMode() {
public void onItemClick(int pos) {
mSelectPos = pos;
mConfigTextView.setText(mDefaultConfiguration.get(mSelectPos));
- mSpinnerPopupWindow.dismiss();
+ mConfigPopupWindow.dismiss();
}
};
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/model/RTCRoomUsersMergeOption.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/model/RTCRoomUsersMergeOption.java
index 6a82f89..1f9d8b1 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/model/RTCRoomUsersMergeOption.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/model/RTCRoomUsersMergeOption.java
@@ -56,6 +56,12 @@ public void onUserLeft(String userId) {
}
}
+ public void onUserLeft() {
+ mRTCVideoMergeOptions.clear();
+ mRTCUsers.clear();
+ mRTCUserMap.clear();
+ }
+
public void onTracksPublished(String userId, List trackInfoList) {
RTCUserMergeOptions userMergeOptions = getRoomUserByUserId(userId);
if (userMergeOptions == null) {
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/service/ForegroundService.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/service/ForegroundService.java
new file mode 100644
index 0000000..3a1fa15
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/service/ForegroundService.java
@@ -0,0 +1,62 @@
+package com.qiniu.droid.rtc.demo.service;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+
+public class ForegroundService extends Service {
+
+ private static final String TAG = "ForegroundService";
+ private final IBinder mBinder = new LocalBinder();
+
+ public class LocalBinder extends Binder {
+ ForegroundService getService() {
+ return ForegroundService.this;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (Build.VERSION.SDK_INT >= 26) {
+ String CHANNEL_ID = "screen share";
+ NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
+ "screen share",
+ NotificationManager.IMPORTANCE_DEFAULT);
+
+ ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
+ Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("")
+ .setContentText("").build();
+ startForeground(1, notification);
+ Log.i(TAG, "start foreground");
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (Build.VERSION.SDK_INT >= 26) {
+ stopForeground(true);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+ return super.onStartCommand(intent, flags, startId);
+ }
+}
+
diff --git a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/MergeLayoutConfigView.java b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/MergeLayoutConfigView.java
index 3ffc26d..11abc20 100644
--- a/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/MergeLayoutConfigView.java
+++ b/QNDroidRTCDemo/app/src/main/java/com/qiniu/droid/rtc/demo/ui/MergeLayoutConfigView.java
@@ -6,7 +6,6 @@
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@@ -68,6 +67,7 @@ public class MergeLayoutConfigView extends FrameLayout {
private RadioGroup mStretchModeRadioGroup;
private QNStretchMode mStretchMode;
private QNMergeJob mCurrentMergeJob;
+ private boolean mCurrentMergeJobValid;
private String mRoomId;
private boolean mIsStreamingEnabled;
@@ -160,15 +160,11 @@ public void updateConfigInfo(RTCUserMergeOptions chooseUser) {
}
/**
- * 获取选中用户更新后的合流配置信息
- *
- * @return 选中用户的合流配置信息
+ * 同步 UI 选择的合流参数到合流配置类
*/
- public List updateMergeOptions() {
- List result = new ArrayList<>();
+ public void updateMergeOptions() {
if (mUserAudioTrack != null) {
mUserAudioTrack.setTrackInclude(mAudioSwitch.isChecked());
- result.add(mUserAudioTrack);
}
if (mUserFirstVideoTrack != null) {
mUserFirstVideoTrack.setTrackInclude(mFirstVideoSwitch.isChecked());
@@ -187,7 +183,6 @@ public List updateMergeOptions() {
} catch (Exception e) {
ToastUtils.s(getContext(), "请输入所有值");//处理空值
}
- result.add(mUserFirstVideoTrack);
}
if (mUserSecondVideoTrack != null) {
mUserSecondVideoTrack.setTrackInclude(mSecondVideoSwitch.isChecked());
@@ -206,9 +201,7 @@ public List updateMergeOptions() {
} catch (Exception e) {
ToastUtils.s(getContext(), "请输入所有值");//处理空值
}
- result.add(mUserSecondVideoTrack);
}
- return result;
}
/**
@@ -233,6 +226,7 @@ public QNMergeJob getCustomMergeJob() {
mCurrentMergeJob.setMaxBitrate(Integer.parseInt(mStreamMaxBitrateText.getText().toString().trim()) * 1000);
mCurrentMergeJob.setFps(Integer.parseInt(mStreamFpsText.getText().toString().trim()));
mCurrentMergeJob.setStretchMode(mStretchMode);
+ mCurrentMergeJobValid = true;
return mCurrentMergeJob;
}
@@ -271,6 +265,22 @@ public void updateMergeJobConfigInfo() {
}
}
+ /**
+ * 更新当前合流任务是否可用
+ * @param valid 是否可用
+ */
+ public void updateMergeJobValid(boolean valid) {
+ mCurrentMergeJobValid = valid;
+ }
+
+ /**
+ * 当前合流任务是否可用
+ * @return valid 是否可用
+ */
+ public boolean isMergeJobValid() {
+ return mCurrentMergeJobValid;
+ }
+
public void updateSerialNum(int serialNum) {
mSerialNum = serialNum;
}
@@ -454,8 +464,9 @@ private boolean isNeedUpdateMergeJob() {
if (mCurrentMergeJob == null) {
return true;
}
- return !mCustomJobIdText.getText().toString().trim().equals(mCurrentMergeJob.getMergeJobId())
- || !mPublishUrlText.getText().toString().trim().equals(mCurrentMergeJob.getPublishUrl())
+ return !mCurrentMergeJobValid
+ || !mCustomJobIdText.getText().toString().trim().equals(mCurrentMergeJob.getMergeJobId())
+ || !isPublishUrlIdentity()
|| Integer.parseInt(mStreamWidthText.getText().toString().trim()) != mCurrentMergeJob.getWidth()
|| Integer.parseInt(mStreamHeightText.getText().toString().trim()) != mCurrentMergeJob.getHeight()
|| Integer.parseInt(mStreamBitrateText.getText().toString().trim()) != mCurrentMergeJob.getBitrate() / 1000
@@ -464,4 +475,10 @@ private boolean isNeedUpdateMergeJob() {
|| Integer.parseInt(mStreamFpsText.getText().toString().trim()) != mCurrentMergeJob.getFps()
|| mStretchMode != mCurrentMergeJob.getStretchMode();
}
+
+ private boolean isPublishUrlIdentity() {
+ String url1 = mPublishUrlText.getText().toString().trim();
+ String url2 = mCurrentMergeJob.getPublishUrl();
+ return url1.substring(0, url1.indexOf("?serialnum")).equals(url2.substring(0, url2.indexOf("?serialnum")));
+ }
}
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
index 6edee01..8b4641e 100644
--- 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
@@ -1,5 +1,6 @@
package com.qiniu.droid.rtc.demo.utils;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
@@ -8,8 +9,6 @@
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.Collection;
import java.util.List;
@@ -54,7 +53,7 @@ public TrackWindowMgr(String currentUserId, int screenWidth, int screenHeight, f
@Override
public void onClick(View v) {
if (mUserWindowMap.size() <= 1) {
- Logging.d(TAG, "skip for single user.");
+ Log.d(TAG, "skip for single user.");
} else if (mUserWindowMap.size() == 2) {
// swap
switchToFullScreenWindow(mUserWindowMap.getOrderedValues(mTrackFullScreenWin).get(0));
@@ -81,7 +80,7 @@ public void onClick(View v) {
@Override
public void onClick(View v) {
if (mUserWindowMap.size() <= 1) {
- Logging.d(TAG, "skip for single user.");
+ Log.d(TAG, "skip for single user.");
} else if (mUserWindowMap.size() == 2) {
// swap
switchToFullScreenWindow(view);
@@ -97,7 +96,7 @@ public void onClick(View v) {
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.");
+ Log.e(TAG, "There were more than 9 published users in the room, with no unUsedWindow to draw.");
return;
}
UserTrackView userTrackView = mUserWindowMap.get(userId);
@@ -182,21 +181,21 @@ private void switchToFullScreenWindow(UserTrackView userTrackView) {
UserTrackView.swap(mEngine, mTrackFullScreenWin, userTrackView);
if (mTrackFullScreenWin.isTaken()) {
- Logging.d(TAG, "put " + mTrackFullScreenWin.getUserId() + " to " + mTrackFullScreenWin.getResourceName());
+ Log.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());
+ Log.d(TAG, "recycle " + mTrackFullScreenWin.getResourceName());
mTrackFullScreenWin.reset();
mTrackFullScreenWin.setVisibility(View.GONE);
}
if (userTrackView.isTaken()) {
- Logging.d(TAG, "put " + userTrackView.getUserId() + " to " + userTrackView.getResourceName());
+ Log.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());
+ Log.d(TAG, "recycle " + userTrackView.getResourceName());
userTrackView.reset();
userTrackView.setVisibility(View.GONE);
// recycle
@@ -220,12 +219,12 @@ private void updateTrackWindowMode(boolean trackWindowP2PMode) {
return;
}
if (trackWindowP2PMode) {
- Logging.d(TAG, "switch to p2p mode");
+ Log.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");
+ Log.d(TAG, "switch to multi user mode");
// relayout. switch to multi user mode.
// (2 users -> 3 users)
if (mTrackFullScreenWin.isTaken()) {
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 d4696dc..d4e4f2b 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_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi-v7a/libqndroid_rtc.so
index 7ddb0b9..e8dbee8 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/libQPlayer.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libQPlayer.so
deleted file mode 100755
index 1fb002b..0000000
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libQPlayer.so and /dev/null differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqcCodec.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqcCodec.so
deleted file mode 100755
index 93732c5..0000000
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqcCodec.so and /dev/null differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqcOpenSSL.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqcOpenSSL.so
deleted file mode 100755
index 6a93a22..0000000
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqcOpenSSL.so and /dev/null differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_amix.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_amix.so
deleted file mode 100755
index 207bf45..0000000
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_amix.so and /dev/null differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so
deleted file mode 100755
index 0bbf7e6..0000000
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_beauty.so and /dev/null differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so
deleted file mode 100755
index 2d520f2..0000000
Binary files a/QNDroidRTCDemo/app/src/main/jniLibs/armeabi/libqndroid_rtc.so and /dev/null differ
diff --git a/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_rtc.so b/QNDroidRTCDemo/app/src/main/jniLibs/x86/libqndroid_rtc.so
index 7aa86d7..6940e1f 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/drawable/ic_upload.xml b/QNDroidRTCDemo/app/src/main/res/drawable/ic_upload.xml
new file mode 100644
index 0000000..4ab9ad3
--- /dev/null
+++ b/QNDroidRTCDemo/app/src/main/res/drawable/ic_upload.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml b/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml
index fb06290..ecd49cb 100644
--- a/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml
+++ b/QNDroidRTCDemo/app/src/main/res/layout/activity_setting.xml
@@ -226,6 +226,7 @@
android:id="@+id/webrtc_aec3_enable_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:switchPadding="10dp"
android:layout_below="@id/app_id_edit_text"
android:layout_marginTop="16dp"
android:layout_marginLeft="45dp"
@@ -313,11 +314,27 @@
+
+